指针是 C 语言的重点,也是难点,这篇文章主要讲解指针是什么以及如何使用。
指针与内存是息息相关的,在学习指针之前,先回忆下内存相关的知识。
如果把内存比作一栋酒店大楼,内存单元就像是一个个小的房间,数据就住在小房间里。
我们知道,为了客人能准确找到属于自己的房间,酒店房间是有房号的。
在编写代码过程中,通常会声明一个变量,然后对变量进行赋值或者其他各种运算。
要使用指针,也需要声明一个指针类型的变量。
指针类型由两部分构成:
*
表示指针比如,char*
表示一个指向字符的指针,float*
表示一个指向float类型的值的指针。
int* intPtr;
星号*可以放在变量名与类型关键字之间的任何地方,下面的写法都是正确的。
int *intPtr;
int * intPtr;
int* intPtr;
推荐使用星号紧跟在类型关键字后面的写法,即int* intPtr;
。
声明指针变量时需要注意,如果要在一行声明多个指针变量,每个变量前都要携带字符*
。
// 正确
int * foo, * bar;
// 错误
int* foo, bar;
上面示例中,第二行实际上仅仅声明了一个指针变量,foo
是整数指针变量,而bar
是整数变量,即*只对第一个变量生效。
一个指针指向的可能还是指针,这时就要用两个星号**
表示,这种指针通常称为二级指针
。
int** foo;
int a = 10;
// &a表示a变量的内存地址
int* pa = &a;
// &a表示指针变量pa的内存地址
int** ppa = &pa;
*
这个字符除了声明变量时代表指针外,还可以作为运算符,用来获取指针指向的内存中的值。
void plus1(int* p) {
*p = *p + 1;
}
上面代码中,函数plus1的参数是一个整数指针p。
函数体里面,*p就表示指针p所指向的那个整数值。
对*p赋值,就是改变指针p指向的内存中的值。
这有点绕,和普通变量对比更容易理解。
int a = 10;
int b = 100;
// 将b的地址赋于指针pb
int* pb = &b;
*pb = *pb +1;
a = a + 1;
对于上述代码的最后两行:
*pb = *pb +1
,这个表达式可以拆解为4步,①计算机首先从指针变量pb中取出地址0xffeecc
,②再去这个地址指向的内存单元中获取整数100
,③然后执行运算100+1
,执行完成后,④0xffeecc
这个内存单元的值就变成101。a = a + 1
,相当于上面的表达式,执行过程更简单。①计算机从变量a
对应的内存直接取出整数10
,②然后执行运算10+1
,③执行完成后,a变量对应的内存的数据更新为11
。int x = 1;
printf("x's address is %p\n", &x);
上一小节中,参数变量加1的函数,可以像下面这样使用。
void plus1(int* p) {
*p = *p + 1;
}
int x = 1;
plus1(&x);
printf("%d\n", x); // 2
&运算符与*运算符互为逆运算,下面的表达式,是成立的。
int i = 5;
if (i == *(&i)) // 正确
声明指针变量之后,编译器会为指针变量本身分配一个内存空间,这个内存空间可能还保存着历史数据。
int* p;
*p = 1; // 错误
上述代码是必须避免的,因为指针p指向的内存单元是随机的。
int* p;
int i;
p = &i;
*p = 13;
强烈推荐,声明指针变量的同时,将指针变量的值设为NULL。
int* p = NULL;
C语言允许指针参与运算,但是指针的运算规则和整数的运算规则是相差很大的。
指针与整数值的运算,表示指针的移动。
short* j;
j = (short*)0x1234;
j = j + 1; // 0x1236
由于0x1234本身是整数类型(int),跟j的类型(short*)并不兼容,所以强制使用类型投射,将0x1234转成short*。
表明上看,j + 1应该等于0x1235,但正确答案是0x1236。
指针移动的单位,与指针指向的数据类型有关。数据类型占据多少个字节,每单位就移动多少个字节。
指针只能与整数值进行加减运算,两个指针进行加法是非法的。
unsigned short* j;
unsigned short* k;
x = j + k; // 非法
上面示例是两个指针相加,这是非法的。
相同类型的指针允许进行减法运算,返回它们之间的距离,即相隔多少个数据单位。
这时,减法返回的值属于ptrdiff_t
类型,这是一个带符号的整数类型别名,具体类型根据系统不同而不同。这个类型的原型定义在头文件stddef.h里面。
short* j1;
short* j2;
j1 = (short*)0x1234;
j2 = (short*)0x1236;
ptrdiff_t dist = j2 - j1;
printf("%td\n", dist); // 1
上面示例中,j1和j2是两个指向 short 类型的指针,变量dist是它们之间的距离,类型为ptrdiff_t,值为1,因为相差2个字节正好存放一个 short 类型的值。