什么是数组:
数组:用于储存多个相同类型数据的集合。
数组的存储方式:
数组在内存中是连续存放的,开辟一块连续的内存空间。
数组根据数组的下标进行访问。
数组的连续空间不是在静态区就是在栈上。
数组的赋值方式:
只能一个一个元素赋值或者拷贝。
数组地址和首元素地址:
1.地址值相等
2.数组首地址->数组名字,若为p,p+指向第二个元素地址。
3.数组地址->&数组名字,若为p,p+1指向数组最后一个元素的下一个地址。
什么是指针:
类型说明符*变量名
指针变量是变量名,*只是用来表示该变量为指针变量。
指针相当于一个变量,存放的是其他变量在内存中的地址。
(指针名指向内存首地址)
同类型的指针变量可以相互赋值。
指针初始化
C 语言中指针初始化是指给所定的指针变量赋初值。
什么是野指针:
指针指向的位置是不可知的(随机、不正确、没有明确限制)
野指针产生原因:
释放内存后指针不及时置空,依然指向这块内存。
可能出现非法访问的错误。
避免产生野指针的方法:
1.初始化置NULL;
2.申请内存后,判空;
3.指针释放后,置NULL;
4.使用智能指针。
指针的存储方式:
指针可以指向任意类型数据,指针类型说明它所指向的地址空间的内存。但因为指针本身就是一个变量,他存放的也是变量,所以存储空间是不确定的。
指针的操作方式:
1.赋值
int *p;
int a=1;
p=&a;
2.访问指针变量所指向的对象
printf(“%d”,*p);
3.指针变量作为函数参数
void point(int *p1,int *p2)//swap函数的形参为两个指针变量
4.通过指针引用数组元素
数组元素的指针就是数组元素的地址。
5.指针和多维数组
6.字符串指针
存放的是字符串中第一个字符的地址,而不是把字符串放在指针变量中。
7.函数指针
编译系统为函数代码分配了一段存储空间,这段存储空间的起始地址(又称入口地址)称为这个函数的指针。可以定义一个指针,用来存放某一函数的起始地址,这就意味值此指针指向该函数。
8.指向指针的指针
设置一个指针变量p,它指向指针数组的元素,p就是指向指针型数据的指针变量。
使用指针的注意事项:
1.不可以把一个数赋给指针变量。
2.不能改变指针形参的值而使指针实参的值改变(因为他们采用的都是单向的“值传递”方式,实参可以把数据传给形参,形参却不可以把数据传回给实参)。
指针和数组:
指针数组:
存放指针的数组。
int *p[n];
[]优先级高,首先和p结合成为数组;
int*说明这是一个整形指针数组;
n说明有n个指针类型数组元素。
(p+1,p指向下一个数组元素的地址)
数组指针:
指向数组的指针
int (*p)[n];
()优先级高,首先p是一个指针,指向一个整型的一维数组;
n代表一维数组的长度;
数组实际上是一个指针变量,指向数组的指针变量;
变量类型可以看成int[n];
int[n]+1就是加了(数组大小)*sizeof类型;
但是p(指针)的大小在32位中还是4字节。
智能指针
什么是智能指针:
智能指针是一个RAII类模型,用于动态分配内存。
设计思想是将基本类型指针封装为(模板)类对象指针,并且在离开作用域时调用析构函数,使用delete删除指针所指向的内存空间。
智能指针实质是一个对象,行为表现是一个指针。
智能指针的作用:
1.处理内存泄漏问题和空悬指针问题。
(防止忘记调用delete,异常安全)
2.把 value 语义转化为 reference 语义
每种智能指针的特点:
auto_ptr:
实现独占式拥有概念,同一时间只有一个智能指针可以指向该对象,但是在c++11中被摒弃。
原因:
1.对象所有权转移:在函数传参过程中,对象所有权不会返还,存在潜在的内存崩溃问题。
2.不能指向数组,也不能作为STL容器的成员。
解决方法:
- 定义赋值运算符,使之执行深复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本,缺点是浪费空间,所以智能指针都未采用此方案。
- 建立所有权(ownership)概念。对于特定的对象,只能有一个智能指针可拥有,这样只有拥有对象的智能指针的构造函数会删除该对象。然后让赋值操作转让所有权。这就是用于 auto_ptr 和 unique_ptr 的策略,但 unique_ptr 的策略更严格。
- 创建智能更高的指针,跟踪引用特定对象的智能指针数,这称为引用计数。例如,赋值时,计数将加 1,而指针过期时,计数将减 1,。当减为 0 时才调用 delete。这是 shared_ptr 采用的策略。
unique_ptr:
实现独占式拥有概念,同一时间只有一个智能指针可以指向该对象,无法进行拷贝构造和拷贝赋值,但是可以进行移动构造和移动赋值。
是auto_ptr的继承者,同一块内存只能有一个持有者。
区别在于,unique_ptr不允许赋值操作,也就是不可以放在等号左边(函数参数和返回值例外),在一定程度上避免了一些错误操作,导致指针所有权转移。
unique_ptr所有权转移的方法:std::move。
调用move后,原来的unique_ptr就会失效,再用其访问裸指针也会发生错误。
shared_ptr:
实现共享式拥有的概念,即多个智能指针可以指向相同的对象,该对象及相关资源会在其所指对象不再使用之后,自动释放与对象相关资源
使用引用计数实现对同一块内存的多个引用,在最后一个引用被释放时,指向的内存才被释放。
使用注意事项:
1.不要用同一个原始指针初始化多个shared_ptr,会造成二次销毁。
2.禁止使用指向shared_ptr的裸指针,也就是智能指针的指针。(使用shared_ptr指向一个shared_ptr时,引用计数不会加一,操作shared_ptr的指针就容易发生野指针异常)
weak_ptr:
解决shared_ptr在相互引用时,两个指针的引用计数永远不会下降为0,从而导致死锁的问题。
weak_ptr是对对象的一种弱引用,可以绑定到shared_ptr,但不会增加对象的引用计数。
通过weak_ptr得到shared_ptr的方式:
1.调用weak_ptr的lock()方法,如果对象已经被析构,lock()返回一个空的shared_ptr。
2.将weak_ptr传递给shared_ptr的构造函数,如果对象已经被析构,就抛出std::exception异常。
判断weak_ptr指向的对象是否存在:
1.use_count()方法,判断引用计数是否为0。
2.调用expired()方法,如对象已经被析构,返回true。
如何选择智能指针:
shared_ptr:
程序要使用多个指向同一个对象的指针:
1.一个指针数组,并使用一些辅助指针来标示特定的元素(最大最小元素)。
2.两个对象包含都指向第三个对象的指针。
3.STL容器包含指针。(很多 STL 算法都支持复制和赋值操作,这些操作可用于 shared_ptr,但不能用于 unique_ptr(编译器发出 warning)和 auto_ptr(行为不确定))
unique_ptr:
程序不需要多个指向同一个对象的指针:
1.函数如果用new分配内存,并且返还指向该内存的指针。
2.可以将 unique_ptr 存储到 STL 容器中,只要不调用将一个 unique_ptr 复制或赋给另一个的算法(如 sort())。