深入理解C指针
文章目录
指针和动态内存管理是C语言学习中的重点和难点,本篇主要总结自己对指针的理解,深入浅出,言简意赅。
指针的本质
用最简洁的话来描述指针的本质就是:一个存储了内存地址的变量。所以说指针变量和普通变量并没有本质的区别,只不过指针中存储的是一个内存地址,而普通变量存储的是具体的数值(其实内存地址也是个数值)。当然,如果仅仅是存值类型的差异,并不能给指针带来如此大的威力。指针厉害的地方在于,基于存储地址这一特性:指针可以访问存储在这个地址中的内容并修改;另外,可以通过给指针赋不同的地址来改变指针指向的变量。指针也支持四则、比较等运算,这更丰富了指针的用途。
这听起来没什么了不起,毕竟知道了地址,支持查看和修改这个地址中的值,再理所当然了。没错,这就是计算机底层CPU和内存模型的工作方式,而C语言是最接近底层硬件的高级语言。所以说,C语言的精髓是指针,指针是C语言的一切。
指针的表示
定义一个指针变量就是在变量名前面加上一个*
,然后将一个变量的地址赋给指针变量。下面有一个详细的例子,相信看完对指针的理解会有一个质的提升。
看例子前,让我们先了解一些符号的含义。*
除了可以用来定义一个指针,单独用在指针变量名前面表示“解引”一个指针。“指针解引"的含义就是获取指针所指向的变量的值。&
是取址符号,表示获取该符号后面的变量的地址。
#include<stdio.h>
#include<stdlib.h>
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语言中的具体应用,比如:数组,字符串、结构体和函数等。