【问题标题】:Why do addresses in array of char type pointers wsklan[0] and wsklan[0][0] differ?为什么 char 类型指针数组 wsklan[0] 和 wsklan[0][0] 中的地址不同?
【发布时间】:2021-10-14 10:28:07
【问题描述】:

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

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

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])

完整代码sn-p:

#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] != '\0')
            i++;
        if(z[i] != '\n')
            z[i] = '\0';
        else
            while(getchar() != '\n')
                continue;
    }

    return res;

}

感谢您的理解并帮助我总体上理解这个概念。那段代码 sn-p 来自 Stephen Prata 的 C 书,我稍作修改。

【问题讨论】:

    标签: c pointers c-strings


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 哦,现在我明白了。我首先在脑海中取消引用变量时遇到了问题。所以你是对的,&wsklan[0] 正是数组中第一个指针的地址。现在我明白了,因为要查看指针数组中第一个元素指向的地址,我必须执行 *(&wsklan[0]),而不是 &wsklan[0]。所以第二个问题非常相似,这一切都归结为了解我到底在取消引用什么。非常感谢@Lundin。
    • @dippie *(&amp;wsklan[0]) 不需要括号,同样operator* 的优先级较低,所以这相当于*&amp;wsklan[0] 相当于简单的wsklan[0]
    • @Aconcagua 你是对的,你是对的。我只是在编辑评论,但已经过去了 5 分钟。我的意思是“我必须做 &(*wsklan[0]),而不是 &wsklan[0]。”感谢您的指正。
    • @dippie 这又与wsklan[0] 相同:获取指针指向的值的地址给出,猜测,存储在指针中的值......
    • @Aconcagua 哦,现在我明白你的意思了。你是对的,它打印出同样的东西,不需要取消引用它然后使用&符号。谢谢,我不知道。
    【解决方案2】:

    为了论证的缘故,让我们假设以下内容:

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

    然后:

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

    注意:一元 &amp; 的优先级低于 [ ],所以 &amp;wsklan[0] 表示 &amp;(wsklan[0])

    和:

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

    注意:&amp;data[0][0] 表示&amp;((data[0])[0])

    之后:

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

    然后:

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

    从上面比较&amp;wsklan[0]&amp;wsklan[0][0]进行总结:

    • &amp;wsklan[0] 转换为 (char **)20000
    • &amp;wsklan[0][0] 转换为(char *)10000(与&amp;data[0][0] 相同)

    【讨论】:

    • 我明白了。谢谢你的回答,也很有帮助。感谢您帮助我理解这个概念。
    猜你喜欢
    • 2013-08-24
    • 2014-01-05
    • 2022-11-03
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多