You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的执行步骤(按运算符优先级:++ > * > -- > +):

  1. ++cpp:cpp从&cp[1]自增到&cp[2]
  2. *++cpp:解引用得到cp[2]的原始值c+1(指向c[1]的指针)
  3. --*++cpp:对c+1做自减操作,得到c,注意:这里修改的是cp[2]的值! 原本cp[2]是c+1,现在变成了c
  4. *--*++cpp:解引用修改后的cp[2](现在是c),得到"GeksQuiz"的首地址
  5. +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数组元素的值。

你的核心错误:

  1. 没跟踪cpp的指向变化:前两个printf中的++cpp修改了cpp本身的指向,--*++cpp修改了cp数组元素的值,这两个变化都会影响后续运算。
  2. 误解数组下标运算:cpp[-n]不是指向c的倒数第n个元素,而是从cpp当前指向的位置往回移动n个cp数组元素,再取该元素的值(这个值才是指向c的指针)。
  3. 混淆“指针指向”和“指针的值”:比如cp[2]是一个char**变量,它的值是c+1;--*++cpp修改的是cp[2]的值,而非cpp的指向。

最后串一遍四个printf的完整输出:

  1. **++cpp → 输出TEST,cpp指向&cp[1]
  2. *--*++cpp+3 → 输出sQuiz,cpp指向&cp[2],cp[2]被改为c
  3. *cpp[-2]+3 → cpp[-2] = cp[0] = c+3,解引用得到"QUIZ",+3后输出Z
  4. cpp[-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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:58:56