C语言KR圣经笔记 6.8联合体 6.9位域

6.8 联合体(union)

联合体是一个可以(在不同时间)保存不同类型和大小的对象的变量,由编译器来跟踪大小和对齐要求。联合体提供了一种不用在程序中嵌入任何与机器相关的信息,而能够在单个存储区域内操作不同类型数据的方式。它们类似于Pascal中的变体记录(variant record)。

以编译器符号表管理器中可能找到的代码为例,我们假定一个常量可能是 int,float 或字符指针。某个特定常量的值必须储存在正确类型的变量中,然而,如果不管该常量的值是什么类型,它都占有相同大小的内存而且保存在同一个位置,那么对表管理而言是最方便的。这就是联合体的目的——单个变量能够合法地保存几种类型中的任意一种。联合体的语法以结构体的为基础:

union u_tag {int ival;float fval;char *sval;
} u;

变量 u 足够大,能容纳这三种类型中最大的;不过具体的大小依赖于实现。这些类型中不管哪一种都能被赋给 u ,然后用在表达式里,但注意使用方式必须是一致的:即取出的类型必须是最近存入的类型。跟踪当前在联合体中保存的是哪种类型,属于程序员的责任【前面也说了,编译只负责内存大小和对齐要求】;如果按一种类型保存,又按另一种类型来取,则结果是由实现定义的。

在语法上,联合体的成员通过如下两种方式来访问

联合体名称 . 成员

联合体指针  -> 成员

和结构体一样。

如果用变量 utype 来跟踪 u 中当前储存的类型,我们能看到的代码类似于

if (utype == INT)printf("%d\n", u.ival);
else if (utype == FLOAT)printf("%f\n", u.fval);
else if (utype == STRING)printf("%s\n", u.sval);
elseprintf("bad type %d in utype\n", utype)

联合体可以出现在结构体和数组中,反之亦然。访问结构体中联合体的成员(或联合体中的结构体成员)的表示法,与访问嵌套结构体是一样的。例如,结构体数组定义如下

struct {char *name,int flags,int utypes,union {int ival;float fval;char *sval;} u;
} systab[NSYM];

则成员 ival 通过如下方式引用:

systab[i].u.ival

字符串 sval 的第一个字符可用如下两种方法引用:

*systab[i].u.sval;
systab[i].u.sval[0];

实际上,联合体就是一个结构体,其所有成员都是从其内存起始地址的偏移量零开始,而且结构体足够大以容纳“最宽”的成员,另外对齐方式也适用于联合体中所有成员。在结构体上能做的操作,在联合体上同样也能做:即作为一个整体单元被赋值或拷贝,以及获取地址,还有访问其成员。

联合体只能被它首个成员类型的值初始化;因此上面描述的联合体 u 只能初始化为整数值。

第八章的存储分配器展示了联合体如何被用来强制让一个变量在特定类型的存储边界对齐。

6.9 位域 (bit-field)

当存储空间非常稀缺时,可能有必要将多个对象打包到单个机器字中;一种常见的做法是使用一组由单个比特构成的标志位,像编译器符号表就是如此。对外暴露的数据格式,例如硬件设备的接口,也经常需要访问一个机器字中部分比特位的能力。

想象一下编译器中操作符号表的那部分代码片段。程序中的每个标识符都有与之关联的信息,例如是否关键字,是否外部或/且静态,等等。对这些信息进行编码的最紧凑方式,是在单个 char 或 int 中保存一组单比特标志位。

通常的做法是定义一组对应到相应比特位的“掩码”,例如

#define KEYWORD  01
#define EXTERNAL 02
#define STATIC   04

或者是

enum { KEYWORD = 01, EXTERNAL = 02, STATIC = 04 }

这些数必须是2的幂。然后对这些比特位的访问就成了一种“位的摆弄”(bit-fiddling),用的是我们在第二章描述的位移、掩码和补码操作。

某些用法非常频繁,如

flags |= EXTERNAL | STATIC;

把 flags 中的 EXTERNAL 和 STATIC 位打开。而

flags ~= ~(EXTERNAL | STATIC);

则把它们关闭。另外下面的判断

if ((flags & (EXTERNAL | STATIC)) == 0) ...

当这两个位都关闭时为真。

