/x86/Debian GNU/Linux/gcc
联合体的所有成员引用的是内存中的相同地址。访问联合的不同成员时,会根据此成员的类型去访问对应的字节,并根据此成员的类型去解释这些字节对应位的含义(表示int还是float)。
联合体初始化时,初始化的是联合的第一个成员(所以类型应该与联合的第一个成员相同,否则会发生类型转换),而且初始值必须位于一对大括号内。
要想知道平台采用的大端还是小端的方式存储数据,可以看.c文件对应的目标文件或者可执行文件。在知道系统采用的存储方式后,可以反推内存地址采用的高地址还是低地址来表示。
//When this paltom uses litte address to be many bytes's address //Test platom is litte or bit endian void test_endian(void) { printf("\n--------------test_endian function------------------\n"); union one_test{ unsigned int i; unsigned char ch; }my_test={1}; printf("address of i = %p\naddress of ch = %p\n\n", &my_test.i, &my_test.ch); printf("ch = %d\nhex dump of ch = 0x%x\n", my_test.ch, my_test.ch); if(my_test.ch == 1){ printf("This platom uses litte endian\n"); }else{ printf("This platom uses big endian\n"); } }
联合体my_test中的i和ch的内存地址相同。不同的是,访问my_test.i时会读sizeof(unsigned int)字节内容并按照int类型解释这些位;访问my_test.ch时会读一个字节内容并按照char类型(实质是int)解释这些位。
my_test经初始化后,my_test.ch与my_test.i的第一个字节(表示整个变量的地址)内容相同。如果系统采用低地址来作为一个类型的地址(int占4个字节,用4个字节中的最低地址表示int变量的地址),那么以上程序就能够测试出系统采用的大端还是小端方式存储数据。如果my_test.ch值为1则表示系统采用小端方式存储数据,否则为大端方式。如果系统采用高地址来作为一个类型的地址,那么my_test.ch值为1则表示系统采用大端方式存储数据,否则为小端方式。
将这段代码放在main函数中,某次运行的结果如下:
--------------test_endian function------------------ address of i = 0xbff03cdc address of ch = 0xbff03cdc ch = 1 hex dump of ch = 0x1 This platom uses litte endian |
已经在某.c文件的目标文件中得知/x86/Debian GNU/Linux平台采用的小端方式存储数据,故而得它用的低地址表示内存段地址。
数组元素的地址按照下标连续递增。结构体内的成员的地址虽不连续,但会按照成员的定义顺序各成员的地址会由低到高。
//Test Bit-field's memory layout void test_bit_field_layout(void) { printf("\n--------------test_bit_field_layout---------------\n"); union two_test{ struct bit_field{ unsigned int one :8; unsigned int two :1; unsigned int three :3; unsigned int :4; unsigned int four :1; unsigned int five :8; }my_bit_field; unsigned char byte[4]; }my_test={{2, 1, 4, 1, 16}}; printf("value of byte:\nbyte[0] = %d\tbyte[1] = %d\tbyte[2] = %d\tbyte[3] = %d\n", \ my_test.byte[0], my_test.byte[1], my_test.byte[2], my_test.byte[3]); printf("\nhex dump of byte:\nbyte[0] = 0x%x\tbyte[1] = 0x%x\tbyte[2] = 0x%x\tbyte[3] = 0x%x\n", \ my_test.byte[0], my_test.byte[1], my_test.byte[2], my_test.byte[3]); }
将这个函数放在main函数中,某次运行得到的结果如下:
--------------test_bit_field_layout--------------- value of byte: byte[0] = 2 byte[1] = 9 byte[2] = 33 byte[3] = 0 hex dump of byte: byte[0] = 0x2 byte[1] = 0x9 byte[2] = 0x21 byte[3] = 0x0 |
分析各位段存储的数据(以小端的方式存储):
如果将未命名的字段unsignedint :4;屏蔽,则程序运行如下:
--------------test_bit_field_layout--------------- value of byte: byte[0] = 2 byte[1] = 25 byte[2] = 2 byte[3] = 0 hex dump of byte: byte[0] = 0x2 byte[1] = 0x19 byte[2] = 0x2 byte[3] = 0x0 |
则各位段存储的数据(以小端的方式存储):
与一般结构体一样,为了访问的效率,编译器可能会在两个位段间及结构体末尾加入填充位。
关于如何排列Bit-field在C标准中没有详细的规定,这跟Byte Order、Bit Order、对齐等问题都有关,不同的平台和编译器可能会排列得很不一样,要编写可移植的代码就不能假定Bit-field是按某一种固定方式排列的。同时Bit-field在驱动程序中是很有用的,因为经常需要单独操作设备寄存器中的一个或几个bit,但一定要小心使用,首先弄清楚每个Bit-field和实际bit的对应关系。
CNote Over.