第3章“程序的机器级表示”:异类的数据结构

news2024/11/24 0:27:34

文章目录

  • 概述
    • 3.9.1 结构
    • 3.9.2 联合

概述

C提供了两种将不同类型的对象结合到一起来创建数据类型的机制:结构(structure),用关键字 struct 来声明,将多个对象集合到一个单位中;联合(union),用关键字 union 来声明,允许用几种不同的类型来引用一个对象。

3.9.1 结构

C 的 struct 声明创建一个数据类型,将可能不同类型的对象聚合到一个对象中。结构的各个组成部分是用名字来引用。结构的实现类似于数组的实现,因为结构的所有组成部分都存放在存储器中连续的区域内,而指向结构的指针就是结构第一个字节的地址。 编译器保存关于每个结构类型的信息,指示每个域(field)的字节偏移。它以这些偏移作为存储器引用指令中的位移,从而产生对结构元素的引用。

将指向结构的指针从一个地方传递到另一个地方,而不是拷贝它们,是很常见的。

举个例子,看如下的结构声明:

struct rec {
	int i;
	int j;
	int a[3];
	int *p;
};

这个结构包括四个域——两个 4 字节 int、一个由三个 4 字节 int 组成的数组和一个 4 字节的整数指针,总共24个字节:

在这里插入图片描述

注意,数组 a 是嵌入到这个结构中的。上图中顶部的数字给出的是各个域相对结构开始处的字节偏移。

为了访问结构的域,编译器产生的代码要将结构的地址加上适当的偏移。例如,假设 struct rec * 类型的变量 r 放在寄存器 %edx 中。然后,下面的代码将元素 r->i 拷贝到元素 r->j

在这里插入图片描述
因为域 i i i 的偏移量为0,所以这个域的地址就是 r r r 的值。为了存储到域 j j j,代码要将 r r r 的地址加上偏移量 4。

要产生一个指向结构内部对象的指针,只需将结构的地址加上该域的偏移量。例如,只用加上偏移量8 + 4 x 1 = 12,就可以得到指针 &(r->a[1])。对于在寄存器 %eax 中的指针 r 和 在寄存器 %edx 中的整数变量 i i i,可以用一条指令产生指针 &(r->a[i]) 的值:

在这里插入图片描述
还有最后一个例子,下面的代码实现的是语句:

r->p = &r->a[r->i + r->j];

开始时 r r r 在寄存器 %edx 中:
在这里插入图片描述
正如这些示例表明的那样,对结构的各个域的选取完全是在编译时处理的。机器代码不包含关于域声明或域名字的信息。

3.9.2 联合

联合提供了一种方式,能够规避 C 的类型系统,允许以多种类型来引用一个对象。联合声明的语法与结构的语法一样,只不过语义相差比较大。它们不是用不同的域来引用不同的存储器块,而是引用的同一存储器块。

看如下的声明:

struct S3 {
	char c;
	int i[2];
	double v;
};

union U3 {
	char c;
	int i[2];
	double v;
};

域的偏移数据类型 S3 和 U3 的整个大小如下表所示:
在这里插入图片描述
(稍后会看到为什么 S3 中的 i i i 的偏移量为 4,而不是 1。)对于类型 union U3 * 的指针 p p pp->cp->i[0]p->v 引用的都是数据结构的起始位置。还要注意,一个联合的总的大小等于它最大域的大小。

在一些情况中,联合十分有用。但是,它也引起了一些讨厌的错误,因为它们绕过了 C 类型系统提供的安全措施。一种应用情况是,事先知道对一个数据结构中的两个不同域的使用是互斥的,那么将这两个域作为联合的一部分,而不是结构的一部分,会减小分配空间的总量。

如,假设想实现一个二叉树的数据结构,每个叶子节点都有一个 double 的数据值,而每个内部节点都有指向两个孩子节点的指针,但是没有数据。如果像这样声明:

struct Node {
	struct Node *left;
	struct Node *right;
	double data;
};

那么每个节点需要 16 个字节,每种类型的节点都要浪费一半的字节。相反,如果这样来声明一个节点:

union Node {
	struct {
		union Node *left;
		union Node *right;
	} internal;
	double data;
};

那么,每个节点就只需要 8 个字节。如果 n n n 是一个指针,指向 union Node * 类型的节点,用 n->data 来引用叶子节点的数据,而用 n->internal.leftn->internal.right 来引用内部节点的孩子。

不过,如果这样编码,就无法确定一个给定的节点到底是叶子节点还是内部节点。通常的方法是引入一个附加的标志域:

struct Node {
	int is_leaf;
	union {
		struct {
			struct Node *left;
			struct Node *right;
		} internal;
		double data;
	} info;
};