尽管这些用法很容易掌握,但作为替代方案,C 语言还提供了一种不通过位逻辑操作符,而是直接定义和操作机器字内的域的能力。位域,或者简称域,是在单个由实现定义的存储单元(我们称之为“机器字”)中,一系列相邻的比特位。域的定义和访问的语法基于结构体的语法。例如,上面符号表的 #define 语句可以由下列三个域的定义来替代:

struct {unsigned int is_keyword : 1;unsigned int is_extern : 1;unsigned int is_static : 1;
} flags;

这定义了一个名为 flags 的变量,它包含三个1比特的位域。冒号后面的数字代表了位域的宽度,单位为比特。这些域都声明为 unsigned int 以保证它们都是无符号的值。

各位域以结构体成员的同样方式访问:flags.is_keyword、flags.is_extern 等等。位域的行为像是小整数,而且可以和其他整数一样参与到算术运算表达式中,因此前面的例子可以很自然地写成:

 flags.is_extern = flags.is_static = 1;

上面会将对应的比特位打开。

flags.is_extern = flags.is_static = 0;

上面会将比特位关闭。

if (flags.is_extern == 0 && flags.is_static == 0)

上面对比特位进行检查。

位域的绝大部分都依赖于实现。一个位域能否跨越机器字的边界,这是由实现定义的。位域甚至不必有名称;未命名位域(只有冒号和位域宽度)用于填充。而特殊的位域宽度 0 可用来在下一个机器字边界处强制对齐。

在某些计算机上位域从左到右分配,而其他机器上是从右到左分配。这意味着,尽管字段对于维护 内部定义的数据结构很有用,但是,在获取外部定义的数据时,必须仔细考虑哪个端在前的问题【大字节序或小字节序】;依赖这些的程序是不可移植的。位域只能声明为 int;为了可移植性,要显式指定 signed 或 unsigned。位域不是数组,也没有地址,因此不能对其使用 & 操作符。

(第六章完)

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

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

相关文章

时间序列预测——GRU模型

时间序列预测——GRU模型 在深度学习领域,循环神经网络(RNN)是处理时间序列数据的一种常见选择。上期已介绍了LSTM的单步和多步预测。本文将深入介绍一种LSTM变体——门控循环单元(GRU)模型,包括其理论基础…

Flutter canvas 画一条会动的波浪线 进度条

之前用 Flutter Canvas 画过一个三角三角形,html 的 Canvas 也画过一次类似的, 今天用 Flutter Canvas 试了下 感觉差不多: html 版本 大致效果如下: 思路和 html 实现的类似: 也就是找出点的位置,使用二阶…

DAY37:贪心算法738

今天写了一道题目,顺便看了一个很好的总结,这篇博客可以跳过。 Leetcode:738 单调递增的数字 因为最大的数字是9,当出现后面位数的数字比前面位数的数字小的时候,就把后面的数字都变成9,前面那个数字--。…

网安面试指南——(渗透,攻击,防御)

网安面试 目录 1.什么是 WebShell? 2.什么是网络钓鱼? 3.你获取网络安全知识途径有哪些? 4.什么是 CC 攻击? 5.Web 服务器被入侵后,怎样进行排查? 6.dll 文件是什么意思,有什么用?…

Cannot assign to read only property ‘exports‘ of object ‘#<Object>‘

看下多语言js文件中,是否同级出现相同名称。

3D 转换

1,3D的特点: 近小远大 物体后面遮挡不可见 2,3D移动 translate3d 3D移动在2D移动的基础上多加了一个可以移动的方向,就是z轴方向 transform:translateX(100px):仅仅是在x轴上移动…

mysql8.0主从搭建

一、架构说明 MySQL 主从架构是一种常见的数据库部署方案,用于实现数据的自动同步和冗余备份。在该架构中,主服务器负责处理事务的写入操作,从服务器则负责复制主服务器上的数据,并可以用于读取操作,以减轻主服务器的…

寒假刷题第19天

PTA甲级 1134 Vertex Cover #include<iostream> #include<vector> #include<set>using namespace std;typedef pair<int , int> PII; vector<PII>v; int n , m;bool check(set<int>&se) {for(auto i : v)if(!se.count(i.first) &…

【C/C++ 07】词频统计

