理解C指针,数组和负索引

Mis*_*dro 9 c arrays pointers

我正在尝试学习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;
}
Run Code Online (Sandbox Code Playgroud)

该行的结果:

 printf("%s ", *cpp[-2]+3);
Run Code Online (Sandbox Code Playgroud)

困惑我,但让我一步一步解释,我是如何理解的.

  • char *c[]- 是char的指针数组.
  • char **cp[]- 是指向char的指针数组(我认为这是一个*c[]反向顺序的包装器).
  • char ***cpp- 是指向指向char的指针的指针(我认为这是一个用于**cp[]执行位置修改的包装器).

**++cpp- 因为cpp指向cp,然后++cpp将指向cp+1哪个c+2,所以将打印双重引用TEST.

*--*++cpp+3- 从现在cpp指向cp+1,然后++cpp将指向cp+2哪个c+1,并且下一个操作--将给我们指针c,因此最后的解引用将打印sQuiz.

这里出现了混乱:

cpp[-2]- 从现在开始cpp指出cp+2,我可以确认

printf("%p\n", cpp); // 0x601090   
printf("%p\n", cp+2); // 0x601090
Run Code Online (Sandbox Code Playgroud)

在这里我打印指针的地址 c

printf("c - %p\n", c); // c - 0x601060
printf("c+1 - %p\n", c+1); // c+1 - 0x601068
printf("c+2 - %p\n", c+2); // c+2 - 0x601070
printf("c+3 - %p\n", c+3); // c+3 - 0x601078
Run Code Online (Sandbox Code Playgroud)

所以当我取消引用喜欢这*(cpp[0])还是**cpp我果然得到了价值MCQc+1

printf("%p\n", &*(cpp[0])); // 0x601068
Run Code Online (Sandbox Code Playgroud)

但是当我说*(cpp[-2])我得到了 QUIZ,但我宁愿期望获得一些垃圾价值.

所以我的问题是:

  1. 魔术如何*--*++cpp+3起作用,我的意思是被--允许我得到的部分修改MCQ而不是TEST当我这样取消引用时**cpp,我假设这个指针*++cpp+3--应用后保留状态,但无法想象它是如何工作的.

  2. 为什么以下工作方式(cpp[-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
    
    Run Code Online (Sandbox Code Playgroud)

它似乎有相反的顺序,我可以接受&*(cpp[0])指向c+1,但我希望&*cpp[1]为指向c+2,并&*(cpp[-1])c.我在这个问题中找到了:C中是否允许使用负数组索引?

  1. 我显然混淆了很多东西,并且可能会把一些指针称为实际上不是一个指针,我想掌握指针的概念,所以如果有人告诉我错误的地方会很高兴.

Aco*_*orn 2

让我首先澄清负索引的混乱,因为稍后我们将用它来回答其他问题:

当我说*(cpp[-2])我得到了QUIZ,但我宁愿期望得到一些垃圾值。

负值很好。请注意以下事项

根据定义,下标运算符E1[E2]与 完全相同*((E1)+(E2))

知道,从 开始cpp == cp+2,那么:

cpp[-2] == *(cpp-2) == *(cp+2-2) == *cp == c+3
Run Code Online (Sandbox Code Playgroud)

因此:

*cpp[-2]+3 == *(c+3)+3 == c[3]+3
Run Code Online (Sandbox Code Playgroud)

这意味着指针的地址"QUIZ"加上 3 个位置char,因此您要传递到中的printf字符的地址,这意味着它将从那里开始打印字符串。Z"QUIZ"

实际上,如果您想知道的话,-2[cpp]也是等效且有效的。


现在,问题是:

  1. 魔法是如何*--*++cpp+3工作的,我的意思是 -- 部分修改了什么,它允许我得到MCQ而不是TEST当我像这样取消引用时**cpp,我假设该指针*++cpp+3在应用 -- 后保留状态,但还无法想象它是如何实现的作品。

让我们把它分解一下(回想一下cpp == cp+1,正如您正确指出的那样):

    ++cpp   // cpp+1 == cp+2 (and saving this new value in cpp)
   *++cpp   // *(cp+2) == cp[2]
 --*++cpp   // cp[2]-1 == c (and saving this new value in cp[2])
*--*++cpp   // *c
*--*++cpp+3 // *c+3
Run Code Online (Sandbox Code Playgroud)

sQuiz正如您正确指出的那样。但是,cppcp[2]进行了修改,因此您现在拥有:

cp[] == {c+3, c+2, c, c}
cpp  == cp+2
Run Code Online (Sandbox Code Playgroud)

问题的其余部分没有使用更改的事实cp[2],但值得注意的是 - 特别是因为您打印了指针的值。看:

  1. 为什么以下内容会这样工作(部分cpp[-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
    
    Run Code Online (Sandbox Code Playgroud)

首先,让我们简化&*xx. 然后,执行与上面类似的操作,如果cpp == cp+2(如上所述),您可以看到:

cpp[ 1] == cp[3] == c
cpp[ 0] == cp[2] == c   // Note this is different to what you had
cpp[-1] == cp[1] == c+2
cpp[-2] == cp[0] == c+3
Run Code Online (Sandbox Code Playgroud)
  1. 我显然混淆了很多东西,并且可能将某些东西称为指针,但实际上不是一个指针,我想掌握指针的概念,所以如果有人告诉我哪里错了,我会很高兴。

其实你已经做得很好了!:-)

基本上,指针是代表内存地址的整数。但是,当您对其执行算术时,它会考虑它指向的类型的大小。这就是为什么,如果c == 0x601060sizeof(char*) == 8,那么:

c+1 == 0x601060 + 1*sizeof(char*) == 0x601068 // Instead of 0x601061
c+2 == 0x601060 + 2*sizeof(char*) == 0x601070 // Instead of 0x601062
Run Code Online (Sandbox Code Playgroud)