C++基础笔记8
指针和内存
指针存储的值并不是实际的内存数据,而是内存地址,通过指针可以直接对内存数据进行精细化的操作
指针
概念
1 | void* ptr; //占8字节 |
指针的使用
void* ptr;
只是地址,没有指明类型信息,因此可以存储任意类型数据的地址,需要获取值的时候再转成对应的类型来取值,也被称为万能指针
1 | //指针的值作为地址,实际解决需要通过指针获取地址对应的数据 |
指针和复合类型
指针的特性
指针 —> 内存地址 —> uint64_t
1 | int x = 1,y = 123; |
c++提供的语法糖,直接使用下标运算符进行地址偏移,但注意要在连续明确的内存下这样操作才有意义
数组 字符串 对象 函数
数组
1 | int arr[5] = {1, 2, 3, 4, 5}; |
数组实际类型为 int [5]
因此定义一个指向数组的指针应该写 int (*ptr) [5] = &arr;
或者使用c++的using
关键字写作using Array5 = int[5];
就可以像普通变量一样定义数组和指向数组的指针Array5* ptr = &arr;
字符串
char *p;
字符指针,在输出时会一直+1做地址偏移,直到遇到内存为0的地址;通过这一特性,就带有终止符的字符数组首元素地址赋值给字符指针,就能把这个指针直接当字符串使用
char str[] = "hello"; char* pstr = str;
输出得到结果hello
对象
对于vector
等stl容器,同样可以通过定义对应类型加星号来定义指针vector<int>* ptr = &vec;
, 可以像原来的变量(*ptr).push_back(1);
来访问
ptr->push_ack(2);
c语言提供的语法糖,通过箭头进行访问
函数
函数指针可以指向函数,并且可以像函数一样使用
C++内存模型
内存分为:代码区 .text
.rodata
全局区 .data
(初始化) .bss
(未初始化)
堆区 栈区
数据的生命周期:
- 出生 -> 死亡
- 申请,分配 -> 销毁,释放
可被访问 -> 无法被访问
静态生命周期:代码区 全局区
程序启动 —> 程序关闭
动态生命周期:堆区
new —> delete 手动申请时分配,手动删除时释放
自动生命周期:栈区
在函数体内或花括号内定义时分配,函数结束或花括号结束释放
自动生命周期
类似栈,不断入栈出栈的过程。栈的大小有限,使用过多会导致栈溢出。
静态生命周期
全局变量 直接在函数体外定义即可(值是确定的,一定为0)
静态变量 加上static
关键字(这样定义的变量也是整个程序执行过程中都存在)
动态生命周期
c语言通过<stdlib.h>
中的malloc
申请内存(通过free释放)
c++新增关键字new
来申请内存,申请时可以说明这块内存用来做什么(通过delete释放)
1 | int* p1 = new int;//undefined |
注意:只有malloc
和new
出来的数据在堆区,指针只是保存了堆区数据的地址,依然还是栈区的数据,也受自动生命周期的限制,所以遇到下一个花括号时就会被销毁。但此时堆区的数据仍然存在,这就是内存泄漏。
所以在使用完这块内存后,需要将这块内存进行释放。在释放完后最好将指针改成nullptr
,否则会变成悬挂指针(野指针的一种,难以定位会造成不以察觉的bug)
c语言的另外两个函数calloc
realloc
内存池 自行查阅
new Type[]
,[]
代表元素个数
智能指针
share_ptr
unique_ptr
week_ptr