为什么 char 类型指针数组 wsklan[0] 和 wsklan[0][0] 中的地址不同?

Why do addresses in array of char type pointers wsklan[0] and wsklan[0][0] differ?

对你来说这可能是一个愚蠢的问题,我找不到一个好的问题,但问题是 - 我不明白,我要求你向我解释这个概念,这样我就可以明白了,我将不胜感激。

所以,问题是 - 首先,我想知道为什么那行代码:

printf("address of wsklan[0] %p\n", &wsklan[0])

打印的地址不同于:

printf("address of wsklan[0][0] %p\n", &wsklan[0][0]);

我猜这是因为在第一种情况下,这是一个指针的地址,而在第二种情况下,这是一个 char 变量的地址。但为什么地址不一样呢?我以为wsklan[0]的地址是第一个char的地址。

字符串在内存中是如何存储的?字符串在内存中是一个接一个吗?所以如果我输入 'onetwo' 和第二个字符串 'threefour',它会是一个接一个吗?

第二个问题 - 为什么我不能像这样使用 puts 函数?:

puts(wsklan+1)

但我可以这样使用它吗?

puts(wsklan[1])

完整代码片段:

#include <stdio.h>
#include <string.h>

char *read(char *z, int amount);

int main(void){
    
    char data[20][300];
    char * wsklan[20];
    read(data[0], 300);
    read(data[1], 300);
    wsklan[0] = data[0];
    wsklan[1] = data[1];
    printf("addres wsklan[0][0] %p\n", &wsklan[0][0]);
    printf("addres wsklan[1][0] %p\n", &wsklan[1][0]);
    puts(wsklan[0]);
    puts(wsklan[1]);
    putchar(*wsklan[1]);

    return 0;
}

char *read(char *z, int amount){
    char * res;

    int i = 0;

    res = fgets(z, amount, stdin);

    if(res){
        while(z[i] != '\n' && z[i] != '[=14=]')
            i++;
        if(z[i] != '\n')
            z[i] = '[=14=]';
        else
            while(getchar() != '\n')
                continue;
    }

    return res;

}

感谢您的理解并帮助我从总​​体上理解这个概念。该代码片段来自 Stephen Prata 的 C 书,我对其进行了一些修改。

假设声明是char * wsklan[20];.

在表达式&wsklan[0]中,[]优先于&,所以我们得到指针编号0。然后&给出该指针的地址。由于它是数组中的第一项,您可以假设它与通过 &wsklan.

获得的地址相同

在表达式 &wsklan[0][0] 中我们也得到指针编号 0,然后 [] 再次优先于 & 所以我们在数组中得到字符编号 0 该指针0号点在。然后 & 给出该单个字符的地址。那是什么地址取决于您将指针设置为指向的位置。它指向与指针本身存储位置不同的地址,这是完全有道理的。 (因为指向它自己地址的指针将毫无用处。)

第二个问题:wsklan[1] 100% 等同于 *(wsklan + 1)。所以它与 wsklan + 1 具有不同的含义。由于该变量属于“字符指针数组”类型,因此在表达式中使用时它会“衰减”为指向第一项的指针。那将是一个指向字符指针的指针,char**。这不是 printf 所期望的 - 您必须首先将其取消引用为普通 char*

为了论证方便,让我们假设如下:

  • sizeof(char) 是 1(根据定义)
  • sizeof(char *) 是 4(例如在具有 32 位指针的系统上)
  • char data[20][300]存储在地址10000(任意)
  • char *wsklan[20]存储在地址20000(任意)

然后:

  • &wsklan[0] 转换为 (char **)20000
  • &wsklan[1] 转换为 (char **)20004
  • &wsklan[2] 转换为 (char **)20008

Note: Unary & has lower precedence than [ ], so &wsklan[0] means &(wsklan[0]).

和:

  • data[0]&data[0][0] 转换为 (char *)10000
  • data[1]&data[1][0] 转换为 (char *)10300
  • data[2]&data[2][0] 转换为 (char *)10600

Note: &data[0][0] means &((data[0])[0]).

之后:

    wsklan[0] = data[0];
    wsklan[1] = data[1];

然后:

  • wsklan[0]&wsklan[0][0]转换为(char *)10000(与data[0]&data[0][0]相同)
  • wsklan[1]&wsklan[1][0]转换为(char *)10300(与data[1]&data[1][0]相同)

从上面比较&wsklan[0]&wsklan[0][0]总结一下:

  • &wsklan[0] 转换为 (char **)20000
  • &wsklan[0][0]转换为(char *)10000(等同于&data[0][0]