c - 了解 C 指针、数组和负索引

标签 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+1c+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
    

它似乎有相反的顺序,我可以接受 &*(cpp[0]) 指向 c+1,但我希望 & *cpp[1]指向c+2&*(cpp[-1])指向c。我在这个问题中发现:Are negative array indexes allowed in C?

  1. 我显然混淆了很多东西,并且可能将某些东西称为指针,而实际上它不是一个指针,我想掌握指针的概念,如果有人告诉我我错在哪里,我会很高兴。

最佳答案

首先让我澄清一下负面索引的混淆,因为我们稍后会用它来回答其他问题:

when I say *(cpp[-2]) I get QUIZ, but I would rather expect to get some garbage value.

负值没问题。 Note the following :

By definition, the subscript operator E1[E2] is exactly identical to *((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 的地址"QUIZ" 中的字符 Z,这意味着它将从那里开始打印字符串。

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


现在,问题:

  1. How the magic with *--*++cpp+3 works, I mean what is modified by the -- part that allows me to get MCQ instead of TEST when I dereference like this **cpp, I assume that this pointer *++cpp+3 preserves the state after the -- is applied, but cannot imagine yet how it works.

让我们分解一下(回想一下 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. Why the following works the way it works (the cpp[-2] part):

    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 == 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. I obviously confuse many things, and may call something a pointer that in reality is not one, I would like to grasp the concept of pointer, so will be glad if someone show me where I am wrong.

其实你学得很好! :-)

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

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

关于c - 了解 C 指针、数组和负索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50535008/

相关文章:

在 C 中使用 execlp 调用可执行文件

java - 通用方法 - "private <T> boolean (T[], T[])"

python - 在python3中使用sounddevice和numpy编写wav文件

pointers - 在 Go 中取消引用指针

C++:两个函数,每个函数都采用枚举参数,签名匹配?

c++ - 任何 C/C++ 到非本地字节码编译器/解释器?

java - android jni c++ UnsatisfiedLinkError

c - 如何初始化此功能?

javascript - 创建对象的递归列表

c++ - 关于取消引用和地址空间的基本 C++ 指针问题