C语言,从联合看字节序

13 篇文章 0 订阅

C语言中的联合(union)类型为我们提供了操纵和解读“数据”的独特方式,它允许对同一块内存以不同的方式进行解读和操纵。

union UINT {
    unsigned int intValue;   //占4个字节
    unsigned char bytes[4];  //占4个字节
};  //注意末尾分号不能少

本文引用自作者编写的下述图书; 本文允许以个人学习、教学等目的引用、讲授或转载,但需要注明原作者"海洋饼干叔
叔";本文不允许以纸质及电子出版为目的进行抄摘或改编。
1.《Python编程基础及应用》,陈波,刘慧君,高等教育出版社。免费授课视频 Python编程基础及应用
2.《Python编程基础及应用实验教程》, 陈波,熊心志,张全和,刘慧君,赵恒军,高等教育出版社Python编程基础及应用实验教程
3. 《简明C及C++语言教程》,陈波,待出版书稿。免费授课视频

上述代码定义了一个名为UINT的联合类型。该类型提供了两个成员,分别是unsigned int类型的intValue,以及元素类型为unsigned char的长度为4的字符数组bytes。这两个成员的内存空间是共享的,即一个union UNIT类型的对象只占4个字节的空间。当以成员intValue进行操作时,这4个字节的内存被当成一个unsigned int进行操纵和解读;当以成员bytes进行操作时,这4个字节的内存被当成一个4字节的字符数组进行操纵和解读。

我们通过下述C语言程序来解释联合类型的使用方法。

//Project - UnionExample
#include <stdio.h>
   
union UINT {
    unsigned int intValue;   //占4个字节
    unsigned char bytes[4];  //占4个字节
};  //注意末尾分号不能少
  
int main() {
    union UINT v = {.intValue=0x11223344};

    printf("&v = %p, &v.intValue = %p, v.bytes = %p\n",
            &v, &v.intValue, v.bytes);
   
    printf("v.bytes[0..3] = 0x%x 0x%x 0x%x 0x%x\n",
            v.bytes[0], v.bytes[1], v.bytes[2], v.bytes[3]);
  
    v.bytes[0] = 0x55; v.bytes[1] = 0x66;  v.bytes[2] = 0x77;  v.bytes[3] = 0x88;
    printf("v.intValue = 0x%x\n",v.intValue);
 
    printf("sizeof(v) = %lld",sizeof(v));
    return 0;
 }

上述程序的执行结果为:

&v = 000000000061FE1C, &v.intValue = 000000000061FE1C, v.bytes = 000000000061FE1C
v.bytes[0..3] = 0x44 0x33 0x22 0x11
v.intValue = 0x88776655
sizeof(v) = 4

说明:在读者的计算机上,执行结果中的地址很可能与本书不同。

🎯要点"."被称为成员操作符,a.b意为对象a的b成员。

🚩第10行:C语言中,union UINT作为一个整体,代表名为UNIT的联合类型。在C++语言中,使用UINT类型时,前面的union关键字可以省略。此处的v是一个对象,其类型为union UINT。因为联合对象的多个成员是内存共享的,所以v的初始值必须以{ }包裹起来,.intValue指明了v初始化的实际动作是把0x11223344赋值给v的intValue成员。作者在这里故意使用了十六进制,因为十六进制每位占4个比特,每两位占1个字节。v定义并初始化以后,其内存结构可以用图10-1表示。图10-1 联合对象v的内存结构

如图10-1所示,v占据了地址为0x0061FE1C、0x0061FE1D、0x0061FE1E和0x0061FE1F的连续4个字节的存储空间。v的所谓数据成员,无非这4字节内存的不同视图(view)而已。从v.intValue的角度看,这是一个地址为0x0061FE1C的32位无符号整数;从v.bytes的角度看,这是一个从地址0x0061FE1C开始的4个元素的字符数组。

读者可能注意到,图10-1 中,4个字节从低地址往高地址方向读,依次是0x44、0x33、0x22和0x11,其顺序与作为无符号整数的v.intValue的值正好相反。这是因为,Intel的x86系列CPU执行Little Endian的字节顺序(小端序),高位字节(0x11)存高地址(0x0061FE1F)。