这里,对叶子节点来说,域 is_leaf 是 1,而对内部节点来说,该域的值是 0。这个结构总共需要 12 个字节:is_leaf 要 4 个,info.internal.leftinfo.internal.right 各要 4 个,或者 info.data 要 8 个。在这种情况中,相对于给代码造成的麻烦,使用联合带来的好处是很小的。对于有较多域的数据结构,这样的节省会更加吸引人一些。

联合还可以用来访问不同数据类型的位的形式。例如,下面这段代码返回一个 float 作为 unsigned 的位表示:

unsigned float2bit(float f)
{
	union {
		float f;
		unsigned u;
	} temp;
	temp.f = f;
	return temp.u;
}

在这段代码中,以一种数据类型来存储联合中的参数,又以另一种数据类型来访问它。有趣的是,为此过程产生的代码与为下面这个过程产生的代码是一样的:

unsigned copy(unsigned u)
{
	return u;
}

这两个过程的主体只有一条指令:
在这里插入图片描述
这就证明汇编代码中缺乏类型信息。无论参数是一个 float,还是一个 unsigned,它都在相对于 %ebp 偏移量为 8 的地方。过程只是简单地将它的参数拷贝到返回值,不修改任何位。

当用联合来将各种不同大小的数据类型结合到一起时,字节顺序问题就变得很重要了。如假设写了一个过程,它会以两个 4 字节的 unsigned 的位的形式,创建一个 8 字节的 double:

double bit2double(unsigned word0, unsigned word1)
{
	union {
		double d;
		unsigned u[2];
	} temp;

	temp.u[0] = word0;
	temp.u[1] = word1;
	return temp.d;
}

在像 IA32 这样的小端法(little-endian)机器上,参数 word0 会是 d d d 的低位四个字节,而 word1 会是高位四个字节。在大端法(big-endian)机器上,这两个参数的角色刚好相反。

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

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

相关文章

Java网络开发(Asynchronous)—— 从 Jsp 到 Ajax 的 axios 到 vue 同步请求 到 异步请求

目录 引出如果想做bilibili边看视频边评论怎么搞?Ajax是啥?& axios的语法1. Ajax(Asynchronous JavaScript And XML)简介2. axios语法 及其与 java后端交互(1)get请求(2)post请求…

SpringBoot之Spring Data JPA入门学习

JPA&#xff08;Java Persistence API&#xff09;Java持久化API&#xff0c;是 Java 持久化的标准规范&#xff0c;Hibernate是持久化规范的技术实现&#xff0c;而Spring Data JPA是在 Hibernate 基础上封装的一款框架。 一、添加依赖 <dependency><groupId>or…

【git指南--命令大全】

我看好多文章命令都记录得比较发散不够全面&#xff0c;这里还是把我自己的笔记分享出来&#xff0c;方便查阅。 文章目录 1. git config作用域--list 显示当前所有配置配置cd ~/.ssh 2. 初始化 git 仓库1、把已有项目代码纳入 git 管理2、新建的项目直接用git 管理3、克隆仓库…

智能指针 smart_ptr

智能指针 为什么需要智能指针&#xff1f;内存泄漏什么是内存泄漏&#xff0c;内存泄漏的危害内存泄漏分类 智能指针的使用及原理RAII智能指针的原理std::auto_ptrstd::unique_ptrstd::shared_ptr 为什么需要智能指针&#xff1f; 下面我们先分析一下下面这段程序有没有什么内…

ps打开出现dll文件丢失怎么办,dll修复的三个方法

在Windows中&#xff0c;动态链接库&#xff08;DLL&#xff09;是一种可重用的代码和数据结构&#xff0c;它由多个应用程序共享&#xff0c;以提高内存利用率并减少冗余。DLL文件通常包含操作系统函数、图形驱动程序、网络驱动程序等&#xff0c;它们在Windows启动时被加载到…

大话Stable-Diffusion-Webui-客制化主题(四)

文章目录 目标效果开始重要说明单选框以及复选框图标样式更改gradio主题构建器上传主题方式代码上传主题方式目标 在DIY的主题中更改gradio单选框组件以及复选框组件的勾选后图标样式 效果 开始 笔者在使用gradio的主题构建器的过程中发现,gradio的复选框以及单选框组件勾选…

高通 Camera HAL3:如何在CAMX中查找Android TAG

之前的博文&#xff1a;《高通 Camera HAL3&#xff1a;添加一个VendorTag》中提到过&#xff0c;MetadataTag在CamX中有两种体现&#xff0c;可以是预定义的AndroidTag或是自定义VendorTag。 自定义VendorTag博文中已经讲解了 如何在CAMX中查找AndroidTag是经常要做的事 下…

【2】Midjourney注册

随着AI技术的问世&#xff0c;2023年可以说是AI爆炸性成长的一年&#xff0c;近期最广为人知的AI服务除了chatgpt外&#xff0c;就是从去年五月就已经问世的AI绘画工具mid journey了。 ▲几个AI工具也代表了人工智能的热门阶段 只要输入一段文字&#xff0c;AI就会根据语意计算…

