是否将数组转换为C中可移植的同类struct?

时间:2017-03-21 07:01:19

标签: c struct

考虑以下同构结构:

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%的保证(我明白这完全是特定于实现的,并且编译器可能因为不明原因而添加填充),更多我要求填充可能添加到同类的原因结构,如果有任何原因。

4 个答案:

答案 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混合,但仍然没有填充(但指针对齐令人头疼)。