关于C struct / union的可移植性问题

时间:2009-10-15 21:59:05

标签: c struct portability unions

假设我从外部库中获得以下类型:

union foreign_t {
    struct {
        enum enum_t an_enum;
        int an_int;
    } header;
    struct {
        double x, y;
    } point;
};

可以安全地假设以下代码片段在不同平台和不同编译器上按预期工作吗?

struct pair_t {
    double x, y;
};

union foreign_t foreign;
struct pair_t *p_pair;

p_pair = (struct pair_t *) &foreign;
p_pair->x = 1234;
p_pair->y = 4321;

/* Expected result: (1234, 4321) or something like that */
printf("(%lf, %lf)", foreign.point.x, foreign.point.y);

编辑:

遵循严格的别名建议我做了以下测试:

#include <stdint.h>
#include <stdio.h>

int main()
{
    uint16_t word = 0xabcd;
    uint8_t tmp;
    struct {
        uint8_t low;
        uint8_t high;
    } *byte = (void *) &word;

    tmp = byte->low;
    byte->low = byte->high;
    byte->high = tmp;

    printf("%x\n", word);

    return 0;
}

以上显然是无辜的代码是不可靠的:

$ gcc -O3 -fno-strict-aliasing -otest test.c
$ ./test
cdab
$ gcc -O3 -fstrict-aliasing -otest test.c
$ ./test
abcd

开发人员没有和平......

8 个答案:

答案 0 :(得分:6)

正如您所写,我相信它应该适用于任何架构上的任何编译器。但是,我确实认为它在技术上违反了strict aliasing规则。您在不相关的指针类型之间进行转换,因此过于激进的优化器可能会重新排序某些内存读取和写入,因为它假定某些指针不会相互别名。

不幸的是,我不认为有一种方法可以使这个代码绝对防止严格别名,假设您无法修改foreign_t的定义。由于内部结构没有名称,因此无法构造指向编译器将假定为可别的指针。但实际上,我认为您的代码不会出现问题。

答案 1 :(得分:2)

是的,应该是完全便携的。 foreign是一个联盟的事实甚至从未涉及到它,因为你从未将它用作header

(你不能写header然后从point读取并希望它在所有平台上都能正常工作。但是,你不想这样做,所以你应该没事。)

答案 2 :(得分:2)

是的,这是完全合理的。 ANSI C标准表明,您不应该在联合中写入一个“类型”并读出另一个并期望在非常特定的情况下获得可靠的东西。在这里,您希望以某种方式将某些内容写入联合,然后以相同的方式将其读出。你保证在联合的开头没有填充,并且适当的指针对齐,所以据我所知,你应该没问题。

答案 3 :(得分:1)

是的,这将正常工作。但是,只要您写入foreign.header,所有投注都会关闭foreign.point的内容,即使某个特定操作适用于给定的编译器。

答案 4 :(得分:1)

是的,保证相同的结构具有相同的尺寸和对齐要求。

答案 5 :(得分:1)

正如所写,是的,它将在任何单一平台上按预期工作。

foreign的一个更典型的定义是将union包装在包含类型鉴别器字段的struct中,以便在运行时明确知道union的有效分支该类型的价值。

有趣的是,当您希望将foreign的值从平台A传达到平台B并再次获取预期数据时。在那里,您至少会遇到对齐,大小和字节顺序差异,甚至数字表示差异,因为标准实际上不需要IEEE浮点数或2的补码二进制整数。

在实践中,事情并没有那么糟糕,但如果需要二进制交换格式,则可以通过特定于平台的测试用例和/或编译时断言最好地减轻可移植性问题。

或者,将来自特定于平台的structunion的数据封送到用于存储和传输的良好定义的八位字节序列中是稳健的答案。例如,这是MPEG标准采用的方法。

答案 6 :(得分:0)

假设doubleintenum保持一致性(我不会发誓,但我相信double是IEEE标准),它应该有效可靠。但是,int会根据系统字词发生变化,{em> I 知道的enum不能依赖于特定大小。

答案 7 :(得分:-1)

可以安全地假设以下代码片段在不同平台和不同编译器上按预期工作吗?

简单的答案是否定的......

对于每台机器和编译器,您需要找出哪个命令将正确对齐结构,这是主要的可移植性问题之一