深入理解C指针

指针和动态内存管理是C语言学习中的重点和难点,本篇主要总结自己对指针的理解,深入浅出,言简意赅。

指针的本质用最简洁的话来描述指针的本质就是:一个存储了内存地址的变量。所以说指针变量和普通变量并没有本质的区别,只不过指针中存储的是一个内存地址,而普通变量存储的是具体的数值(其实内存地址也是个数值)。当然,如果仅仅是存值类型的差异,并不能给指针带来如此大的威力。指针厉害的地方在于,基于存储地址这一特性:指针可以访问存储在这个地址中的内容并修改;另外,可以通过给指针赋不同的地址来改变指针指向的变量。指针也支持四则、比较等运算,这更丰富了指针的用途。

这听起来没什么了不起,毕竟知道了地址,支持查看和修改这个地址中的值,再理所当然了。没错,这就是计算机底层CPU和内存模型的工作方式,而C语言是最接近底层硬件的高级语言。所以说,C语言的精髓是指针,指针是C语言的一切。

指针的表示定义一个指针变量就是在变量名前面加上一个*,然后将一个变量的地址赋给指针变量。下面有一个详细的例子,相信看完对指针的理解会有一个质的提升。

看例子前,让我们先了解一些符号的含义。*除了可以用来定义一个指针,单独用在指针变量名前面表示“解引”一个指针。“指针解引"的含义就是获取指针所指向的变量的值。&是取址符号,表示获取该符号后面的变量的地址。

#include

#include

int main () {

int a = 2;

int *ptr = &a;

printf("a的地址是:%p\n", &a);

printf("a的内容是:%d\n", a);

printf("ptr指针的地址是:%p\n", &ptr); // 指针变量自己所在的地址

printf("ptr指针的内容是:%p\n", ptr); // a的地址

printf("ptr指针所指向的内容是:%d\n", *ptr); // 指针指向的a的值

printf("----------\n");

*ptr = 5; // 修改指针指向的变量a的值

printf("修改值后,a的地址是:%p\n", &a);

printf("修改值后,a的内容是:%d\n", a);

printf("修改值后,ptr指针的地址是:%p\n", &ptr); // 指针变量自己所在的地址

printf("修改值后,ptr指针的内容是:%p\n", ptr); // 指针的指向未变:还是a的地址

printf("修改值后,ptr指针所指向的内容是:%d\n", *ptr); // 指针指向的a的值,此时被修改了

printf("----------\n");

int b = 3;

ptr = &b; // 修改指针的指向为变量b

printf("修改指向后,a的地址是:%p\n", &a); // 指针已经不指向a了

printf("修改指向后,a的内容是:%d\n", a); // 指针已经不指向a了

printf("修改指向后,b的地址是:%p\n", &b);

printf("修改指向后,b的内容是:%d\n", b);

printf("修改指向后,ptr指针的地址是:%p\n", &ptr); // 指针变量自己所在的地址,未修改

printf("修改指向后,ptr指针的内容是:%p\n", ptr); // b的地址

printf("修改指向后,ptr指针所指向的内容是:%d\n", *ptr); // 指针指向的b的值

printf("----------\n");

}

打印结果:

a的地址是:0x30981757c

a的内容是:2

ptr指针的地址是:0x309817570

ptr指针的内容是:0x30981757c

ptr指针所指向的内容是:2

----------

修改值后,a的地址是:0x30981757c

修改值后,a的内容是:5

修改值后,ptr指针的地址是:0x309817570

修改值后,ptr指针的内容是:0x30981757c

修改值后,ptr指针所指向的内容是:5

----------

修改指向后,a的地址是:0x30981757c

修改指向后,a的内容是:5

修改指向后,b的地址是:0x30981756c

修改指向后,b的内容是:3

修改指向后,ptr指针的地址是:0x309817570

修改指向后,ptr指针的内容是:0x30981756c

修改指向后,ptr指针所指向的内容是:3

----------

值得注意的是,例子中有一个“ptr指针的地址”,注意这是指针变量所在的地址,不要和指针所存储的地址混淆了。前面提到指针的本质也是一个变量,因此当然也可以把这个指针变量的地址赋值给另一个指针,得到的这个指针就叫“指针的指针”,再赋值一次,就是“指针的指针的指针”… 依次类推,万变不离其宗。

指针的用法通常我们用*定义的指针叫做:指向非常量的指针。就像我们前面使用的那样,“指向非常量的指针”可以修改指针的指向(地址),也可以通过解引来修改指针指向的数据。但在一些特殊情况下,我们可能需要控制指针这两种操作的权限。这就需要引入"常量"的概念。C语言中,用const关键字定义一个常量。

下面介绍三种和常量相关的指针:

指向常量的指针: 可以修改指针的指向,但是不能修改指向的数据。指向非常量的常量指针:不可以修改指针的指向,但可以修改指向的数据。指向常量的常量指针:即不可以修改指针的指向,也不可以修改指向的数据。要想理解上面的几种指针,我们要先理解“常量”这个概念:在计算机世界中,“常量”即意味着不可修改。因此,我们只需要关注常量这个词是用来 修饰指针,还是修饰指针所指向的那个对象就可以很好的区分。

常量指针指针本身是不可修改的,即指针的值(所指向的地址)不可修改。因此,常量指针表示不可以修改指针的指向,即不能给指针赋值一个新的地址。

指向常量和指向非常量说的是指针所指向的那个对象是否可修改。如果指针指向的那个对象本身是不可以修改的,自然也就不可以通过指针解引的方式修改它的数据。

总结一下:

指针类型指针(的指向)是否可修改指针指向的数据是否可修改指向非常量的指针(普通指针)是是指向常量的指针是否指向非常量的常量指针否是指向常量的常量指针否否几种指针的定义:

指向常量的指针:

const int a = 2;

const int *ptr = &a; // 等价:int const *ptr = &a;

指向非常量的常量指针:

int a = 2;

int *const ptr = &a;

指向常量的常量指针:

const int a = 2;

const int *const ptr = &a;

总结掌握本质后,指针也并不难理解。下一篇会介绍一些指针在C语言中的具体应用,比如:数组,字符串、结构体和函数等。