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

时间:2018-05-25 18:31:14

标签: 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;
}

该行的结果:

 printf("%s ", *cpp[-2]+3);

让我困惑,但让我逐步解释,我是如何理解的。

  • 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

这里我在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

因此,当我取消引用此*(cpp[0])**cpp时,我希望获得MCQ的值c+1

printf("%p\n", &*(cpp[0])); // 0x601068

但是当我说*(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
    
  3. 它似乎有相反的顺序,我可以接受指向&*(cpp[0])的{​​{1}},但我希望c+1指向&*cpp[1]和{{1}到c+2。我在这个问题中找到了:Are negative array indexes allowed in C?

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

1 个答案:

答案 0 :(得分:2)

首先让我澄清消极指数的混淆,因为我们稍后会用它来回答其他问题:

  

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

负值很好。 Note the following

  

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

知道了,因为cpp == cp+2,然后:

cpp[-2] == *(cpp-2) == *(cp+2-2) == *cp == c+3

因此:

*cpp[-2]+3 == *(c+3)+3 == c[3]+3

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

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

现在,问题:

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

让我们分解(在这里回忆一下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

正如你正确指出的那样,这指向sQuiz。但是,cppcp[2]已被修改,因此您现在拥有:

cp[] == {c+3, c+2, c, c}
cpp  == cp+2

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
    
  2.   

首先,让我们将&*x简化为x。然后,执行与上述类似的操作,如果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
  
      
  1. 我显然很混淆很多事情,并且可能会把一些指针称为实际上不是一个指针,我想掌握指针的概念,所以如果有人告诉我错误的地方会很高兴。
  2.   

你真的得到了它! : - )

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

c+1 == 0x601060 + 1*sizeof(char*) == 0x601068 // Instead of 0x601061
c+2 == 0x601060 + 2*sizeof(char*) == 0x601070 // Instead of 0x601062