这个特定问题和zwol 的答案的问题在于它们将类型双关语和严格别名混为一谈。Zwol 的答案对于该特定用例是正确的,因为用于初始化结构的类型;但不是在一般情况下,也不是。struct sockaddr
POSIX 类型可能会读到暗示的答案。
对于具有公共初始成员的结构类型之间的类型双关语,您需要做的就是声明(而不是使用!)这些结构的联合,并且您可以通过任何结构类型的指针安全地访问公共成员。这是自 ANSI C 3.3.2.3 以来明确允许的行为,包括C11 6.5.2.3p6(链接到 n1570 草案)。
如果一个实现包含struct sockaddr_
对用户空间应用程序可见的所有结构的联合,那么在我看来, zwol 的答案OP 链接到是具有误导性的,如果有人读它暗示struct sockaddr
结构支持需要编译器提供非标准的东西。(如果您定义_GNU_SOURCE
,glibc 将此类联合定义为struct __SOCKADDR_ARG
包含所有此类类型的匿名联合。但是,glibc 设计为使用 GCC 编译,因此可能存在其他问题。)
严格的别名要求函数的参数不引用相同的存储(内存)。例如,如果您有
int i = 0;
char *iptr = (char *)(&i);
int modify(int *iptr, char *cptr)
{
*cptr = 1;
return *iptr;
}
那么调用modify(&i, iptr)
是一个严格的混叠违规。定义中的类型双关语iptr
是偶然的,实际上是允许的(因为您可以使用该char
类型来检查任何类型的存储表示;C11 6.2.6.1p4)。
这是类型双关语的正确示例,避免了严格的别名问题:
struct item {
struct item *next;
int type;
};
struct item_int {
struct item *next;
int type; /* == ITEMTYPE_INT */
int value;
};
struct item_double {
struct item *next;
int type; /* == ITEMTYPE_DOUBLE */
double value;
};
struct item_string {
struct item *next;
int type; /* == ITEMTYPE_STRING */
size_t length; /* Excluding the '\0' */
char value[]; /* Always has a terminating '\0' */
};
enum {
ITEMTYPE_UNKNOWN = 0,
ITEMTYPE_INT,
ITEMTYPE_DOUBLE,
ITEMTYPE_STRING,
};
现在,如果在同一范围内可以看到以下联合,我们可以在指向上述结构类型的指针之间进行类型双关,并完全安全地访问next
andtype
成员:
union item_types {
struct item any;
struct item_int i;
struct item_double d;
struct item_string s;
};
对于其他(非常见)成员,我们必须使用用于初始化结构的相同结构类型。这就是该type
领域存在的原因。
作为这种完全安全用法的示例,请考虑以下打印项目列表中的值的函数:
void print_items(const struct item *list, FILE *out)
{
const char *separator = NULL;
fputs("{", out);
while (list) {
if (separator)
fputs(separator, out);
else
separator = ",";
if (list->type == ITEMTYPE_INT)
fprintf(out, " %d", ((const struct item_int *)list)->value);
else
if (list->type == ITEMTYPE_DOUBLE)
fprintf(out, " %f", ((const struct item_double *)list)->value);
else
if (list->type == ITEMTYPE_STRING)
fprintf(out, " \"%s\"", ((const struct item_string *)list)->value);
else
fprintf(out, " (invalid)");
list = list->next;
}
fputs(" }\n", out);
}
请注意,我value
为 value 字段使用了相同的名称,只是因为我没有想到更好的名称;它们不需要相同。
类型双关语出现在fprintf()
语句中,并且当且仅当 1) 结构是使用与type
字段匹配的结构初始化的,并且 2)union item_types
在当前范围内可见时才有效。
我尝试过的当前 C 编译器中没有一个与上述代码有任何问题,即使在破坏标准行为某些方面的极端优化级别也是如此。(我没有检查过 MSVC,但它确实是一个 C++ 编译器,它也可以编译大多数 C 代码。但是,如果上面的代码有任何问题,我会感到惊讶。)