🚩第12 ~ 13行:依次打印v的地址,v.intValue的地址,v.bytes的地址(数组名即为地址)。从执行结果的第1行可见,3个地址值相同。这证实,联合对象v的不同成员间是共享内存的。此处的v.intValue应用了"."操作符,读者可以形象地将其理解为“v对象的intValue”。

🚩第15 ~ 16行:依次打印v.bytes的4个元素。这相当于从v.bytes的角度去解释v.intValue的数据。执行结果的第2行证实,v.intValue最高位字节的0x11存在了v.bytes[3]里。如刚才所述,这是小端序导致的。

🚩第18 ~ 19行:对v.bytes成员进行赋值,然后再以v.intValue成员解释数据。执行结果的第3行证实,对v.bytes的修改即是对v.intValue的修改。

🚩第21行:打印sizeof(v),执行结果证实,联合对象v占4个字节的空间。

请阅读下述C语言程序:

//Project - MoreUnion
#include <stdio.h>
   
union UMore {
    double dValue;    //全部8个字节
    char   cValue;    //8个字节中的前1个字节
    int    iValue;    //8个字节中的前4个字节
};
   
int main() {
    printf("sizeof(union UMore) = %d\n", sizeof(union UMore));
 
    union UMore v = {33.22};  //未指定初始化成员时默认赋值给第0个成员dValue
    printf("v.dValue = %f\n",v.dValue);

    printf("&v.dValue = %p, &v.cValue = %p, &v.iValue = %p\n",
            &v.dValue, &v.cValue, &v.iValue);
 
    union UMore* p = &v;
    printf("p->iValue = %d", p->iValue);
 
    return 0;
}

上述代码的执行结果为:

sizeof(union UMore) = 8
v.dValue = 33.220000
&v.dValue = 000000000061FE10, &v.cValue = 000000000061FE10, &v.iValue = 000000000061FE10
p->iValue = -171798692

说明:在读者的计算机上,执行结果中的地址很可能与本书不同。

🎯要点"->"称为指向操作符,如果p是一个指针,p->a表示p所指向的对象的a成员。p->a与(*p).a等价。

上述程序中,联合类型UMore的三个成员分别占据8个、1个及4个字节的空间。从执行结果看,UMore类型的联合对象v占据8个字节的空间,正好是各成员空间尺寸的最大值。同时,还应注意到v的三个成员的地址相同。这意味着,当我们操作v.cValue时,仅会影响v的第0个字节,其余7个字节不受影响。

🚩第13 ~ 14行:当联合对象初始化时,如果没有指明被初始化的成员,则会默认初始化第1个成员。执行结果的第2行证实,v.dValue被初始化为33.22。

🚩第16 ~ 17行:打印v的dValue、cValue及iValue成员的地址。执行结果证实,三者的地址相同。

🚩第19 ~ 20行:定义了一个指向v的指针p。p->iValue表示指针p所指向的联合对象的iValue成员。从执行结果可见,将8个字节double数据的前4个字节当成int来解读,结果是“莫名其妙”的。

练习巩固 👣

10-3(判定大小端序)结合typedef定义一个联合类型Endian,使得下述代码能正确判断CPU的大小端序。请解释该程序的工作原理。

int main(){
    Endian e = {.i=99};
    if (e.b==99)
        printf("little endian."); //小端序:高位字节在高地址
    else
        printf("big endian.");    //大端序:高位字节在低地址
    return 0;
}

提示:联合体包括无符号整数成员i和无符号字符成员b。

为了帮助更多的年轻朋友们学好编程,作者在B站上开了两门免费的网课,一门零基础讲Python,一门零基础C和C++一起学,拿走不谢!

简洁的C及C++
由编程界擅长教书,教书界特能编程的海洋饼干叔叔打造
Python编程基础及应用
由编程界擅长教书,教书界特能编程的海洋饼干叔叔打造

如果你觉得纸质书看起来更顺手,目前Python有两本,C和C++在出版过程中。

Python编程基础及应用

Python编程基础及应用实验教程
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值