编译C语言的结构体

3
这是我的代码:
#include <stdio.h>
typedef struct {
    const char *description;
    float value;
    int age;
} swag;

typedef struct {
    swag *swag;
    const char *sequence;
} combination;

typedef struct {
    combination numbers;
    const char *make;
} safe;

int main(void)
{
    swag gold = { "GOLD!", 100000.0 };
    combination numbers = { &gold, "6503" };
    safe s = { numbers, "RAMCON" };

    printf("Contents = %s\n", s.numbers.swag->description);

    getchar();

    return 0;
}

每次我使用VS开发人员控制台进行编译时,都会收到以下错误提示:错误C2440:“初始化”:无法将“组合”转换为“swag *”。 但是如果我使用gcc,控制台只会打印“GOLD!”不太明白这里发生了什么。

@remyabel 我正在使用Visual Studio 13开发者命令提示符。 - Anas Ayubi
抱歉,我贴错了编译器链接。 - user3920237
@remyabel 你有什么线索为什么我会收到这个错误? - Anas Ayubi
另一种选择是使用“组合”。 - ChiefTwoPencils
2个回答

4
您所遇到的是一种特定于实现的非标准编译器扩展变体,它在各种 C89/90 编译器中被广泛使用。
经典 C89/90 的严格规则禁止在 {} 初始化程序中使用非常量对象。这意味着在初始化程序中指定整个结构体对象是不可能的,因为那将违反上述要求。根据该规则,在 {} 中只能使用标量常量。
然而,许多 C89/90 编译器忽略了这个标准要求,并允许用户在编写局部对象的 {} 初始化程序时指定非常量值。不幸的是,如果用户在初始化程序中指定了一个复杂的结构体对象,就会立即出现歧义,就像您所示例的情况。
safe s = { numbers, "RAMCON" };

语言标准不允许这样做,因此并不清楚应该将这个“numbers”初始化器应用于哪里。有两种解释方式:
1. 现有的语言规则指出编译器必须自动进入每个嵌套的结构体层级,并将连续的初始化器从“{}”中按顺序应用于其中找到的所有连续标量字段(实际上,情况稍微复杂一些,但这是一般的想法)。这正是您的编译器所做的。它取了第一个初始化器“numbers”,找到第一个标量字段“s.numbers.swag”,并尝试将前者应用于后者。这显然导致了您观察到的错误。
2. 其他编译器采用了更为复杂的方法来处理这个扩展。当编译器看到“{}”列表中的下一个初始化器与左侧目标字段具有相同的类型时,它不会“打开”目标字段并进入下一级嵌套,而是使用整个初始化器值来初始化整个目标字段。
您在示例中期望的是这种行为(如果我没有弄错的话,这是C99所要求的行为),但您的C89/90编译器遵循了第一种方法。
换句话说,当您编写C89/90代码时,通常可以在指定局部“{}”初始化器中使用非标准扩展。但最好避免在这样的初始化器中使用结构体对象,并且只使用标量初始化器。

0

看起来是初始化程序的问题。如果您在gcc中使用正确的选项,它会告诉您:

$ gcc -Wall -ansi -pedantic x.c
x.c: In function ‘main’:
x.c:21: warning: initializer element is not computable at load time
x.c:22: warning: initializer element is not computable at load time

这可能是VS试图告诉你的相同问题。如果你声明goldnumbers为静态的,就可以消除这些问题。


为什么我应该将它们初始化为静态的? - Anas Ayubi
为了使元素在加载时可计算。 - Jens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接