概述
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 = %pn",
&v, &v.intValue, v.bytes);
printf("v.bytes[0..3] = 0x%x 0x%x 0x%x 0x%xn",
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%xn",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占据了地址为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) = %dn", sizeof(union UMore));
union UMore v = {33.22}; //未指定初始化成员时默认赋值给第0个成员dValue
printf("v.dValue = %fn",v.dValue);
printf("&v.dValue = %p, &v.cValue = %p, &v.iValue = %pn",
&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编程基础及应用实验教程
最后
以上就是美好康乃馨为你收集整理的C语言,从联合看字节序的全部内容,希望文章能够帮你解决C语言,从联合看字节序所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复