前言
指针是C,C++与其它语言的一大区别,也是C++程序设计里的难点。
本篇学习C++指针的基本用法。
指针与地址
地址是内存空间的位置,指针是保存地址的特殊数据类型。
广义的说,指针就是地址,指针变量用于存储地址。
狭义的说,地址就是内存空间的编号,而指针有具体的存储数据类型(比如int *p; ),因此指针不完全是地址。
指针使用
声明指针
声明指针必须说明是指向什么数据类型的指针(这样才能确定数据在内存空间的使用量):
int *p_int;
double *p_double;
指针变量的创建细节
声明指针时,计算机会给指针变量分配内存空间用于存储地址,但是不会给指针指向的数据分配内存空间,下面的使用方法将报错:
int *p_n;
*p_n = 0;
声明指针p_n 时,系统给p_n 分配了内存空间,其地址为addr1,存储的是p_n ;但系统没有在p_n 存储的地址addr2分配空间,因此addr2是个未定值,系统不知道把0放到哪个地址上,因而报错。
取地址符
& 被用于取得变量的地址:
int variable = 0;
std::cout << &variable;
初始化
可以在声明指针时初始化指针:
int a = 2;
int *p_a = &a;
指针p_a 被初始化,获得a 的地址,指向变量a 。
赋值
可以通过赋值运算符,把地址赋给指针:
int b = 2;
int *p_b;
p_b = &b;
指针的整形赋值
计算机将指针当作整数处理,但指针和整形是两种不同的数据类型,指针没有乘除计算。因此不能简单的把整形赋给指针,必须使用强制类型转换:
int *p_s = (int *) 0x10000000;
算术运算
指针有加减法:
int *pp = new int;
cout << (pp + 1);
比如pp=0x10000000 ,pp + 1 = 0x10000004 ,即地址增加了4个字节的长度(int类型长度)。
在数组中的两个元素的指针相减,能得到两个元素的间隔。
指针解引用(取值运算符)
* 除了是指针类型名,还用于取得指针指向的数据,也叫指针的解引用:
double c = 5.2f;
double *p_c = &c;
std::cout << *p_c;
区分指针类型与解引用
在声明和初始化中,* 表示声明的是指针类型。
在指针变量被声明和赋值后,* 表示的是指针解引用。
short d = 1;
short *p_d = &d;
*p_d;
动态内存分配
定义变量是一种分配内存空间的方法(自动变量,栈空间):
int e = 5;
int *p_e = &e;
使用new 是另一种分配内存空间的方法(动态存储,堆空间):
int *p_f = new int;
new TypeName 在内存空间中找到一块满足TypeName类型的块,返回内存空间的地址。
动态数组
new TypeName [] 用于创建动态数组:
int *p_vec = new int [10];
``delete []````用于释放动态数组空间:
delete [] p_vec;
动态内存释放
动态分配的内存空间,如果不需要再使用时,要及时通过delete 释放,new,delete 要成对使用。否则容易出现内存泄漏memory leak。
delete 不会把指针删除,而是释放指针指向的内存空间。空间释放后,最好把指针置空:
int *p_g = new int;
delete p_g;
p_g = nullptr;
delete 不能释放栈空间上的内存,但是可以delete 空指针。
也不能对同一个指针释放两次,会报double free的错误。
C++11推出了智能指针,能够降低内存泄漏的风险。
数组名与指针
C++中,数组和指针基本是等价的,数组名表示数组第1个元素的地址。区别在于,数组名的值是不能改变的,但指针变量的值是可以改变的。通过指针访问数组元素:
p_vec[1] = 1;
*(p_vec + 1) = 1;
需要注意,对数组使用sizeof 得到数组长度,对一个指向数组的指针应用sizeof得到指针长度。
指针数组与数组指针
指针数组
指针数组,变量名表示一个数组,也就是数组中的每个元素都是一个指针,:
int* vec_p[5];
上面这个声明中,[] 比* 的优先级高,因此vec_p[5] 是一个数组,而int* 指定了数组类型为整形指针。
指针数组常用于字符串数组:
char* vec_string[2] {"Hello, ", "World!"};
数组指针
数组指针,变量名表示一个指针,指向整个数组:
int (*p_vec)[10];
上面这个声明中,首先声明了p_vec 是一个指针,指向一个10元素整形数组。此时,p_vec + 1 是p_vec地址后40个字节的位置。
数组指针常用于多维数组(比如OpenCV中Mat数据读取),举个二维数组的例子:
ushort pixel[5][10];
ushort (*p_pixel)[10] = pixel;
(*(p_pixel + 2))[4] = 3
std::cout << (*(p_pixel + 2))[4];
p_pixel[2][4] = 3;
对数组取地址
数组名表示数组首元素的地址,对数组取地址也会获得数组的地址,但二者的含义是不一样的:
int vec[5] {0, 1, 2, 3, 4};
std::cout << vec << "\n";
std::cout << &vec << "\n";
std::cout << (vec + 1) << "\n";
std::cout << (&vec + 1) << "\n";
结果:
0x7ffe8742ebd0
0x7ffe8742ebd0
0x7ffe8742ebd4
0x7ffe8742ebe4
取数组的地址时,返回的是整个数组的地址,也就是包含5个int元素的地址,&vec + 1 的地址为数组首元素地址 + 5 * 4 。
而数组名,就是数组首元素的地址,vec + 1 的地址为数组首元素地址 + 4 。
后记
本篇记录了C++中指针的基本使用,以及指针和数组的关系,下一篇将继续记录指针与字符串的关系。
|