【问题标题】:Why are *curr and curr->val the same value?为什么 *curr 和 curr->val 的值相同?
【发布时间】:2016-06-29 05:12:55
【问题描述】:

考虑:

#include<stdlib.h>
#include<stdio.h>

struct node {
    int val;
    struct node *next;
};

typedef struct node item;

void main() {
    item * curr, *head;
    int i;

    head = NULL;
    for (i = 1; i <= 10; i++) {
        curr = (item *)malloc(sizeof(item));

        curr->val = i;
        curr->next = head;
        head = curr;
    }

   curr = head;
   while (curr) {
        printf("%d\n", curr->val);
        printf("%d\n", *curr);
        curr = curr->next;
   }

   getchar();
}

当我打印出来时,*currcurr-&gt;val 是相同的。为什么?

我对@9​​87654325@ 感到困惑。我认为*curr&amp;curr 指向的值。 (我是 C 的新手。)

【问题讨论】:

  • 如果您在编译时出现警告,请使用 "printf("%d\n", *curr);" 行应该打印一个警告,因为 struct node 不是整数。虽然您看到这种行为是有原因的,但它有点复杂,并且与 C 中如何处理可变参数有关。可能您应该避免这样做,以便更容易理解这种行为。
  • printf("%d\n", *curr); 是未定义的行为,因为 *curr 不是 int。
  • 除了上述cmets正确指出的内容外,供参考,使用(*curr).val访问元素与curr-&gt;val相同
  • warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘item’ [-Wformat=] printf("%d\n", *curr);

标签: c pointers linked-list


【解决方案1】:

首先,作为初学者,您应该尝试编写不会引发警告的正确代码,然后再尝试了解幕后发生的事情以及为什么某些不正确的表达式仍然会给出正确的结果。

C 是一种非常宽松的语言,只要编译器可以翻译它读取的内容,它通常假定程序员知道他为什么编写它。这就是这里发生的事情。

在 C 中,struct 的地址可以转换为其第一个元素的地址。所以(void *) &amp;curr(void *) &amp;(curr-&gt;val) 相同。在这里,我将所有内容都转换为 void * 以确保具有兼容的指针类型。

这意味着*((int *) curr) 是一个完美定义的C 表达式,它的值确实是curr-&gt;val。但它是正确的,因为我写了一个显式的指针转换。

你写的工作只是偶然,因为curr是一个聚合类型;您将 intitem * 传递给 printf 并仅使用其第一个值 - 这是 不再 标准 C,我只能假设 classic 实现猜猜。但只需将您的打印命令替换为:

printf("%d - %d\n", curr->val, 12);
printf("%d - %d\n", *curr, 15);

您应该在第二个printf 中看到垃圾,因为使用%d 您声明您传递了一个简单的int 并实际上传递了一个聚合。同样,每个标准都没有明确规定,但常见的实现应该给出这个结果。

TL;DR:由于*curr 不是intprintf("%d\n", *curr); 是未定义的行为。这意味着任何事情都可能发生,即使是预期的结果,但是相同编译器的不同版本,或另一个编译器,甚至编译参数的更改都可能产生不同的结果,就像代码中的微小变化一样。

C 允许自动指针转换到void * 和从void * 转换,你不应该转换malloc 的返回值,因为它可以隐藏错误。请写:

curr = malloc(sizeof(item));

它不再与 C++ 兼容,但它是正确的 C。

【讨论】:

    【解决方案2】:

    这是未定义的行为,会给你警告。

    printf 在格式说明符的帮助下获取传递给它的参数类型。在您的情况下,结构的第一个元素是int,因此在堆栈顶部有一个int。因此,当printf 查找int 时,它会找到一个并给出正确的输出,即使您传递了整个结构。

    如果有除int 之外的任何其他数据类型作为结构的第一个元素,它会给出未定义的输出。 因此,正如我在 cmets 中提到的,访问结构元素的正确方法是使用 (*curr).value 或使用 curr-&gt;value

    【讨论】:

    • 您说代码是未定义的行为,但后来又说“如果有任何其他数据类型而不是int... 它会给出未定义的输出”。当然,如果它是未定义的行为,则输出已经未定义,无论类型如何?
    • @PaulHankin 没错,我写这个只是为了澄清在这种情况下正确的输出,以及它在其他情况下如何不适用。
    【解决方案3】:

    这里

    curr->val = i;  
    curr->next = head;
    head = curr;
    

    首先将(*curr).val 设置为i。然后让curr-&gt;next 指向head。这时候还是NULL。然后让head 指向curr。这里有趣的部分,curr 指向它的开头,*curr 取消引用它,它本质上是curr-&gt;val,因为从那里开始结构curr

    按照标准和这个answer

    (C1x §6.7.2.1.13:“一个指向结构对象的指针,经过适当转换,指向它的初始成员......反之亦然。作为结构对象,可能有未命名的填充,但不是在它的开头.")

    这意味着在实际结构的开始和它的第一个元素的开始之间没有任何空格(填充)(或者例如一些HEAD 信息)。

    编辑:
    我不说任何关于 UB 的事情,不是因为我没有看到它。 OP要求别的东西。应该指出,但不是答案!这就是我们有评论区的原因!

    【讨论】:

    • curr 确实指向&amp;(curr-&gt;val),但*curr 不是 curr-&gt;val 它只是以curr-&gt;val 开头,但它更长。更多详情见我的回答
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-04
    • 1970-01-01
    • 2019-04-28
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    相关资源
    最近更新 更多