gcc C++中未定义但正确的行为

mhd8tkvw  于 2023-04-30  发布在  其他
关注(0)|答案(1)|浏览(100)

下面的程序对bit-fields的打包、类型双关和对象表示进行了假设。换句话说,它在可移植性方面没有任何伪装。它仍然具有being fast的优势。这个程序是否可以说是正确的,因为它需要一个受支持的编译器和一个具有正确endity的平台?

#include <cassert>
#include <cstdint>

union Data {
    uint8_t raw[2];
    struct __attribute__((packed)) {
        unsigned int field1: 3, field2: 3, field3: 1, field4: 2;
        unsigned int field5: 7;
    } interpreted;
};

int main() {
    static_assert(sizeof(Data) == 2, "Struct size incorrect");
    static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, "Only little-endian platform support is currently implemented");
    static_assert(
        #if defined(__GNUC__) || defined(__clang__)
                    true,
        #else
                    false,
        #endif
            "Compiler is neither GCC nor Clang"
    );
    Data d{.raw{0x69, 0x01}};
    /**
     *
     * 0x69  = 0b0110 1001, 0x01 = 0b0000 0001
     * On a little endian platform, each byte will be laid out in memory in reverse order, that is
     * [1001 0110, 1000 0000].
     * The GCC and clang compilers lay out the struct members in the order they are defined, and each of the values will be interpreted in the reverse order (little-endian), so
     * field1: 100 = 0b001
     * field2: 101 = 0b101
     * field3: 1   = 0b1
     * field4: 01  = 0b10
     * field5: 0000000 = 0
     *
     * Therefore, the following assertions will hold if the preceding assertions were satisfied.
     */
    assert(d.interpreted.field1 == 1);
    assert(d.interpreted.field2 == 5);
    assert(d.interpreted.field3 == 1);
    assert(d.interpreted.field4 == 2);
    assert(d.interpreted.field5 == 0);
}
0ve6wy6x

0ve6wy6x1#

你的问题让我考虑如何使用Ada编程语言来表达这一点。下面的示例将按照您希望的方式工作。

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
   type bit_3 is range 0 .. 2**3 - 1 with
      Size => 3;
   type bit_1 is range 0 .. 1 with
      Size => 1;
   type bit_2 is range 0 .. 3 with
      Size => 2;
   type bit_7 is range 0 .. 2**7 - 1 with
      Size => 7;

   type bit_fields is record
      field1 : bit_3;
      field2 : bit_3;
      field3 : bit_1;
      field4 : bit_2;
      field5 : bit_7;
   end record;

   for bit_fields use record
      field1 at 0 range 0 ..  2;
      field2 at 0 range 3 ..  5;
      field3 at 0 range 6 ..  6;
      field4 at 0 range 7 ..  8;
      field5 at 0 range 9 .. 15;
   end record;

   type unint_8 is range 0 .. 2**8 - 1 with
      Size => 8;

   package unint_io is new Integer_IO (unint_8);
   use unint_io;
   type two_bytes is array (Positive range 1 .. 2) of unint_8;

   foo : two_bytes;
   bar : bit_fields with
      Address => foo'Address;

begin
   foo := (16#69#, 1);
   for value of foo loop
      Put (Item => value, Base => 2);
      Put (" ");
   end loop;
   New_Line;

   Put_Line ("Field1 :" & bit_3'Image (bar.field1));
   Put_Line ("Field2 :" & bit_3'Image (bar.field2));
   Put_Line ("Field3 :" & bit_1'Image (bar.field3));
   Put_Line ("field4 :" & bit_2'Image (bar.field4));
   Put_Line ("field5 :" & bit_7'Image (bar.field5));
end Main;

我首先为位字段中需要的每个字段类型定义数据类型。因此,类型bit_3被定义为3比特整数,bit_1被定义为1比特整数,bit_2被定义为2比特整数,并且bit_7被定义为7比特整数。
然后,我创建了一个名为bit_fields的记录类型,指定了您在示例中指定的字段。记录定义子句之后的记录表示子句指定记录的精确位布局,位号从字节0的开头开始。
然后,我创建了一个名为unint_8的1字节整数类型,大小为8位。我为unint_8示例化了一个通用I/O包,以便可以以二进制形式输出该类型的值。我创建了一个名为two_bytes的数组类型,其中包含两个unint_8元素。
Ada没有工会本身。我通过创建一个名为foo的two_bytes示例,后面跟着一个名为bar的bit_fields示例,并将bar的地址设置为与变量foo相同的地址,实现了相同的效果。
我将值hex 69和1分配给foo的两个元素,并以二进制打印foo,然后以十进制打印bar的每个字段。
这个程序的输出是:

2#1101001# 2#1# 
Field1 : 1
Field2 : 5
Field3 : 1
field4 : 2
field5 : 0

相关问题