【c++】【基础】【primer_plus】【第四章】指针与内存
指针
指针一直是c/c++学习的难点。指针是用来获取变量在内存中地址的变量。
指针使用解除引用符*
来声明,而变量则可以通过取地址符&
获取其在内存中的地址。故指针的声明与初始化如下代码所示。其中声明了一个指针类型的变量p
,它指向变量a
的地址。
1 | int a = 5; |
在这之后,p
便代表着变量a
的地址。我们可以通过解除引用符*
来从指针变量获取其地址上的值,代码如下。
1 | cout << *p << endl; // * 解除引用,取值 |
危险指针
没有指向具有一定意义内存的指针称为空指针。如指向了nullptr
或NULL
的指针。如果使用了空指针,将有可能造成内存访问异常,获取不到正确的数据。
除了空指针外,还有一类指针更危险且隐藏极深,那便是野指针。野指针一般出现于以下几个场景。
指针变量未初始化。此可手动初始化为
nullptr
或NULL
。指针释放后之后未置空。此时也可在释放后手动初始化为
nullptr
或NULL
。指针操作超越变量作用域。注意不要返回指向栈内存空间的指针或引用。
除此之外,还要注意,数字不能直接赋值给指针,需要强制类型转换方可。如下代码所示。
1 | int* p; |
指针算术
指针变量+1后,并非内存地址对应的数字+1,而是增加它指向的数据类型的字节数,即指向下一个元素。如下代码所示。
1 | int a[10]; |
输出结果如下。
1 | 0x6afed8 |
这是因为对数组取地址时,得到的是整个数组的地址,而非第一个元素的地址,即该指针的数据类型为数组类型,而非元素类型。
1 | int (*p)[10] = &a; // 即&a的类型为 int(*)[10] |
另外,对指针使用sizeof()
得到的是指针的长度,即使指针指向一个数组。一般指针的长度为4字节。
对于数组而言,a[i]
等价于*(a + i)
。所以有时候会说数组的本质也还是指针运算而已。
c++中的内存存储
自动存储
在函数内部定义的常规变量、局部变量等都使用使用自动存储空间,称为自动变量。它们在函数被调用时(或所在代码块为活动代码块时)自动产生,在函数结束时(或离开代码块时)消亡。
自动变量将存储在栈空间
中,执行其所在代码块时入栈
,离开代码块时出栈
,且先进后出(FILO)。
静态存储
在函数外定义的变量、在函数内使用static
声明的静态变量、全局变量等都使用静态存储空间,称为静态变量。它们在整个程序执行期间都存在,我们会在后面第九章详细论述此种存储方式。
动态存储
除了以上存储空间外,c++还有一个自由存储空间,称为内存池,或堆空间
。动态存储空间将变量的创建和删除权限交给开发人员。理论上讲,我们可以在任何地方创建变量,同时可以在任何地方删除该变量,且在该变量存在的整个周期内,它都是存储在堆空间
的某个具体位置上的。
换句话数,动态数据的生命周期不完全受程序和函数的生存时间控制,我们可在一个函数中创建,在另一个函数中删除,对内存有了更大的控制权。但这也导致一些变量可能占用的自由存储空间不连续,使得跟踪内存位置更加困难。
动态内存分配
我们使用new
关键字来创建存储在堆空间
的动态变量,使用delete
关键字来删除该动态变量。new
和delete
的基本使用格式如下。
1 | type_name* point_name = new type_name; // new |
1 | delete point_name; // delete |
下面以int
为例。
1 | int* p = new int; // new |
需要注意的是,使用delete
释放new
申请的内存时,并不会删除指针本身,还是可以重新分配内存给该指针。与此同时,new
和delete
要配对使用,否则会发生内存泄漏(memory leak),导致堆空间有无法回收的内存。
更不要尝试释放已经释放的内存块,其结果将不确定,故不要创建两个指向同一内存块的指针。但是对空指针delete
是安全的,所以我们最好在delete
之后加上一句置空的代码。
1 | int* p = new int; |
动态复合类型
使用new
也可以创建动态数组。其一般格式如下。
1 | type_name* point_name = new type_name[element_num]; // new |
1 | delete[] point_name; // delete |
同样以int
为例。
1 | int* p = new int[10]; // new |
其中数组的第一个元素为*p
,也是p[0]
,数组下标为i
个元素为p[i]
,参见上述指针算术。
使用new
也可创建结构体。此时不再使用.
进行成员访问,而是替换为->
。
1 | struct pos { |