CPP-06-指针

指针是一个变量,其存储的是值的地址,而不是值本身

1
int * p_updates;
  • * p_updates 的类型为 int,不是指针
  • p_updates 的类型是 int *,是指向 int 的指针

指针在地址中的存储格式

1
2
int ducks = 12;
int *birddog = &ducks;
cpp_pointer_06_0729

ducks 的地址是 0x1000,存储的值是 12。而 birddog 的地址是 0x1006,存储的值就是 ducks 的地址 0x1000

指针变量的声明

1
2
3
4
5
6
7
8
9
10
int a = 12;
int *p1, p2, *p3;
p1 = &a;
p3 = &a;
*p1 = 100;
p2 = 200;
cout << a <<endl;
cout << p1 << " " << *p1 << endl;
cout << &p2 << " " << p2 << endl;
cout << p3 << " " << *p3 << endl;

输出结果

1
2
3
4
100
0x7ff7b7a6b458 100
0x7ff7b7a6b44c 200
0x7ff7b7a6b458 100

从结果来看:

  1. 声明的时候 int *p1, p2;,声明的是两种类型
  2. 当两个指针指向同一个地址时,修改一个会影响另外一个

既然是地址,是不是可以直接给地址复制,答案是可以的

1
2
3
int * p3;
//p3 = 0x7ff7b7a6b458; //incompatible integer to pointer conversion assigning to 'int *' from 'long'
p3 = (int *)0x7ff7b7a6b458

不能直接将整型复制给指针,可通过类型转换赋值给 p3,但是会段错误(Segmentation fault),即未知的地址

new

在C语言中,可以 用库函数malloc( )来分配内存;在C中仍然可以这样做,但C还有new运算符

1
2
3
4
5
int night = 1000;
int *pt = new int;
*pt = 1001;
cout << night << " " << *pt << endl;
cout << sizeof(pt) << " " << sizeof(*pt) << endl;

运行结果

1
2
1000 1001
8 4

地址本身只指出了对象存储地址的开始,而没有指出其类型(使用的字节数)。而通过

变量值都存储在被称为栈(stack)的内存区域中,而new从被称为堆(heap)或自由存储区(free store)的内存区域分配内存

在 C中,值为0的指针被称为空指针(null pointer)。C确保空指针不会指向有效的数据

归还或释放(free)的内存可供程序的其他部分使用。使用 delete 时,后面要加上指向内存块的指针

1
2
3
4
5
int *pt = new int;
cout << pt << endl;
delete(pt);
cout << pt << endl;
delete(pt); //malloc: Double free of object 0x7f822cf05ce0

运行结果

1
2
0x7f93c3705ce0
0x7f93c3705ce0
  1. 使用 delete 仅会释放内存块,但是不会将指针的地址置为空

  2. newdelete 需要配合使用,否则将发生内存泄露(memory leak)

  3. 不能重复释放已经释放的内存块

delete 不能删除一个不是 new 分配的内存

1
2
3
int a = 10;
int *pt = &a;
delete pt;

delete 能删除一个空指针

1
2
int *pt2 = nullptr;
delete pt2;

动态数组

在编译时给数组分配内存被称为静态联编(static binding),在编写程序时指定数组的长度,意味着数组是在编译时加入到程序中的

还可以在程序运行时选择数组的长度,被称为动态联编(dynamic binding),程序将在运行时确定数组的长度,这种数组叫作动态数组(dynamic array)

在C++中,创建动态数组,只要将数组的元素类型和元素数目告诉new即可

1
2
3
int *psome = new int [10];
delete []psome;
//delete psome; //'delete' applied to a pointer that was allocated with 'new[]';

方括号告诉程序,应释放整个数组,而不仅仅是指针指向的元素。 注意delete和指针之间的方括号

  • 如果使用new时,不带方括号,则使用delete时,也不应带方括号。

  • 如果使用new时带方括号,则使用delete 时也应带方括号

1
2
3
4
5
6
7
8
9
10
11
12
double * p1 = new double [3];
p1[0] = 0.2;
p1[1] = 0.3;
p1[2] = 0.8;
cout << "p1[1] is " << p1[1] << endl;
p1 = p1 + 1;
cout << "Now p1[0] is " << p1[0] << endl;
cout << "p1[1] is " << p1[1] << endl;
p1 = p1 - 1;
delete [] p1;
//cout << "p1[0] is " << p1[0] << endl;
return 0;

运行结果

1
2
3
p1[1] is 0.3
Now p1[0] is 0.3
p1[1] is 0.8

注意在释放指针的时候执行了 p1 = p1 - 1;否则会提示错误 error for object 0x7fe25e705ce8: pointer being freed was not allocated

将整数变量加 1 后,其值将增加1;但将指针变量加1后,增加的量等于它指向的类型的字节数。将指向double的指针加1后,如果系统对double使用8个字节存储,则数值将增加8;将指向short的指针加1后,如果系统对short使用2 个字节存储,则指针值将增加2

1
2
3
4
5
6
7
8
double wages[3] = {10000.0, 20000.0, 30000.0};
short stacks[3] = {3, 2, 1};
double * pw = wages;
short * ps = &stacks[0];
short * ps2 = stacks;
cout << *(pw + 1) << endl;
cout << *(ps + 1) << endl;
cout << *(ps2 + 1) << endl;

运行结果

1
2
3
20000
2
2
cpp_pointer_1449_0729

数组名被解释为其第一个元素的地址,而对数组名应用地址运算符时,得到的是整个数组的地址:

总结

  • 不要使用delete来释放不是new分配的内存
  • 不要使用delete释放同一个内存块两次
  • 如果使用new [ ]为数组分配内存,则应使用delete [ ]来释放。
  • 如果使用new 为一个实体分配内存,则应使用delete(没有方括 号)来释放。
  • 对空指针应用delete是安全的。

参考链接

  1. 《C++ Primer Plus》