考虑:
#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("%dn", curr->val);
printf("%dn", *curr);
curr = curr->next;
}
getchar();
}
当我打印出来时,*curr
和curr->val
是一样的。为什么?
我对*curr
感到困惑.我认为*curr
是&curr
指向的价值。(我是C的新手。
首先,作为初学者,您应该尝试编写不会引发警告的正确代码,然后再尝试了解幕后发生的事情以及为什么一些不正确的表达式仍然给出正确的结果。
C是一种非常宽松的语言,只要编译器可以翻译它所读的内容,它通常假设程序员知道他为什么写它。这就是这里发生的事情。
在 C 中,struct
的地址可以强制转换为其第一个元素的地址。所以(void *) &curr
和(void *) &(curr->val)
一样.在这里,我将所有内容转换为void *
以确保具有兼容的指针类型。
这意味着*((int *) curr)
是一个完美定义的 C 表达式,其值确实是 curr->val
。但这是正确的,因为我编写了一个显式指针强制转换。
你写的东西只是偶然的,因为curr
是一个聚合类型;你传递一个int
和一个item *
来printf
,只使用它的第一个值 - 这不再是标准的C,我只能假设一个经典的实现来猜测。但只需将您的打印命令替换为:
printf("%d - %dn", curr->val, 12);
printf("%d - %dn", *curr, 15);
你应该在第二个printf
中看到垃圾,因为使用 %d
你声明你传递了一个简单的int
,实际上传递了一个聚合。同样,每个标准都没有明确规定,但共同实现应该给出这个结果。
DR:由于*curr
不是int
,printf("%dn", *curr);
是未定义的行为。这意味着任何事情都可能发生,甚至是预期的结果,但是同一编译器的不同版本,或另一个编译器,甚至编译参数的更改都可能产生不同的结果,代码中的微小更改也是如此。
C 允许自动指针强制转换为void *
并且您永远不应该强制转换 malloc
的返回值,因为它可以隐藏错误。请写:
curr = malloc(sizeof(item));
它不再C++兼容,但它是正确的 C 吗?
这是未定义的行为,会给你警告。
printf
在格式说明符的帮助下获取传递给它的参数类型。在您的情况下,结构的第一个元素是一个int
,因此在堆栈的顶部有一个int
。因此,当printf
查找int
时,它会找到一个并给出正确的输出,即使您传递了整个结构。
如果除了 int
之外还有其他数据类型作为结构的第一个元素,它将给出一个未定义的输出。因此,正如我在评论中提到的,访问结构元素的正确方法是使用 (*curr).value
或您使用curr->value
.
这里
curr->val = i;
curr->next = head;
head = curr;
首先,将(*curr).val
设置为 i
。然后你向head
提出curr->next
点。此时还NULL
.然后你head
指向curr
.在这里有趣的部分,curr
指向它的开头,*curr
取消引用它,这本质上是curr->val
的,因为从那里开始结构curr
。
根据标准和这个答案
(C1x §6.7.2.1.13:"指向结构对象的指针,经过适当转换,指向其初始成员......反之亦然。作为结构对象,其中可能有未命名的填充,但不是在它的开头。
这意味着在实际结构开始和第一个元素的开始之间没有任何空间(填充((或者例如一些HEAD
信息(。
编辑:
我不说UB任何事情,不是因为我看不到它。OP要求别的东西。应该指出,但不是答案!这就是为什么我们有评论部分!