一、题目 读入一篇英文短文&#xff0c;去除介词、连词、冠词、副词、代词等非关键性单词后&#xff0c;统计每个单词出现的次数&#xff0c;并将单词按出现次数的降序和单词字符的升序进行显示5个单词。 二、算法 1. 通过<fstream>库创建fstream流对象&#xff0c;并从…

uniapp【组件封装】时间戳格式化为星期

组件 components/dos-week.vue <template><text>{{week}}</text> </template> <script>export default {props: {time: String},mounted(e) {this.week this.getWeek(Number(this.time))},data() {return {week: }},methods: {// 通过时间戳计…

美区或其他外区Appstore账号AppleID注册教程,简单快速,苹果必备!

▍前言 现在越来越多的APP在国区APPstore下架&#xff0c;如果想有更好的使用体验&#xff0c;不得不去外区下载APP&#xff0c;那就需要一个外区的apple id&#xff0c;注册也很简单&#xff0c;今天大鹏通过电脑ipad给大家注册一个&#xff0c;建议大家直接使用iPhone或者iPa…

python魔法函数[全面]

1、init 用于初始化对象的属性和状态 当创建一个对象时&#xff0c;Python会自动调用该对象的__init__方法。 这个方法用于初始化对象的属性和状态&#xff0c;是对象创建过程中的一个重要环节 2、new # 通常我们不需要重写__new__方法&#xff0c;除非我们正在进行一些非常…

好书推荐丨保姆级Midjourney教程,这本写给大家看的设计书闭眼入!

文章目录 写在前面好书推荐Part.1Part.2Part.3 粉丝福利写在后面 写在前面 在AI绘画界&#xff0c;有每日经典一问&#xff1a;“你今天用Midjourney画了啥&#xff1f;”晒作品成为重头戏。 小红书上关于Midjourney出的图片点赞数惊人。 reddit上的恶搞幽默图片热度居高不下…

GSM-TRIAL-21.04.9-VMware-Workstation.OVA安装教程,GreenBone虚拟机安装教程

将GSM-TRIAL-21.04.9-VMware-Workstation.ova用VMware打开 先设置好网络和内存&#xff1a; 1、打开虚拟机&#xff0c;显示&#xff1a;你的GSM还不能完全正常工作。您想现在完成设置吗? 点击yes 2、创建用户&#xff0c;一会儿登录网页要用&#xff0c;点击yes 3、创建用户…

指向 Data Member 的指针

看一下很简单的一个例子&#xff1a; #include <stdlib.h> #include <stdio.h> #include <malloc.h>class origin { public:virtual ~origin(){} public:int x; };int main() {origin A;printf("&origin::x %p, &A.x %p\n", &origi…

OpenGL查询对象 Query Objects

查询对象和异步查询(Query Objects and Asynchronous Queries) Query Objects&#xff08;查询对象&#xff09;是OpenGL中的一种机制&#xff0c;用于获取有关一系列GL命令处理过程的信息。这些信息可以包括&#xff1a; 绘图命令处理的图元数量。写入变换反馈缓冲区的图元数…

小黄鸭聊电脑(4)硬盘分区

小黄鸭聊电脑(4)硬盘分区 夜深人静&#xff0c;万籁俱寂&#xff0c;老郭趴在电脑桌上打盹&#xff0c;桌子上的小黄鸭和桌子旁的冰箱又开始窃窃私语…… 小黄鸭&#xff1a;冰箱大哥&#xff0c;上次你说的那个“分区”和“格式化”是什么意思&#xff1f; 冰箱&#xff1a;…

洛夫克拉夫特与文学中的超自然恐怖:前哥特时代

洛夫克拉夫特与文学中的超自然恐怖&#xff1a;前哥特时代 ![ 洛夫克拉夫特是美国恐怖、科幻与奇幻小说作家&#xff0c;尤以其怪奇小说著称&#xff0c;他在自己的一系列小说中开发出了克苏鲁神话体系。他的创作对后世恐怖小说创造影响深远&#xff0c;我们可以在许多当代文…

[leetcode] 21. 合并两个有序链表

文章目录 题目描述解题方法双指针遍历java代码 题目描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff…

自动驾驶:Apollo如何塑造人类的未来出行

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言1. 什么是自定义指令&#xff1f;2. Apollo中的自定义指令2.1 查询中的自定…