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