1.基础bufen

形参带参数的默认函数

int sum(int a=10,int b=20)
{
    return a+b;
}

int main() {
    int a = 10;int b = 20;
    int ret = sum(a,b);
    /*mov eax,dword ptr[ebp-8]
     *push eax
     *mov ecx,dword ptr[ebp-4]
     *push ecx
     * call sum
     * */

    ret = sum(a);
    /*push 14H
     *mov ecx,dword ptr[ebp-4]
     *push ecx
     *call sum
     * */
     
    ret = sum();
    /*
     * push 14H
     * push 0aH
     * call sum
     * */
    return 0;
}

调用形参带参数的默认函数,提升了效率,从汇编码中可以看到少了 mov 指令,在传参时若传立即数,则只有push 而没有 mov 指令

inline内联函数:

inline内联函数在编译的过程中没有函数的开销,在函数的调用点直接吧函数的代码进行展开处理

inline函数(内联成功)不会生成相应的函数符号

不是所有的inline都会被执行为内联函数,inline只是一种建议,具体要看编译器

函数重载:

  1. c++为什么支持函数重载,c语言为什么不支持函数重载
  2. 函数重载要注意什么
  3. c++和c语言代码之间如何相互调用

原因:

  1. c++代码在生成符号的过程中由函数名加参数列表决定,c语言在生成符号的过程中只有函数名决定,而链接器中不允许符号重名

  2. 不要在局部进行声明

bool compare(int a,int b)
{
    cout<<"compare_int_int"<<endl;
    return a>b;
}

bool compare(double a,double b)
{
    cout<<"compare_double_double"<<endl;
    return a>b;
}

bool compare(const char *a,const char *b)
{
    cout<<"compare_char*_char*"<<endl;
    return strcmp(a,b) > 0;
}

int main()
{
    bool compare(int a,int b);
    compare(10,20);
    compare(10.0,20.0);
    compare("aaa","bbb");
}

这样是无法通过编译的,因为,在main函数内声明了 bool compare(int a,int b); 局部变量是可以覆盖全局变量的,因此在main函数内只能看到参数为int 的版本

const指针 应用:

C语言中的const

#include <stdio.h>
int main()
{
    //const int b = 3;     
    //C语言中const变量可以不初始化但是之后将没有机会再次初始化
    //叫做常变量,本质是一种变量
    //int arr[b] = {};  报错,说明b不是常量
    
    const int a = 20;
    int *p = (int*)&a;  
    *p = 30;           
    //改变了a内存中的值,const只是语法上保证a不可以做左值使用,其内存是可以修改的
    printf("%d,%d,%d",*p,a,*(&a));
    return 0 ;
}
输出:30,30,30

c++中的const

int main()
{
	//const int a;        //报错。const 修饰的元素必须初始化,叫做常量
	//const int b = 5;
	//int arr[b] = {};    //编译通过,说明b是常量
	  const int a = 20;
      int *p = (int*)&a;
      *p = 30;            //已经将const int a 的内存修改为 30
      cout<<a<<"  "<<*p<<"  "<<*(&a);
      return 0 ;
}
输出:20  30  30

const的编译方式不同:

c语言,将有const出现的变量就当作是变量处理、c++将所有出现const常量名字的地方替换为常量初始化的值

int main()
{
	int b = 20;
	const int a = b;  //未用立即数初始化,a退化为常变量
	//int arr[a]= {};  
	//报错,
	//解释。编译器将a 替换为 b 就成了int arr[b], b是一个变量,所以报错
	//实际是不会替换的。但可以这么理解
	//证明	

    
    const int a = b;
    int *p = (int*)&a;
    *p = 30;
    cout<<a<<"  "<<*p<<"  "<<*(&a);
    return 0 ;
}
输出:30  30  30    与C语言一样,说明当用变量初始化const修饰的常量时,该常量会退化为常变量

const与一级指针的结合:

const修饰的常量的地址是不能泄露给一个普通的指针或者普通的引用变量 --> 这保证了const修饰的常量不能被间接改变

const int a =20;
int *p = &a;    无法通过

c++语言规范:const修饰的是离它最近的类型

const int *p;	   		类型是int 所以*p 不可改变,但是p本身没有限制
int const *p;	    	同上
int *const p;	    	类型是 int* 所以p的指向是不可以改变的,但是p的内存是可以通过指针解引用来修改p指向的内存的值
const int *const p; 	p不可指向其他内存,p指向的内存也不可以被修改 

编译时:

int main()
{
    int c = 20;
    const int* a = &c;
    int const *d = &c;
    int *const b = &c;
    const int*const e = &c;
    cout<< typeid(a).name()<<endl;
    cout<< typeid(d).name()<<endl;
    cout<< typeid(b).name()<<endl;
    cout<< typeid(e).name()<<endl;
    return 0 ;
}
输出:
PKi
PKi
Pi
PKi

总结:当const的右边出现 * 时const才参与类型

含有const的类型转化:

int * = const int *		是错误的
const int *	= int *		是可以的
int *const = int *		是可以的,*位于const的左边 const不参与类型,相当于int*转int*
int*  = int *const 		是可以的,原因同上

const与二级指针的结合:

const int * *q		**q不可变
int const * *q		**q不可变
int * const *q		*q不可变
int * *const q		q不可变

含有const的类型转化:

int ** = const int **	错误
const int ** = int **	错误
int ** = const * int *	错误,本质是一级指针与const的结合拖一层指针 变为 
					        int* = const int * ,这个不可以转化,故原式不可以转换
const * int * = int** 	正确,托应用 const int * = int * 可以 故可以

解释int ** = const int ** 错误的原因

int main()
{
	int a =10;
	int*p = &a;
	const int **q = &p;  //const int ** = int**
	假设这部可以那么 *q 与 p 操作的是同一块内存。
	若执行 
	const int b = 20;
	*q = &b;
	由于*q 与 p 是同一块内存 那么 就相当于 p = &b;
	而这就将一个常量的地址泄露给了普通指针,错误。
}

修改

int main()
{
	int a =10;
	const int*p = &a;    //保证*p不被修改
	const int **q = &p;   
	return 0;
}
或者:
int main()
{
	int a =10;
	int*p = &a;    
	const int *const*q = &p;     //保证*p不被修改
	return 0;
}

C++的左值引用和右值引用

左值引用:

int main()
{
    int c = 20;
    int *p = &c;
    int &q = c;
    return 0 ;
}
disassemble:
(gdb) disassemble 
Dump of assembler code for function main():
   0x00007ff7a71016e0 <+0>:	push   %rbp
   0x00007ff7a71016e1 <+1>:	mov    %rsp,%rbp
   0x00007ff7a71016e4 <+4>:	sub    $0x40,%rsp
   0x00007ff7a71016e8 <+8>:	call   0x7ff7a7101877 <__main>
   0x00007ff7a71016ed <+13>:	movl   $0x14,-0x14(%rbp)	  
   0x00007ff7a71016f4 <+20>:	lea    -0x14(%rbp),%rax		==指针 
   0x00007ff7a71016f8 <+24>:	mov    %rax,-0x8(%rbp)		==
   0x00007ff7a71016fc <+28>:	lea    -0x14(%rbp),%rax		==引用
   0x00007ff7a7101700 <+32>:	mov    %rax,-0x10(%rbp)		==
   0x00007ff7a7101704 <+36>:	mov    $0x0,%eax
   0x00007ff7a7101709 <+41>:	add    $0x40,%rsp
   0x00007ff7a710170d <+45>:	pop    %rbp
   0x00007ff7a710170e <+46>:	ret

说明引用的本质就是指针,从汇编码上是没有区别的

int &a = 20	错误的原因
20是一个立即数不可被取地址,所以lea这条指令就无法执行,使编译失败

右值引用:

#include <iostream>
using namespace std;

int main()
{
    int&& c = 20;  //右值引用
    /*
    	mov dword ptr [ebp-30h],14h
    	lea eax,[ebp-30]
    	mov dword ptr[c],eax
    */
   
   const int& d = 20;  //常引用
   /*
   		mov dword ptr[ebp-48h],14h
   		lea eax,[ebp-48h]
   		mov dword ptr[d],eax
   */
   return 0 ;
}

右值引用与常引用都是在栈上开辟了空间,用于存放右值,再将该空间内的值赋给定义的变量

new和delete

new不仅可以开辟内存,还可以为内存进行初始化操作
malloc开辟内存失败返回bullptr,new开辟内存失败会抛出bad_alloc异常

try
{
	int *pl = new int(20);
}
catch(const bad_alloc&e)
{
	cout<<"无内存可用"<<endl;
	return -1;
}

new有多少种:

int* p1 = new int(20);                //普通new
int* p2 = new(nothrow) int;			 //不抛出异常的new,错误返回nullpre
const int* p3 = new const int(40);	  //在堆上申请内存存放常量,所以也要用常量指针

int data = 0;                          //定位new
int* p4 = new(&data) int(50);		  //在原有的空间上重新构造,初始化
posted @ 2022-10-18 20:14  satellite2002  阅读(338)  评论(0编辑  收藏  举报