Android 逆向之安全防护基本策略

对抗反编译 混淆 使用混淆主要可以减小包的大小。混淆对于安全保护来说&#xff0c;只是增加了阅读难度而已。混淆不会把关键代码混淆掉&#xff0c;比如MainActivity&#xff0c;Application等&#xff0c;可以通过分析smali和阅读jar包定位代码。 资源混淆也是换汤不换药&…

【VPX612】基于C6678 的6U VPX 实时信号处理平台

产品概述 VPX612 是一款基于6U VPX 架构的高性能实时信号处理平台&#xff0c;该平台采用4 片TI 的KeyStone 系列多核DSP TMS320C6678 作为主处理单元&#xff0c;采用1 片Xilinx 的Kintex-7 系列FPGA 作为协处理单元&#xff0c;各个处理节点之间通过高速串行总线进行互联。板…

遇到这3种接口测试问题,其实,你可以这么办~

作为整个软件项目的必经环节&#xff0c;软件测试是不可缺少的“查漏补缺”环节。而作为软件测试中的重要一环——接口测试&#xff0c;几乎串联了整个项目所有的输入和输出环节。 前几年&#xff0c;我在做后端测试时&#xff0c;接触最多的正是接口测试。基于此&#xff0c;…

chatgpt赋能python:Python怎么倒序输出一百以内的整数

Python怎么倒序输出一百以内的整数 Python是一个广泛使用的高级编程语言&#xff0c;其简单易学、易读性强、具有良好的开发效率和可维护性等特点&#xff0c;使得Python成为了软件开发、数据分析和机器学习等领域的首选语言。本篇文章将介绍Python如何倒序输出一百以内的整数…

走进 WWDC 2023 苹果开发者大会

6 月 6 日凌晨开始,苹果在加州总部举行了 WWDC 2023 开发者大会的主题演讲,向全球观众展示了 iOS 17、iPadOS 17、tvOS 17、watchOS 10 和 macOS 14 这五大新系统,以及备受期待的混合现实头显 Apple Vision Pro 和 Mac Pro 等硬件。 以下是对发布会的主要内容和亮点的总结和…

电脑误删文件有多大几率能恢复回来

电脑误删文件是一种常见情况&#xff0c;但能否成功恢复取决于多种因素。本文将探讨电脑误删文件的数据恢复几率以及影响因素&#xff0c;帮助你了解恢复的可能性并采取适当的行动。 工具/原料&#xff1a; 系统版本&#xff1a;win10系统 品牌型号&#xff1a;华硕F83E66Se-S…

开源代码分享(2)—综合能源系统零碳优化调度(附matlab代码)

参考文献&#xff1a; Optimal dispatch of zero-carbon-emission micro Energy Internet integrated with non-supplementary fired compressed air energy storage system | SGEPRI Journals & Magazine | IEEE Xplore 1.引言 全球能源危机和环境污染的双重压力促使能量…

Vision Transformer综述 part I Transformer简介及组成

Vision Transformer综述 1. Transformer简介2. Transformer组成2.1 Self-AttentionMulti-Head Attention&#xff08;多头注意力&#xff09; 2.2 Transformer的其他关键概念2.2.1 Feed-Forward Network 前馈网络2.2.2 Residual Connection 残差连接2.2.3 解码器中的最后一层 1…

红蓝攻防基础-认识红蓝紫,初步学习网络安全属于那个队?

一、关于红队 红队&#xff0c;也叫蓝军是指网络实战攻防演练中的攻击一方&#xff0c;以发现系统薄弱环节、提升系统安全性为目标&#xff0c;一般会针对目标单位的从业人员以及目标系统所在网络内的软件、硬件设备执行多角度、全方位、对抗性的混合式模拟攻击&#xff0c;通过…

HTML中的 JavaScript 运行模式

导读&#xff1a; 在前面章节中&#xff0c;我们提到了&#xff0c;JavaScript在诞生初期&#xff0c;主要用途是代替Perl等服务器端语言处理输入验证,但如今 JavaScript,已经被广泛应用在了 WEB 开发领域&#xff0c;所以本章节就和大家聊聊&#xff0c;JavaScript&#xff0c…

HTML5-创建HTML文档

HTML5中的一个主要变化是&#xff1a;将元素的语义与元素对其内容呈现结果的影响分开。从原理上讲这合乎情理。HTML元素负责文档内容的结构和含义&#xff0c;内容的呈现则由应用于元素上的CSS样式控制。下面介绍最基础的HTML元素&#xff1a;文档元素和元数据元素。 一、构建…

leetcode 85. 最大矩形

题目链接&#xff1a;leetcode 85 1.题目 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵&#xff0c;找出只包含 1 的最大矩形&#xff0c;并返回其面积。 2.示例 1&#xff09;示例 1&#xff1a; 输入&#xff1a;matrix [[“1”,“0”,“1”,“0”,“…