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

1 联合体

(1)联合体机制

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

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

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

要想知道平台采用的大端还是小端的方式存储数据,可以看.c文件对应的目标文件或者可执行文件。在知道系统采用的存储方式后,可以反推内存地址采用的高地址还是低地址来表示。

  1. //When this paltom uses litte address to be many bytes's address  
  2. //Test platom is litte or bit endian  
  3. void test_endian(void)  
  4. {  
  5.     printf("\n--------------test_endian function------------------\n");  
  6.     union one_test{  
  7.         unsigned int i;  
  8.         unsigned char ch;  
  9.     }my_test={1};  
  10.       
  11.     printf("address of i = %p\naddress of ch = %p\n\n", &my_test.i, &my_test.ch);  
  12.     printf("ch = %d\nhex dump of ch = 0x%x\n", my_test.ch, my_test.ch);  
  13.       
  14.     if(my_test.ch == 1){  
  15.         printf("This platom uses litte endian\n");  
  16.     }else{  
  17.         printf("This platom uses big endian\n");  
  18.     }  
  19. }  

联合体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平台采用的小端方式存储数据,故而得它用的低地址表示内存段地址。

位段

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

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

(2)位段的存储 

  1. //Test Bit-field's memory layout  
  2. void test_bit_field_layout(void)  
  3. {  
  4.     printf("\n--------------test_bit_field_layout---------------\n");  
  5.     union two_test{  
  6.         struct bit_field{  
  7.             unsigned int one    :8;  
  8.             unsigned int two    :1;  
  9.             unsigned int three  :3;  
  10.             unsigned int        :4;  
  11.             unsigned int four   :1;  
  12.             unsigned int five   :8;  
  13.         }my_bit_field;  
  14.         unsigned char byte[4];  
  15.     }my_test={{2, 1, 4, 1, 16}};  
  16.   
  17.     printf("value of byte:\nbyte[0] = %d\tbyte[1] = %d\tbyte[2] = %d\tbyte[3] = %d\n",      \  
  18.         my_test.byte[0], my_test.byte[1], my_test.byte[2], my_test.byte[3]);  
  19.     printf("\nhex dump of byte:\nbyte[0] = 0x%x\tbyte[1] = 0x%x\tbyte[2] = 0x%x\tbyte[3] = 0x%x\n", \  
  20.         my_test.byte[0], my_test.byte[1], my_test.byte[2], my_test.byte[3]);  
  21. }  

将这个函数放在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的对应关系

永不止步步 发表于04-19 10:58 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

永不止步步
金币:67410个|学分:308467个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号