C语言,从联合看字节序

news2025/7/16 7:04:42

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编程基础及应用实验教程
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/9058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

aj-report页面嵌入其他项目

我们前面已经制作了自己的报表,我们可以通过共享报表将结果呈现给其他人,但是对一些小白来说,报表与其他项目合成是一个新的问题。怎么合成呢? 我们继续未完的探索。 1、首先,我们可以创建一个已做好的报表的链接: 如上图,我们可以在报表管理里面分享建成的报表,选…

UnRaid安装CloudDrive以实现阿里云盘、天翼云盘、115网盘挂载

文章目录1、前言2、准备工作2.1、修改Docker源2.2、开启Docker服务的MountFlags功能3、添加Docker应用CloudDrive4、添加云盘1、前言 最近一直在学习UnRaid这个Nas系统&#xff0c;折腾起来易用性十足&#xff0c;但由于其自带的应用市场不能完全满足所有人的需求&#xff0c;…

高纯度高活性艾美捷人重组MEGACD40L蛋白(可溶性)

艾美捷人重组MEGACD40L蛋白&#xff08;可溶性&#xff09;&#xff1a;高活性、高纯度CD40L蛋白&#xff0c;用于免疫应答的共刺激激活。 艾美捷人重组MEGACD40L蛋白&#xff08;可溶性&#xff09;特点&#xff1a; 1、高活性MEGACD40L低聚物模拟体内膜辅助CD40L聚集和刺激&…

【C++修炼之路】9. string类的模拟实现

每一个不曾起舞的日子都是对生命的辜负 string类的模拟实现前言代码&#xff1a;1. string.h2. test.cpp扩展&#xff1a;内置类型的拷贝构造总结前言 本篇文章是衔接上一篇string&#xff0c;进行string的模拟实现&#xff0c;其中包含了众多重载函数&#xff0c;以及一些实现…

pytest中allure特性

一、allure.step allure报告最重要的一点是&#xff0c;它允许对每个测试用例进行非常详细的步骤说明 通过 allure.step() 装饰器&#xff0c;可以让测试用例在allure报告中显示更详细的测试过程 step() 只有一个参数&#xff0c;就是title&#xff0c;你传什么&#xff0c;在…

Linux------网络基础1

文章目录计算机网络的发展历程网络协议计算机网络分层体系结构局域网通信的原理IP地址和 MAC地址的区别计算机网络的发展历程 简单的了解一下就行&#xff0c;图就不提供了。 1&#xff0c;最开始&#xff0c;计算机之间是相互独立的&#xff0c;不能沟通交流。 2&#xff0c;…

第02章_MySQL的数据目录

第02章_MySQL的数据目录1. MySQL8的主要目录结构1.1 数据库文件的存放路径1.2 相关命令目录1.3 配置文件目录2. 数据库和文件系统的关系2.1 查看默认数据库2.2 数据库在文件系统中的表示2.3 表在文件系统中的表示1. MySQL8的主要目录结构 [rootatguigu01 ~]# find / -name mys…

React中的useEffect(副作用)

目录 useEffect(副作用)介绍 useEffect(副作用)各种写法的调用时刻 1.写法一&#xff1a;没有依赖项时 父组件给子组件传值&#xff1a; 2.写法二:依赖项中有监听的值时 3.写法三&#xff1a;依赖项为空数组时 4.写法4&#xff1a;清除副作用写法(假如副作用是一个定时器,…

【C++】string类的模拟实现

文章目录一、string类的构造、拷贝构造、赋值重载以及析构1.构造函数2.拷贝构造3.swap问题4.赋值重载5.析构函数二、常用接口1.c_str2.[]3.迭代器和范围for4.size和capacity三、插入1.reserve和resize2.push_back3.append4.5.insert四、删除1.erase2.clear五、查找1.find六、运…

Nginx

What is Nginx&#xff1f; Nginx 同 Apache 一样都是一种 Web 服务器。基于 REST 架构风格&#xff0c;以统一资源描述符&#xff08;Uniform Resources Identifier&#xff09;URI 或者统一资源定位符&#xff08;Uniform Resources Locator&#xff09;URL 作为沟通依据&…

