C的联合体(测试数据存储的大小端模式) 位段(位段与位的对应关系)

/x86/Debian GNU/Linux/gcc

 

1 联合体

(1)联合体机制

联合体的所有成员引用的是内存中的相同地址。访问联合的不同成员时,会根据此成员的类型去访问对应的字节,并根据此成员的类型去解释这些字节对应位的含义(表示int还是float)。

 

联合体初始化时,初始化的是联合的第一个成员(所以类型应该与联合的第一个成员相同,否则会发生类型转换),而且初始值必须位于一对大括号内。

 

(2)用联合体测平台数据存储是大端还是小端

要想知道平台采用的大端还是小端的方式存储数据,可以看.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中的ich的内存地址相同。不同的是,访问my_test.i时会读sizeof(unsigned int)字节内容并按照int类型解释这些位;访问my_test.ch时会读一个字节内容并按照char类型(实质是int)解释这些位。

 

my_test经初始化后,my_test.chmy_test.i的第一个字节(表示整个变量的地址)内容相同。如果系统采用低地址来作为一个类型的地址int4个字节,用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平台采用的小端方式存储数据,故而得它用的低地址表示内存段地址。

 

2 位段

(1)结构体成员的存储地址

数组元素的地址按照下标连续递增。结构体内的成员的地址虽不连续,但会按照成员的定义顺序各成员的地址会由低到高。

 

(2)位段的存储

//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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值