C语言指针、数组与负索引理解困惑及代码问题咨询
拆解C语言指针迷宫:一步步解决你的困惑
先把原代码贴出来,咱们从初始状态开始梳理:
#include <stdio.h> char *c[] = {"GeksQuiz", "MCQ", "TEST", "QUIZ"}; char **cp[] = {c+3, c+2, c+1, c}; char ***cpp = cp; int main() { printf("%s ", **++cpp); printf("%s ", *--*++cpp+3); printf("%s ", *cpp[-2]+3); printf("%s ", cpp[-1][-1]+1); return 0; }
首先明确初始指针关系(和你打印的地址对应):
c是char*类型数组,每个元素指向对应字符串首地址:c[0] → "GeksQuiz"(地址0x601060)c[1] → "MCQ"(地址0x601068)c[2] → "TEST"(地址0x601070)c[3] → "QUIZ"(地址0x601078)
cp是char**类型数组,每个元素存储c数组的元素地址:cp[0] = c+3 → &c[3](指向"QUIZ"的指针)cp[1] = c+2 → &c[2](指向"TEST"的指针)cp[2] = c+1 → &c[1](指向"MCQ"的指针)cp[3] = c → &c[0](指向"GeksQuiz"的指针)
cpp初始是char***类型指针,指向cp数组的首元素,即cpp = &cp[0]
问题1:*--*++cpp+3的执行逻辑是什么?--操作修改了什么?
首先得记住:前一个printf已经修改了cpp的指向,先回顾第一个printf:printf("%s ", **++cpp);
++cpp:cpp原本指向&cp[0],自增后指向&cp[1]**cpp:第一次解引用得到cp[1]的值(c+2,指向c[2]),第二次解引用得到"TEST",输出TEST- 此时cpp的位置:
cpp = &cp[1]
现在拆解第二个printf的执行步骤(按运算符优先级:++ > * > -- > +):
++cpp:cpp从&cp[1]自增到&cp[2]*++cpp:解引用得到cp[2]的原始值c+1(指向c[1]的指针)--*++cpp:对c+1做自减操作,得到c,注意:这里修改的是cp[2]的值! 原本cp[2]是c+1,现在变成了c*--*++cpp:解引用修改后的cp[2](现在是c),得到"GeksQuiz"的首地址+3:地址往后移3个字符,指向"sQuiz",输出sQuiz
此时cpp的位置是&cp[2],且cp[2]的值已被改成c,这就是后续**cpp得到c[0]内容的原因。
问题2:为何打印的指针地址是那样的顺序?
你贴的打印代码:
printf("%p\n", &*cpp[1]); // 0x601060 -> c printf("%p\n", &*(cpp[0])); // 0x601068 -> c+1 printf("%p\n", &*(cpp[-1])); // 0x601070 -> c+2 printf("%p", &*(cpp[-2])); // 0x601078 -> c+3
先简化:&*x等价于x(解引用再取地址,结果就是原指针),所以上面的代码等价于直接打印cpp[1]、cpp[0]等。
此时cpp指向&cp[2],根据数组下标规则a[b] = *(a + b),反向a[-b] = *(a - b),所以:
cpp[1] = *(cpp + 1) = cp[3],而cp[3]原本是c(地址0x601060),输出对应地址cpp[0] = *(cpp + 0) = cp[2],未修改前cp[2]是c+1(地址0x601068),输出对应地址cpp[-1] = *(cpp - 1) = cp[1],cp[1]是c+2(地址0x601070),输出对应地址cpp[-2] = *(cpp - 2) = cp[0],cp[0]是c+3(地址0x601078),输出对应地址
你的错误在于误以为cpp[n]直接指向c的元素,但实际上cpp是指向cp数组的指针,cpp[n]取的是cp数组的元素,再由这个元素指向c的元素。
问题3:理清指针概念,纠正你的混淆点
咱们把几个指针类型的本质说透:
char *c[]:这是数组,每个元素是char*(字符串首地址)。数组名c可以隐式转换成char**类型指针(指向数组首元素的指针),你的描述是对的,但要明确c本身是数组,不是指针变量。char **cp[]:这也是数组,每个元素是char**(指向char*的指针),它不是“反向包装”,而是存储c数组元素的地址(比如c+3就是&c[3])。char ***cpp:这是指针变量,指向char**类型的变量(也就是cp数组的元素)。它不是“修改**cp[]的包装”,而是可以修改自己的指向,或者通过解引用修改cp数组元素的值。
你的核心错误:
- 没跟踪
cpp的指向变化:前两个printf中的++cpp修改了cpp本身的指向,--*++cpp修改了cp数组元素的值,这两个变化都会影响后续运算。 - 误解数组下标运算:
cpp[-n]不是指向c的倒数第n个元素,而是从cpp当前指向的位置往回移动n个cp数组元素,再取该元素的值(这个值才是指向c的指针)。 - 混淆“指针指向”和“指针的值”:比如
cp[2]是一个char**变量,它的值是c+1;--*++cpp修改的是cp[2]的值,而非cpp的指向。
最后串一遍四个printf的完整输出:
**++cpp→ 输出TEST,cpp指向&cp[1]*--*++cpp+3→ 输出sQuiz,cpp指向&cp[2],cp[2]被改为c*cpp[-2]+3→cpp[-2] = cp[0] = c+3,解引用得到"QUIZ",+3后输出Zcpp[-1][-1]+1→cpp[-1] = cp[1] = c+2,cpp[-1][-1] = *(c+2-1) = c[1],+1后输出CQ
最终完整输出:TEST sQuiz Z CQ
内容的提问来源于stack exchange,提问作者MisterAlejandro
相关产品推荐
相关产品推荐