基于51单片机的多功能时钟温度计proteus仿真原理图

本系统是由AT89S52单片机为控制核心&#xff0c;具有在线编程功能&#xff0c;低功耗&#xff0c;能在3V超低压环境中工作&#xff1b;时钟电路由内部时钟电路外接晶振提供&#xff0c;它是一种高性能、低功耗、带RAM的可随时调整时钟电路&#xff0c;工作电压为3V&#xff5e;…

数据中台与大数据、数据仓库、数据湖、BI的区别

一、什么是数据中台 数据中台是一种将企业沉睡的数据变成数据资产&#xff0c;持续使用数据、产生智能、为业务服务&#xff0c;从而实现数据价值变现的系统和机制。通过数据中台提供的方法和运行机制&#xff0c;形成汇聚整合、提纯加工、建模处理、算法学习&#xff0c;并以…

电源管理ISL95869HRTZ、ISL95808HRZ概述、规格和应用

ISL95869完全符合英特尔IMVP9规范&#xff0c;并为处理器的主输入轨道电源提供了完整的解决方案。它提供了一个电压调节器(VR)与两个集成和一个外部门驱动器。VR可以配置为3-&#xff0c;2-或1-相位&#xff0c;提供最大的灵活性。虚拟现实采用串行控制总线SVID (serial contro…

es环境搭建

1.es与es-head的搭建 1.1 es7.6.2 每个es都是自成一个集群&#xff0c;不同于solar还需要zk来搭建集群 1.1.1 下载安装 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-6-2 因为占用内存实在是太大了&#xff0c;我在服务器上装了运行不起来&#xff…

Flameshot源码编译方法

一、简介 Flameshot是一款功能强大但易于使用的屏幕截图软件&#xff0c;中文名称火焰截图。Flameshot 简单易用并有一个CLI版本&#xff0c;所以你也可以从命令行来进行截图。Flameshot 是一个Linux发行版中完全免费且开源的截图工具。 二、在线安装 在线安装方法很简单&…

java基于web的自行车租赁系统ssh

目 录 摘 要 I Abstract II 第1章 绪论 1 1.1 课题背景 1 1.2 课题研究的意义 1 1.3 课题的目标 2 1.4 研究内容与章节安排 2 第2章 可行性分析 3 2.1 经济可行性 3 2.2 技术可行性 3 2.3 操作可行性 4 2.4法律可行性 4 2.5业务流程分析…

win10实现nfs文件共享II

文章目录&#xff08;一&#xff09;在服务器A设置共享目录&#xff08;二&#xff09;在客户端B安装nfs,挂载目录&#xff08;一&#xff09;在服务器A设置共享目录 步骤1&#xff1a;在D盘新建目录“nfs”,将其目录设置为共享目录。 步骤2&#xff1a;点击权限&#xff0c;设…

税票贷产品的准入与额度判断有哪些逻辑

近两周&#xff0c;番茄风控的课程中&#xff0c;涉及的税票贷产品课程干货满满。 今天我们再跟大家讲一下关于税票贷中风控的核心准入策略与额度判断有哪些逻辑是需要关注的&#xff1f; 先来说下税务的数据&#xff0c;然后再来讲下发票类型的数据。 一.关于税务的风控准入策…

PCB Layout爬电距离、电气间隙如何确定-安规

PCB Layout爬电距离、电气间隙如何确定 爬电距离&#xff1a;沿绝缘表面测得的两个导电零部件之间或导电零部件与设备防护界面之间的最短路径。 电气间隙&#xff1a;在两个导电零部件之间或导电零部件与设备防护界面之间测得的最短空间距离。即在保证电气性能稳定和安全的情况…

Redis真没那么难,这份大佬实战笔记也太可了,吹爆

Redis的技术全景 Redis一个开源的基于键值对&#xff08;Key-Value&#xff09;NoSQL数据库。使用ANSI C语言编写、支持网络、基于内存但支持持久化。性能优秀&#xff0c;并提供多种语言的API。 我们要首先理解一点&#xff0c;我们把Redis称为KV数据库&#xff0c;键值对数据…