考虑以下同构结构:
struct myStruct {
void* a;
char* b;
int* c;
};
我相信它是同类的,因为所有数据类型都是指针。
鉴于此结构,以下代码在C99中是否有效且可移植?
int main()
{
void* x = NULL;
char* y = "hello";
int* z = malloc(sizeof(int) * 10);
z[2] = 10;
void** myArray = malloc(sizeof(void*) * 3);
myArray[0] = x;
myArray[1] = y;
myArray[2] = z;
struct myStruct* s = (struct myStruct*)myArray;
printf("%p %s %d\n", s->a, s->b, s->c[2]);
return 0;
}
我理解结构通常会在组件之间添加填充以保持结构的大小一致,但是,因为指针的类型都是相同的,是否可以安全地假设不添加填充?我不一定要问是否有100%的保证(我明白这完全是特定于实现的,并且编译器可能因为不明原因而添加填充),更多我要求填充可能添加到同类的原因结构,如果有任何原因。
答案 0 :(得分:19)
不,它不便携。指针的大小实际上可能不同,这意味着您的结构中可能存在填充。
在大多数现代平台上,这不会成为问题,但标准中没有任何内容表明所有指针必须相同尺寸。只有那些指针可以隐式转换为void *
。
指针大小不同的平台的一个很好的例子是DOS(仍在主动使用,例如在嵌入式系统上)和其他16位分段系统。
答案 1 :(得分:12)
代码违反了别名规则。 void *和struct myStruct类型不兼容,在这种情况下没有例外。 printf语句中结构指针的取消引用会导致未定义的行为:
printf("%p %s %d\n", s->a, s->b, s->c[2]);
即使结构与三个void指针具有相同的大小,没有填充,并且具有相同的对齐要求,也是如此。
答案 2 :(得分:1)
没有什么能阻止编译器对结构和数组使用不同的对齐方式。例如,在使用GCC的SPARC上,可以使用-mfaster-structs
/ -mnofaster-structs
开关将结构对齐到4或8字节边界,而指针数组则与其元素的大小对齐(同样,4或8个字节,具体取决于您使用的指针类型)。如果不匹配,你的演员
struct myStruct* s = (struct myStruct*)myArray;
将产生无效指针并导致UB。
答案 3 :(得分:0)
(...)因为指针的类型都是相同的,是否可以安全地假设不添加填充?
不,一点也不。填充(包装)与结构是完全无关的是完全不相关的问题。它工作简单"只要一个成员比当前包装短,它就会被填补以补偿"。例如,单数struct
的同质char
几乎总是被大量填充。
你可以做#pragma pack(16)
,你的结构成员间隔16个字节。或者您可以将打包设置为1个字节,然后将void*
与单个char
混合,但仍然没有填充(但指针对齐令人头疼)。