C语言指针引用解引用:修改值/地址?多级指针作用答疑
先把原代码里的语法错误(main函数缺左大括号、混入无关的strong text、打印指针用错格式符)修正,可正常编译运行的版本如下:
#include <stdio.h> int main() { int a = 300; int *p = &a; int **q = &p; **q = -5000; *p = -200; printf("a的值:%d\n", a); // 注意:打印指针必须用%p格式符,强转void*才是标准写法,用%d属于未定义行为 printf("p存的地址:%p\n", (void*)p); printf("q存的地址:%p\n", (void*)q); return 0; }
这段代码运行后a的最终值是-200,p存的是a的内存地址,q存的是p的内存地址。
引用、解引用操作到底改的是存储值还是内存地址?
先讲两个最基础的常识:第一,C语言里不管是普通int变量还是指针变量,它的内存地址是进函数栈(栈上局部变量)或者程序加载(全局/静态变量)时就定死的,运行时根本改不了一个变量本身的内存地址;第二,&取地址操作是纯读操作,只是把变量对应的内存地址读出来,结果是个不能被赋值的右值,半分修改能力都没有。
解引用操作*改什么,全看你怎么用:如果解引用的结果放在赋值号左边当左值,改的是指针指向的那块内存里存的内容,既不会改指针自己存的地址,更碰不到变量本身的固定地址。比如示例里的**q = -5000,逻辑就是先读q里存的p的地址,顺着找到p的内存空间,再读p里存的a的地址,顺着找到a的内存空间,把-5000写进a里;后面的*p = -200就是直接读p存的a的地址,把-200写进a里。全程a、p、q三个变量自己待的内存位置半分没动,改的只是对应内存里存的数值。为什么修改没有“仅能改一次”的限制?
指针本质就是个存内存地址的普通变量,和int变量存整数没什么本质区别。只要你要写的那块内存有写权限、没被const关键字锁死写操作,你想改多少次就改多少次,根本不存在“只能改一次”的默认规则。
真要加修改限制,你自己用const声明就行:比如int * const p = &a;是锁p本身存的地址,p不能再指向其他变量,但p指向的a的值可以随便改;const int *p = &a;是锁p指向的内容,不能通过p改a的值,但p自己存的地址可以改成指向其他int变量。这些限制都是开发者主动加的,不是语言强制要求的。多级指针的实际设计作用是什么?
多级指针不是为了出考题难为人,是真的贴合实际开发需求的设计,常见用法就这几类:- 解决函数内改外部指针的需求:C语言函数传参全是值传递,你要是把一级指针当参数传进函数,函数里拿到的是这个指针的拷贝,你改拷贝里存的地址,外面的原始指针根本不受影响。这时候传二级指针进去,函数里解引用一次就能直接定位到外部的原始一级指针,直接改它存的地址就行,典型场景就是写封装的内存分配函数、修改链表头节点的操作。
- 适配指针数组结构:最常见的就是C语言的命令行参数
argv,类型就是char**——本质是个数组,每个元素都是char*指针,指向一个命令行字符串的首地址,用二级指针遍历起来非常顺。 - 实现动态多维数组:比如写程序的时候不知道二维数组有多少行多少列,要运行时根据输入确定大小,就常用二级指针实现:先申请一块内存存一组int*指针,每个指针再单独申请一块内存存一行int数据,二级指针直接指向这个结构的首地址,用起来和静态二维数组差不多。
- 底层开发适配硬件逻辑:不管是操作系统操作多级页表做地址翻译,还是驱动操作带多级索引的硬件寄存器,本质逻辑都是顺着一层一层的地址找最终的目标内存,用多级指针写代码可以直接和硬件逻辑对应上,不需要做额外的抽象转换。
是不是必须通过另一个指针才能访问某个指针?
这个说法完全站不住脚。指针本身就是普通变量,和int、char这类变量没有任何特殊之处,你直接通过变量名就能读、改指针的值,根本不需要额外定义个指针存它的地址。比如示例里你直接写p = &a;,就是直接操作p这个指针变量,完全不需要借助q。
会有这种误解,基本都是把CPU硬件的寻址逻辑和C语言的语法规则搞混了:CPU访问任何内存确实都要先拿到地址(也就是指针的硬件本质),但这个寻址过程是编译器生成代码、CPU执行的时候自动做的,C语言早就把这个过程封装成了“直接用变量名访问”的简单语义,根本不需要开发者在代码里显式定义额外的指针,去存普通变量(包括指针变量)的地址。
内容的提问来源于stack exchange,提问作者Sayhem Rahman

