取消对匿名结构指针的强制转换是否违反严格的别名?
Does dereferencing a cast to an anonymous structure pointer violate strict aliasing?
关于 C 标准在多大程度上保证结构布局的一致性,我听说过相互矛盾的事情。有限范围的争论提到了严格的别名规则。例如,比较这两个答案: and .
在下面的代码中,我假设在所有结构 foo
、bar
和 struct { char *id; }
中 char *id
都在同一个地方,因此可以安全地在它们之间进行转换如果它是唯一访问的成员。
无论转换是否会导致错误,它是否违反了严格的别名规则?
#include <string.h>
struct foo {
char *id;
int a;
};
struct bar {
char *id;
int x, y, z;
};
struct list {
struct list *next;
union {
struct foo *foop;
struct bar *barp;
void *either;
} ptr;
};
struct list *find_id(struct list *l, char *key)
{
while (l != NULL) {
/* cast to anonymous struct and dereferenced */
if (!strcmp(((struct { char *id; } *)(l->ptr.either))->id, key))
return l;
l = l->next;
}
return NULL;
}
gcc -o /dev/null -Wstrict-aliasing test.c
注意 gcc
没有错误。
是的,您的程序中存在多个 aliasing-related 问题。使用与底层对象类型不匹配的匿名结构类型的左值会导致未定义的行为。它可以用类似的东西修复:
*(char**)((char *)either + offsetof(struct { ... char *id; ... }, id))
如果您知道 id
成员在所有成员中都处于相同的偏移量(例如,它们都共享相同的前缀)。但在你的特定情况下,它是你可以做的第一个成员:
*(char**)either
因为将指向结构的指针转换为指向其第一个成员的指针(并返回)始终有效。
另一个问题是您对联合的使用是错误的。最大的问题是它假定 struct foo *
、struct bar *
和 void *
都具有相同的大小和表示形式,这是无法保证的。此外,访问联合成员而不是先前存储的成员可以说是未定义的,但根据缺陷报告中的解释,可以安全地说它等同于 "reinterpret cast"。但这让你回到错误假设相同的问题 size/representation.
您应该只删除联合,使用 void *
成员,并将 value(而不是重新解释位)转换为正确的指针类型以访问pointed-to 结构(struct foo *
或 struct bar *
)或其初始 ID 字段(char *
)。
关于 C 标准在多大程度上保证结构布局的一致性,我听说过相互矛盾的事情。有限范围的争论提到了严格的别名规则。例如,比较这两个答案: and .
在下面的代码中,我假设在所有结构 foo
、bar
和 struct { char *id; }
中 char *id
都在同一个地方,因此可以安全地在它们之间进行转换如果它是唯一访问的成员。
无论转换是否会导致错误,它是否违反了严格的别名规则?
#include <string.h>
struct foo {
char *id;
int a;
};
struct bar {
char *id;
int x, y, z;
};
struct list {
struct list *next;
union {
struct foo *foop;
struct bar *barp;
void *either;
} ptr;
};
struct list *find_id(struct list *l, char *key)
{
while (l != NULL) {
/* cast to anonymous struct and dereferenced */
if (!strcmp(((struct { char *id; } *)(l->ptr.either))->id, key))
return l;
l = l->next;
}
return NULL;
}
gcc -o /dev/null -Wstrict-aliasing test.c
注意 gcc
没有错误。
是的,您的程序中存在多个 aliasing-related 问题。使用与底层对象类型不匹配的匿名结构类型的左值会导致未定义的行为。它可以用类似的东西修复:
*(char**)((char *)either + offsetof(struct { ... char *id; ... }, id))
如果您知道 id
成员在所有成员中都处于相同的偏移量(例如,它们都共享相同的前缀)。但在你的特定情况下,它是你可以做的第一个成员:
*(char**)either
因为将指向结构的指针转换为指向其第一个成员的指针(并返回)始终有效。
另一个问题是您对联合的使用是错误的。最大的问题是它假定 struct foo *
、struct bar *
和 void *
都具有相同的大小和表示形式,这是无法保证的。此外,访问联合成员而不是先前存储的成员可以说是未定义的,但根据缺陷报告中的解释,可以安全地说它等同于 "reinterpret cast"。但这让你回到错误假设相同的问题 size/representation.
您应该只删除联合,使用 void *
成员,并将 value(而不是重新解释位)转换为正确的指针类型以访问pointed-to 结构(struct foo *
或 struct bar *
)或其初始 ID 字段(char *
)。