C/C++指针合集
1. 指针变量的空间大小
指针变量的空间大小是固定值(64位下为8字节, 32位下为4字节),跟其指向的数据类型及多级指针无关,与编译平台相关(指针大小由当前CPU运行模式的寻址位数决定)。
2. 指针的宽度(步长)
指针宽度与指针变量的数据类型相关,其宽度就是对应的数据类型占用的字节数。
3. 野指针
-
指针定义之后未初始化,出现了随机值。
-
数组下标越界或者是访问了不存在的元素。
-
使用了已经销毁的内存地址。
注意:
-
指针变量定义完成之后要初始化;
-
小心指针越界;
-
指针变量使用之前要检查是否有效;
-
指针使用完成之后赋值为nullptr;
4. void指针
解引用时要强制转换
void *p1 = &a;
cout<<*(int *)p1<<endl;
5. const 指针
*const int p = &a; const修饰的是int,解引用正常,但是不能修改其指向空间里面的具体内容;
int * const p = &a; const修饰的是变量p,不可修改其指向,但可修改其指向空间里面的具体内容;
const int * const &p = &a; 两个const修饰,指针指向的内容和指针指向均不可修改;
6. 多级指针
*int p1 = &a; p1表示指针变量,int表示指向的数据类型;
int **p2 = &p1 p2表示指针变量,int *表示指向的数据类型;
int ***p3 = &p2 p3表示指针变量,int **表示指向的数据类型;
7. 用指针操作数组
int a[3] = {1, 2, 3};
cout<<a<<endl; //等价于&a[0]
cout<<&a<<endl; //整个数组的指针
cout<<&a[0]<<endl; //数组中首个元素的指针cout<<a<<a+1<<a+2<<endl; //步长4字节, 1个int
cout<<&a<<&a+1<<&a+2<<endl; //&a的步长单位是整个数组的空间大小
cout<<&a[0]<<&a[0]+1<<&a[0]+2<<endl; //1个intsizeof(a) = 12字节
sizeof(&a) = sizeof(&a[0]) = 8
8. 用指针操作多维数组
int a[2][3] = {11, 22, 33, 44, 55, 66};
cout<<a<<endl; //等价于&a[0]
cout<<&a<<endl; //整个数组的指针
cout<<a[0]<<endl; //等价于&a[0][0]
cout<<&a[0]<<endl; //数组中首个元素a[0][0]的指针
cout<<&a[0][0]<<endl; //数组中首个元素的指针cout<<sizeof(a)<<endl; //24
cout<<sizeof(a[0])<<endl; //12
cout<<sizeof(a[0][0])<<endl; //4cout<<a<<a+1<<a+2<<endl; //步长12个字节
cout<<&a<<&a+1<<&a+2<<endl; //步长24个字节
cout<<&a[0]<<&a[0]+1<<&a[0]+2<<endl; //步长12字节
cout<<&a[0][0]<<endl; //步长4字节
9. 指针的运算
指针与整数运算
int a[4] = {1, 2, 3, 4};
cout<<a<<*a<<endl;
cout<<a+1<<*(a+1)<<endl; // 步长4
cout<<a+2<<*(a+2)<<endl;
cout<<a+10<<*(a+10)<<endl; //越界int *p = &a[3];
cout<<p<<*(p-2)<<endl;
cout<<p-10<<*(p-10)<<endl; //越界
自增++/自减–
int *p = a;
cout<<p<<*p<<endl;
p++;
cout<<p<<*p<<endl;
指针与整数的加减运算,每次加减的步长是与其对应的数据类型的长度。
指针与指针的运算
==
< >
10. 指针数组
11. 函数指针
void fun1() {cout<<"fun1"<<endl;
}int fun2(int a, int b) {return a + b;
}int main() {void (*pfun1) = &fun1; //获取函数指针(*pfun1)(); //用函数指针调用函数,解引用int (*pfun2)(int, int) = &fun2; //获取函数指针int c = (*pfun2)(1, 2);cout<<(&fun2 == fun2)<<endl;&fun2(); //fun2:定位到某一地址 ():执行其对应的代码块// 函数名就是一个隐式的指针,函数名在程序里面,就是指针地址的别名。// 通过函数指针调用函数是没有办法内联的,存在函数调用开销
}
12. 结构体内存对齐
struct T1 {int a;int *p;char c;
};
struct T1 t;
cout<<sizeof(t)<<endl;
//结构体变量的 起始地址 要能被其最大的成员整除
//结构体变量的 总体大小 要能被其最大的成员整除
//结构体变量的 每个成员 相对于开始地址的偏移量,要被其自身大小整除
13. calloc、realloc
calloc 分配完空间后会清0,相当于malloc + memset
realloc 是表示将原有的指针变量的空间进行扩充,如果原指针指向的空间后面还有足够大的空间,就直接在原地址扩充;
如果原地址没有足够大的空间,则会开辟新地址,并将原来的数据拷贝到新空间。
14. 智能指针
shared_ptr
定义和初始化
int main() {shared_ptr<int> p1; //shared_ptr定义完成之后就是空指针int *p2 = new int; //野指针cout<<p1 == nullptr<<p1 == NULL<<endl;cout<<p2 == nullptr<<p2 == NULL<<endl;shared_ptr<int> p3(new int(123)); // 初始化shared_ptr<int> p4 = new int(123); //不支持隐式类型转换
}
//智能指针做函数返回值
shared_ptr<int>func1(int a) {return shared_ptr<int>(new int(a));
}
int main() {shared_ptr<int>p1 = func(123);shared_ptr<int>p2 = p1;shared_ptr<int>p3(p2);
}
int main() {int *p1 = new int(123); //用new定义的指针为裸指针,不建议用裸指针来初始化智能指针。shared_ptr<int>p2(p1);shared_ptr<int>p3 = p2;
}
int main() {shared_ptr<int>p1 = make_shared<int>(123); //初始化shared_ptr<int>p2 = p1;shared_ptr<int>p3(p2);
}
shared_ptr
共享原理和引用计数
所谓共享,是指也可能有其他指针指向该块内存,同时也有读写和销毁的权限;
int main() {shared_ptr<int>p1 = make_shared<int>(1234);shared_ptr<int>p2 = p1;shared_ptr<int>p3(p2);//实现共享的原理。每个shared_ptr都会维护一个其自身指向那个内存空间的引用计数器,并随时同步更新,以达到与其他shared_ptr同步的目的//维护计数器需要额外的开销
}
引用计数器的增加和删除
- 新建一个
shared_ptr
,并初始化其指向,此时,该变量的引用计数器为1; - 用上面的
shared_ptr
变量初始化一个新的shared_ptr
,此时,指向同一个内存地址的shared_ptr
引用计数器都要+1; - 如果将
shared_ptr
作为一个实参传递到一个函数里面,在函数声明周期内,函数也有操作该内存的权限,引用计数器也要+1; - 如果函数将一个
shared_ptr
作为返回值返回,并且有变量接受,则指向对应内存地址的shared_ptr
引用计数器也要+1;