【问题标题】:Why value stored in struct and union object correspond to any padding bytes take unspecified values?为什么存储在结构和联合对象中的值对应于任何填充字节采用未指定的值?
【发布时间】:2018-04-23 16:32:17
【问题描述】:

来自 C11 标准

我只是在 C11 标准中阅读了此内容,无法理解为什么值存储在 struct/union 的对象中,填充字节的对象表示采用未指定的值?

我知道标准没有规定要求的未指定方式。


当一个值存储在结构或联合类型的对象中时, 包括在成员对象中,对象表示的字节 对应于任何填充字节的值采用未指定的值。

结构或联合对象的值绝不是陷阱 表示,即使结构成员的值或 union 对象可能是一个陷阱表示

谁能给我一个例子来更好地理解这句话?

【问题讨论】:

    标签: c padding


    【解决方案1】:

    基本上是这样的:

    • 为了获得成员的正确对齐,结构/联合需要填充字节。编译器会根据需要自动插入这些内容。
    • C 委员会没有对填充字节必须具有的值提出任何要求。这样做是故意的,以便程序在初始化或复制结构时不需要写入填充字节。如果标准要求填充字节具有例如值零,那么这将引入非常轻微的执行开销。

      (这种微乎其微的潜在性能提升就是为什么结构会附加所有这些晦涩的机制。这确实是 C 语言的精神——如果我们能够以混淆和不一致性为代价使某些东西变得稍微快一点的话,然后就这样吧。)

    • 但是,C 还允许外来系统具有陷阱表示 - 某些位序列在读取时会产生某种运行时异常。如果允许填充字节具有任何值,它们可能最终成为陷阱表示。因此,存在一个例外,即填充字节可以具有任何值,但不能具有陷阱表示的值。

    因此:您不能相信填充字节具有任何给定值(除非它们不能是陷阱表示)。填充字节的值可能因情况而异 - 不能保证它们的值是一致的。

    考虑一个没有陷阱表示的普通 32 位二进制补码系统:

    typedef struct
    {
      uint8_t  u8;
      uint32_t u32;
    } something_t;
    
    something_t thing1 = {1, 2};
    something_t thing2 = {3, 4}
    

    这里,一种可能的内存布局是这样的(十六进制,小端):

    01 AA BB CC 02 00 00 00  // thing 1
    03 55 66 77 04 00 00 00  // thing 2
    ^  ^        ^
    u8 padding  u32
    

    在 thing1 中,01 是 u8 成员,AA BB CC 序列是用未指定值填充字节,02 00 00 00 是 u32 成员。

    如果我们现在写thing1 = thing2,则允许编译器执行以下操作之一:

    • 复制整个thing2 并覆盖所有thing1,包括填充,如果这是最有效的,
    • 或如果效率更高,复制u8 和u32,但不要写入填充字节,让它们保持原样,导致thing1 现在具有内存布局03 AA BB CC 04 00 00 00。李>

    这实际上就是为什么我们不能将结构与== 运算符或memcmp() 之类的函数进行比较的原因。

    【讨论】:

      【解决方案2】:

      假设sizeof(long)=8,sizeof(int)=4,64位机:

      struct {
          int a;
          long b;
      } obj = {0, 0};
      

      在 &obj.a 和 &obj.b 之间有 4 个字节的填充是有意义的。填充的内容应该是什么?为什么要强制运行时将任何东西放在那里? obj 的布局也可以是 0x00000000, 0xDEADBEEF, 0x00000000, 0x00000000 - 即 4 字节的垃圾,因为它们不需要被访问(因此不应该)。

      一般来说,问题是相反的 - 如果您认为应该指定它(即您想强制编译器编写者做更多的工作,并且可能效率较低的工作),那么您应该解释原因。

      【讨论】:

        【解决方案3】:

        问题是你永远不知道结构或联合的元素是如何事先确定在内存中的。填充位可能因系统而异。

        您也可以考虑编译器对此的处理方法。布局它们实现的优化是不同的。在某些编译器中,它将是一个,然后在另一个编译器中有所不同。

        陷阱表示是适合类型占用空间的位模式,但如果用作该类型的值则会触发未定义的行为。

        例如:-

        struct a{
           int a;
           char b;
        };
        

        这里结构的大小不一定是a和b的大小。正在添加填充,这是标准不评论的内容。您想将结构作为一个整体来工作,表示工作。但是如果你想访问具有严格 8 字节表示的元素,它可能会失败。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-02-10
          • 2022-12-18
          • 1970-01-01
          • 2016-05-08
          • 2023-03-29
          • 1970-01-01
          相关资源
          最近更新 更多