指针和内存

指针存储的值并不是实际的内存数据,而是内存地址,通过指针可以直接对内存数据进行精细化的操作

指针

概念

1
2
3
void* ptr;   //占8字节
int i = 1;
int* ptr = &i; //& 取地址符 可以获取变量的内存地址

指针的使用

void* ptr;只是地址,没有指明类型信息,因此可以存储任意类型数据的地址,需要获取值的时候再转成对应的类型来取值,也被称为万能指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//指针的值作为地址,实际解决需要通过指针获取地址对应的数据
//* 指针的取值,指针解引用
int var = 1;
int* ptr = &var;
cout << *ptr << endl; // 1
*ptr = 2; //改变了原本的变量,指针指向的值也会跟着变,因为是对同一块内存数据的操作
cout << var << endl; // 2
var = 3;
cout << *ptr << endl; // 3
int* p;
*p; //野指针, 无意义的指针
int* p = NULL;
int* p = nullptr;//空指针

指针和复合类型

指针的特性

指针 —> 内存地址 —> uint64_t

1
2
3
4
5
int x = 1,y = 123;
uint64_t addr = (uint64_t)&x;
cout << *(int*)(addr + 4) << endl; //得到y的值
//指针在做地址偏移时会自动乘上类型的大小 (ptr + 1 * sizeof(int))
cout << ptr[1] << endl;

c++提供的语法糖,直接使用下标运算符进行地址偏移,但注意要在连续明确的内存下这样操作才有意义

数组 字符串 对象 函数

数组

1
2
3
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr; //进行隐式转化 转为了第一个元素的首地址
cout << ptr[i] << endl;

数组实际类型为 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
2
3
4
5
6
7
8
int* p1 = new int;//undefined
int* p2 = new int[5]();//0,0,0,0,0
int* (*p3)[5] =(int(*)[5]) new int[5]();//0,0,0,0,0
int* p4 = new int(5);//5
int* p5 = new int[5]{1,2,3,4,5};//1,2,3,4,5
vector<int>* p = new vector<int>;//[]
vector<int>* p = new vector<int>(5, 1);//1,1,1,1,1
vector<int>* p = new vector<int>{5,1};//5,1

注意:只有mallocnew出来的数据在堆区,指针只是保存了堆区数据的地址,依然还是栈区的数据,也受自动生命周期的限制,所以遇到下一个花括号时就会被销毁。但此时堆区的数据仍然存在,这就是内存泄漏

所以在使用完这块内存后,需要将这块内存进行释放。在释放完后最好将指针改成nullptr,否则会变成悬挂指针(野指针的一种,难以定位会造成不以察觉的bug)

c语言的另外两个函数calloc realloc

内存池 自行查阅

new Type[][]代表元素个数

智能指针

share_ptr unique_ptr week_ptr