一起来理解计算机系统系列文章目录

1. 大致简介
程序结构篇
1. 信息表示与处理
2. 程序的机器级表示
程序运行篇

程序交互篇

文章目录

  • 一起来理解计算机系统系列文章目录
  • 一、前言
  • 二、本文参考资料
  • 三、正文部分
    • 3.1 目的 / 目标
    • 3.2 程序编码
      • 3.2.1 机器级代码
      • 3.2.2 代码示例
      • 3.2.3 关于格式的注释
    • 3.3 数据格式
    • 3.4 访问信息
      • 3.4.1 操作数指示符
      • 3.4.2 数据传送指令
      • 3.4.3 数据传送实例
      • 3.4.4 压入和弹出栈数据
    • 3.5 算术和逻辑操作
      • 3.5.1 加载有效地址
      • 3.5.2 一元和二元操作
      • 3.5.3 移位操作
      • 3.5.4 特殊算术操作
    • 3.6 控制
      • 3.6.1 条件码
      • 3.6.2 访问条件码
      • 3.6.3 跳转指令
      • 3.6.4 跳转指令的编码
      • 3.6.5 用条件控制来实现条件分支
      • 3.6.6 用条件传送来实现条件分支
      • 3.6.7 循环
        • 3.6.7.1 do-while 循环
        • 3.6.7.2 while循环
        • 3.6.7.3 for循环
      • 3.6.8 switch语句
    • 3.7 过程
      • 3.7.1 运行时栈
      • 3.7.2 转移控制
      • 3.7.3 数据传送
      • 3.7.4 栈上的局部存储
      • 3.7.5 寄存器中的局部存储空间
      • 3.7.6 递归过程
    • 3.8 数组分配和访问
      • 3.8.1 基本原则
      • 3.8.2 指针运算
      • 3.8.3 嵌套的数组
      • 3.8.4 定长数组
      • 3.8.5 变长数组
    • 3.9 异质的数据结构
      • 3.9.1 结构
      • 3.9.2 联合
      • 3.9.3 数据对齐
    • 3.10 在机器级程序中将控制与数据结合起来
      • 3.10.1 理解指针
      • 3.10.2 GDB
      • 3.10.3 内存越界引用和缓冲区溢出
      • 3.10.4 对抗缓冲区溢出攻击
      • 3.10.5 支持变长栈帧


一、前言

本节主要介绍下计算机系统中的信息是以什么样的方式表示的,主要涉及无符号数、补码、以及浮点数。


二、本文参考资料

《深入理解计算机系统》


三、正文部分

3.1 目的 / 目标

计算机执行机器代码汇编代码是机器代码的文本表示
GCC C语言编译器以汇编代码的形式产生输出,汇编代码是机器代码的文本表示,给出程序中的每一条指令。然后GCC 调用汇编器和链接器,根据汇编代码生成可执行的机器代码。

用高级语言编写的程序可以在很多不同的机器上编译和执行,而汇编代码则是与特定机器密切相关的。

学习机器语言的目的是为了理解编译器的优化能力,并分析代码中隐含的低效率。
程序员学习汇编代码的需求随着时间的推移也发生了变化,开始时要求程序员能直接用汇编语言编写程序,现在则要求他们能够阅读和理解编译器产生的代码。

与机器代码的二进制格式相比,汇编代码的主要特点是它用可读性更好的文本格式表示
能够理解汇编代码以及它与原始C代码的联系,是理解计算机如何执行程序的关键一步。

主要学习了解典型的编译器在将C程序结构变换成机器代码时所做的转换。
通过研究系统和逆向工作,来试图了解系统的创建过程。

在这里,系统是一个机器产生的汇编语言程序。

精通细节是理解更深和更基本概念的先决条件。

3.2 程序编码

GCC是Linux上默认的编译器,我们也可以简单地用cc来启动它。

  • 优化等级
    编译选项-Og告诉编译器使用会生成符合原始C代码整体结构的机器代码的优化等级。
    使用较高级别优化产生的代码会严重变形,以至于产生的机器代码和初始源代码之间的关系非常难以理解。
    因此我们会使用-Og优化作为学习工具,然后当我们增加优化级别时,再看会发生什么。
    实际中,从得到的程序的性能考虑,较高级别的优化(例如,以选项-O1或-O2指定)被认为是较好的选择。
  • 编译过程
    实际上gcc命令调用了一整套的程序,将源代码转化成可执行代码。
    1. C预处理器扩展源代码,插入所有用#include命令指定的文件,并扩展所有用#define声明指定的宏。
    2. 编译器产生两个源文件的汇编代码,名字分别为pl.s和p2.s。
    3. 汇编器会将汇编代码转化成二进制目标代码文件p1.o和p2.o。目标代码是机器代码的一种形式,它包含所有指令的二进制表示,但是还没有填入全局值的地址。
    4. 链接器将两个目标代码文件与实现库函数(例如printf)的代码合并,并产生最终的可执行代码文件p(由命令行指示符-op指定的)。

3.2.1 机器级代码

对于机器级编程来说, 其中两种抽象尤为重要。

  1. 由指令集体系结构或指令集架构(Instruction Set Architecture, ISA)
    定义了机器级程序的格式和行为,它定义了处理器状态、指令的格式,以及每条指令对状态的影响 。
  2. 机器级程序使用的内存地址是虚拟地址
    提供的内存模型看上去是一个非常大的字节数组。
    存储器系统的实际实现是将多个硬件存储器和操作系统软件组合起来。
  • 常见处理器状态

    • 程序计数器(通常称为"PC",在 x86-64 中用%rip表示)给出将要执行的下一条指令在内存中的地址。
    • 整数寄存器文件包含16个命名的位置,分别存储 64 位的值。
      这些寄存器可以存储地址(对应于C语言的指针)或整数数据。
      有的寄存器被用来记录某些重要的程序状态,而其他的寄存器用来保存临时数据,例如过程的参数和局部变量,以及函数的返回值。
    • 条件码寄存器保存着最近执行的算术或逻辑指令的状态信息
      它们用来实现控制或数据流中的条件变化 ,比如说用来实现 if 和 while 语句。
    • 一组向量寄存器可以存放一个或多个整数或浮点数值。
  • 程序内存
    机器代码只是简单地将内存看成一个很大的、按字节寻址的数组。
    汇编代码也不区分有符号或无符号整数,不区分各种类型的指针,甚至于不区分指针和整数。

    程序内存包含:程序的可执行机器代码,操作系统需要的一些信息,用来管理过程调用和返回的运行时栈,以及用户分配的内存块 (比如说用 malloc 库函数分配的)。

    操作系统负责管理虚拟地址空间,将虚拟地址翻译成实际处理器内存中的物理地址。
    一条机器指令只执行一个非常基本的操作。例如,将存放在寄存器中的两个数字相加,在存储器和寄存器之间传送数据,或是条件分支转移到新的指令地址。
    编译器必须产生这些指令的序列,从而实现(像算术表达式求值、循环或过程调用和返回这样的)程序结构。

3.2.2 代码示例

  • 编译

    	long mult2(long, long) ;void multstore (long x, long y, long *dest){long t = mult2 (x, y) ;*dest = t ;

    在命令行上使用“-s"选项,就能看到C语言编译器产生的汇编代码:

    	multstore:pushq %rbxmovq %rdx, %rbxcall mult2movq %rax, (%rbx)popq %rbxret
    

    这段代码中已经除去了所有关于局部变星名或数据类型的信息。

  • 汇编
    如果我们使用”-c"命令行选项,GCC会编译并汇编该代码
    这就会产生目标代码文件mstore.o,它是二进制格式的,所以无法直接查看。
    1368字节的文件mstore.o中有一段14字节的序列,它的十六进制表示为:

    53 48 89 d3 e8 00 00 00 00 48 89 03 5b c3
    

    这就是上面列出的汇编指令对应的目标代码。
    从中得到一个重要信息,即机器执行的程序只是一个字节序列,它是对一系列指令的编码。机器对产生这些指令的源代码几乎一无所知。

  • 反汇编
    在这里插入图片描述
    其中一些关于机器代码和它的反汇编表示的特性值得注意:

    1. x86-64的指令长度从1到15个字节不等。常用的指令以及操作数较少的指令所需的字节数少,而那些不太常用或操作数较多的指令所需字节数较多。
    2. 设计指令格式的方式是,从某个给定位置开始,可以将字节唯一地解码成机器指令。例如,只有指令pushq%rbx是以字节值53开头的。
    3. 反汇编器只是基于机器代码文件中的字节序列来确定汇编代码。它不需要访问该程序的源代码或汇编代码。
    4. 反汇编器使用的指令命名规则与GCC生成的汇编代码使用的有些细微的差别。在我们的示例中,它省略了很多指令结尾的`q’。这些后缀是大小指示符,在大多数 情况中可以省略。相反,反汇编器给call和ret指令添加了'矿后缀,同样,省略这些后缀也没有问题

若将一份代码汇编后,再次反汇编,最后将二者进行对比
在这里插入图片描述
这段代码与mstore.c反汇编产生的代码几乎完全一样。
- 其中一个主要的区别是左边列出的地址不同——链接器将这段代码的地址移到了一段不同的地址范围中。
- 第二个不同之处在于链接器填上了callq指令调用函数mult2需要使用的地址(反汇编代码第4行)。
链接器的任务之一就是为函数调用找到匹配的函数的可执行代码的位置。
- 最后一个区别是多了两行代码(第8和9行)。这两条指令对程序没有影响,因为它们出现在返回指令后面(第7行)。插入这些指令是为了使函数代码变为16字节,使得就存储器系统性能而言,能更好地放置下一个代码块。

3.2.3 关于格式的注释

在这里插入图片描述
所有以‘·’开头的行都是指导汇编器和链接器工作的伪指令。我们通常可以忽略这些行。
另一方面,也没有关于指令的用途以及它们与源代码之间关系的解释说明。
为了更清楚地说明汇编代码,我们用这样一种格式来表示汇编代码,它省略了大部分伪指令,但包括行号和解释性说明。
在这里插入图片描述
对于一些应用程序,程序员必须用汇编代码来访
问机器的低级特性。一种方法是用汇编代码编写整个函数,在链接阶段把它们和C函数组
合起来。另一种方法是利用GCC的支持,直接在C程序中嵌入汇编代码。

3.3 数据格式

在这里插入图片描述
大多数GCC生成的汇编代码指令都有一个字符的后缀,表明操作数的大小。
例如,数据传送指令有四个变种:movb(传送字节)、movw(传送字)、movl(传送双字)和movq(传送四字)。
后缀 ‘l’ 用来表示双字,因为32位数被看成是“长字(long word)”。
注意,汇编代码也使用后缀 ''l 来表示4字节整数和8字节双精度浮点数。
这不会产生歧义,因为浮点数使用的是一组完全不同的指令和寄存器。

3.4 访问信息

一个x86-64的中央处理单元(CPU)包含一组16个存储64位值的通用目的寄存器。这些寄存器用来存储整数数据和指针。
在这里插入图片描述
如图3-2中嵌套的方框标明的,指令可以对这16个寄存器的低位字节中存放的不同大小的数据进行操作。
字节级操作可以访问最低的字节,16位操作可以访问最低的2个字节,32位操作可以访问最低的4个字节,而64位操作可以访问整个寄存器。
在后面的章节中,我们会展现很多指令,复制和生成1字节、2字节、4字节和8字节值。
当这些指令以寄存器作为目标时,对于生成小于8字节结果的指令,寄存器中剩下的字节会怎么样,对此有两条规则

  1. 生成1字节和2字节数字的指令会保持剩下的字节不变;
  2. 生成4字节数字的指令会把高位4个字节置为0。(后面这条规则是作为从IA32到x86-64的扩展的一部分而采用的。)

就像图3-2右边的解释说明的那样,在常见的程序里不同的寄存器扮演不同的角色。
其中最特别的是栈指针%rsp,用来指明运行时栈的结束位置。有些程序会明确地读写这个寄存器。
另外15个寄存器的用法更灵活。少量指令会使用某些特定的寄存器。
更重要的是,有一组标准的编程规范控制着如何使用寄存器来管理栈、传递函数参数、从函数的返回值,以及存储局部和临时数据。

3.4.1 操作数指示符

大多数指令有一个或多个操作数(operand),指示出执行一个操作中要使用的源数据值,以及放置结果的目的位置。
x86-64支持多种操作数格式(参见图3-3)。源数据值可以以常数形式给出,或是从寄存器或内存中读出。结果可以存放在寄存器或内存中。因此,各种不同的操作数的可能性被分为三种类型。

  • 第一种类型是立即数(immediate),用来表示常数值。
    在ATT格式的汇编代码中,立即数的书写方式是 ‘$’ 后面跟一个用标准C表示法表示的整数,比如, $-577或$0x1F。
    不同的指令允许的立即数值范围不同,汇编器会自动选择最紧凑的方式进行数值编码。

  • 第二种类型是寄存器(register),它表示某个寄存器的内容,16个寄存器的低位1字节、2字节、4字节或8字节中的一个作为操作数,这些字节数分别对应于8位、16位、32位或64位。
    在图3-3中,我们用符号ra来表示任意寄存器a,用引用R[ra]来表示它的值,这是将寄存器集合看成一个数组R,用寄存器标识符作为索引。

  • 第三类操作数是内存引用,它会根据计算出来的地址(通常称为有效地址)访问某个内存位置。
    因为将内存看成一个很大的字节数组,我们用符号Mb[Addr]表示对存储在内存中从地址Addr开始的b个字节值的引用。为了简便,我们通常省去下标b。
    如图3-3所示,有多种不同的寻址模式,允许不同形式的内存引用。表中底部用语法Imm(rb, ri, s)表示的是最常用的形式。
    这样的引用有四个组成部分:一个立即数偏移Imm,一个基址寄存器rb,一个变址寄存器ri,和一个比例因子s,这里s必须是1、2、4或者8。
    基址和变址寄存器都必须是64位寄存器。
    有效地址被计算为Imm + R[rb] + R[ri] • s。
    引用数组元素时,会用到这种通用形式。
    其他形式都是这种通用形式的特殊情况,只是省略了某些部分。
    正如我们将看到的,当引用数组和结构元素时,比较复杂的寻址模式是很有用的。
    在这里插入图片描述

3.4.2 数据传送指令

最频繁使用的指令是将数据从一个位置复制到另一个位置的指令。
操作数表示的通用性使得一条简单的数据传送指令能够完成在许多机器中要好几条不同指令才能完成的功能。
我们会介绍多种不同的数据传送指令,它们或者源和目的类型不同,或者执行的转换不同,或者具有的一些副作用不同。
在我们的讲述中,把许多不同的指令划分成指令类,每一类中的指令执行相同的操作,只不过操作数大小不同

图3-4列出的是最简单形式的数据传送指令-MOV类。这些指令把数据从源位置复制到目的位置,不做任何变化。
MOV类由四条指令组成:movb、movw、movl和movq。
这些指令都执行同样的操作;主要区别在于它们操作的数据大小不同:分别是1、2、4和8字节。
在这里插入图片描述
源操作数指定的值是一个立即数,存储在寄存器中或者内存中。
目的操作数指定一个位置,要么是一个寄存器或者,要么是一个内存地址。
将一个值从一个内存位置复制到另一个内存位置需要两条指令
–> 第一条指令将源值加载到寄存器中
–> 第二条将该寄存器值写入目的位置。

大多数情况中,MOV指令只会更新目的操作数指定的那些寄存器字节或内存位置。
唯一的例外是movl指令以寄存器作为目的时,它会把该寄存器的高位4字节设置为0。

造成这个例外的原因是x86-64采用的惯例,即任何为寄存器生成32位值的指令都会把该寄存器的高位部分置成0。

第一个是源操作数,第二个是目的操作数
在这里插入图片描述

在将较小的源值复制到较大的目的时使用。所有这些指令都把数据从源(在寄存器或内存中)复制到目的寄存器。
MOVZ类中的指令把目的中剩余的字节填充为0,而MOVS类中的指令通过符号扩展来填充,把源操作的最高位进行复制。
在这里插入图片描述

剩下的指令的源操作数值是立即数值-1。
回想-1的十六进制表示形如FF…F,这里F的数量是表述中字节数量的两倍。
因此movb指令(第2行)把%rax的低位字节设置为FF,而movw指令(第3行)把低2位字节设置为FFFF,剩下的字节保持不变。
movl指令(第4行)将低4个字节设置为FFFFFFFF,同时把高位4字节设置为00000000。
最后movq指令(第5行)把整个寄存器设置为FFFFFFFFFFFFFFFF。

在这里插入图片描述
movb指令(笫3行)不改变其他字节。根据 源字节 的最高位,movsbq指令(第4行)将其他7个字节设为全1或全0。由于十六进制A表示二进制值1010,符号扩展会把高位字节都设置为FF。movzbq指令(笫5行)总是将其他7个字节全都设置为0
在这里插入图片描述

3.4.3 数据传送实例

函数通过把值存储在寄存器%rax或该寄存器的某个低位部分中返回

首先,我们看到C语言中所谓的”指针”其实就是地址。间接引用指针就是将该指针放在一个寄存器中,然后在内存引用中使用这个寄存
器。其次,像x这样的局部变量通常是保存在寄存器中,而不是内存中。访问寄存器比访问内存要快得多。
在这里插入图片描述

3.4.4 压入和弹出栈数据

通过push操作把数据压入栈中,通过pop操作删除数据;它具有一个属性:弹出的值永远是最近被压入而且仍然在栈中的值。
栈可以实现为一个数组,总是从数组的一端插入和删除元素。这一端被称为栈顶。在x86-64中,程序栈存放在内存中某个区域。
如图3-9所示,栈向下增长,这样一来,栈顶元素的地址是所有栈中元素地址中最低的。(根据惯例,我们的栈是倒过来画的,栈“顶”在图的底部。)栈指针%rsp保存着栈顶元素的地址。
在这里插入图片描述
pushq指令的功能是把数据压入到栈上,而popq指令是弹出数据。这些指令都只有
一个操作数一一压入的数据源和弹出的数据目的。
将一个四字值压人栈中,首先要将栈指针减8,然后将值写到新的栈顶地址。
因此,指令 pushq %rbp 的行为等价于下面两条指令:

subq $8, %rsp
movq %rbp, (%rsp)

它们之间的区别是在机器代码中pushq指令编码为1个字节,而上面那两条指令一共需要8个字节。

在这里插入图片描述

因为栈和程序代码以及其他形式的程序数据都是放在同一内存中,所以程序可以用标准的内存寻址方法访问栈内的任意位置。

3.5 算术和逻辑操作

3.5.1 加载有效地址

加载有效地址指令leaq实际上是movq指令的变形。
它的指令形式是从内存读数据到寄存器,但实际上它根本就没有引用内存。它的第一个操作数是将有效地址写入到目的操作数。
在图3-10中我们用C语言的地址操作符&S说明这种计算。这条指令可以为后面的内存引用产生指针。
另外,它还可以简洁地描述普通的算术操作。例如,如果寄存器%rdx的值为x,那么指令leaq7(%rdx, %rdx, 4),%rax将设置寄存器%rax的值为5x+7。
编译器经常发现leaq的一些灵活用法,根本就与有效地址计算无关。目的操作数必须是一个寄存器。
leaq指令能执行加法和有限形式的乘法,在编译如上简单的算术表达式时,是很有用处的。
在这里插入图片描述

3.5.2 一元和二元操作

  • 一元操作
    只有一个操作数,既是源又是目的。
    这个操作数可以是一个寄存器,也可以是一个内存位置。比如说,指令incq(%rsp)会使栈顶的8字节元素加1。

  • 二元操作
    其中,第二个操作数既是源又是目的。
    要注意,源操作数是第一个,目的操作数是第二个,对千不可交换操作来说,这看上去很奇特。
    例如,指令subq %rax,%rdx使寄存器%rdx的值减去%rax中的值。(将指令解读成“从%rdx中减去%rax"会有所帮助。)
    第一个操作数可以是立即数、寄存器或是内存位置。第二个操作数可以是寄存器或是内存位置。
    注意,当第二个操作数为内存地址时,处理器必须从内存读出值,执行操作,再把结果写回内存。

在这里插入图片描述

3.5.3 移位操作

先给出移位量,然后第二项给出的是要移位的数。可以进行算术和逻辑右移。
移位量可以是一个立即数,或者放在单字节寄存器%cl中。

3.5.4 特殊算术操作

在这里插入图片描述

3.6 控制

机器代码提供两种基本的低级机制来实现有条件的行为:测试数据值,然后根据测试的结果来改变控制流或者数据流。
通常,C语言中的语句和机器代码中的指令都是按照它们在程序中出现的次序,顺序执行的。
用jump指令可以改变一组机器代码指令的执行顺序,jump指令指定控制应该被传递到程序的某个其他部分,可能是依赖于某个测试的结果。

3.6.1 条件码

除了整数寄存器,CPU还维护着一组单个位的条件码(condition code)寄存器,它们描述了最近的算术或逻辑操作的属性。
可以检测这些寄存器来执行条件分支指令。
最常用的条件码有:
CF:进位标志。最近的操作使最高位产生了进位。可用来检查无符号操作的溢出。
ZF:零标志。最近的操作得出的结果为0。
SF:符号标志。最近的操作得到的结果为负数。
OF:溢出标志。最近的操作导致一个补码溢出正溢出或负溢出。
在这里插入图片描述
leaq指令不改变任何条件码,因为它是用来进行地址计算的。除此之外,图3-10中列出的所有指令都会设置条件码。
对千逻辑操作,例如XOR,进位标志和溢出标志会设置成0。
对于移位操作,进位标志将设置为最后一个被移出的位,而溢出标志设置为0。
INC和DEC指令会设置溢出和零标志,但是不会改变进位标志

在这里插入图片描述
还有两类指令(有8、16、32和64位形式),它们只设置条件码而不改变任何其他寄存器;如图3-13所示。

  • CMP指令根据两个操作数之差来设置条件码。
    除了只设置条件码而不更新目的寄存器之外,CMP指令与SUB指令的行为是一样的。
    在ATT格式中,列出操作数的顺序是相反的,这使代码有点难读。如果两个操作数相等,这些指令会将零标志设置为1,而其他的标志可以用来确定两个操作数之间的大小关系。
  • TEST指令的行为与AND指令一样,除了它们只设置条件码而不改变目的寄存器的值。
    典型的用法是,两个操作数是一样的(例如,testq %rax, %rax用来检查%rax是负数、零,还是正数),或其中的一个操作数是一个掩码,用来指示哪些位应该被测试。

3.6.2 访问条件码

条件码通常不会直接读取,常用的使用方法有三种:
l)可以根据条件码的某种组合,将一个字节设置为0或者1,我们将这一整类指令称为SET指令;它们之间的区别就在于它们考虑的条件码的组合是什么,这些指令名字的不同后缀指明了它们所考虑的条件码的组合。
这些指令的后缀表示不同的条件而不是操作数大小
2)可以条件跳转到程序的某个其他的部分,
3)可以有条件地传送数据。

在这里插入图片描述

一条SET指令的目的操作数是低位单字节寄存器元素(图3-2)之一,或是一个字节的内存位置,指令会将这个字节设置成0或者1。
为了得到一个32位或64位结果,我们必须对高位清零。一个计算C语言表达式a<b的典型指令序列如下所示,这里a和b都是long类型:
在这里插入图片描述
注意cmpq指令的比较顺序(第2行)。虽然参数列出的顺序先是%rsi(b)再是%rdi(a),实际上比较的是a和b
还要记得,正如在3.4.2节中讨论过的那样,movzbl指令不仅会把%eax的高3个字节清零,还会把整个寄存器%rax的高4个字节都清零。
某些底层的机器指令可能有多个名字,我们称之为“同义名(synonym)”。比如说,setg(表示“设置大千”)和setnle(表示“设置不小千等千”)指的就是同一条机器指令。编译器和反汇编器会随意决定使用哪个名字。

来看sete的情况,即“当相等时设置(setwhenequal)"指令。当a=b时,会得到t=0,因此零标志置位就表示相等。类似地,考虑用setl,即“当小千时设置(setwhenless)"指令,测试一个有符号比较。

对于无符号比较的测试,现在设a和b是变量a和b的无符号形式表示的整数。在执行计算t=a-b中,当a-b<O时,CMP指令会设置进位标志,因而无符号比较使用的是进位标志和零标志的组合。

注意到机器代码如何区分有符号和无符号值是很重要的。
同C语言不同,机器代码不会将每个程序值都和一个数据类型联系起来。相反,大多数情况下,机器代码对于有符号和无符号两种情况都使用一样的指令,这是因为许多算术运算对无符号和补码算术都有一样的位级行为。有些情况需要用不同的指令来处理有符号和无符号操作,例如,使用不同版本的右移、除法和乘法指令,以及不同的条件码组合。

3.6.3 跳转指令

正常执行的情况下,指令按照它们出现的顺序一条一条地执行。跳转(jump)指令会导致执行切换到程序中一个全新的位置。在汇编代码中,这些跳转的目的地通常用一个标号(label)指明。

在产生目标代码文件时,汇编器会确定所有带标号指令的地址,并将跳转目标(目的指令的地址)编码为跳转指令的一部分。

图3-15列举了不同的跳转指令。jmp指令是无条件跳转。它可以是直接跳转,即跳转目标是作为指令的一部分编码的;也可以是间接跳转,即跳转目标是从寄存器或内存位置中读出的。汇编语言中,直接跳转是给出一个标号作为跳转目标的,间接跳转的写法是 ‘ * ’ 后面跟一个操作数指示符,使用图3-3中描述的内存操作数格式中的一种。

  • 用寄存器%rax中的值作为跳转目标
    	jmp *%rax
    
  • 以%rax中的值作为读地址,从内存中读出跳转目标。
    	jmp *(%rax)
    

在这里插入图片描述
表中所示的其他跳转指令都是有条件的它们根据条件码的某种组合,或者跳转,或者继续执行代码序列中下一条指令。
这些指令的名字和跳转条件与SET指令的名字和设置条件是相匹配的(参见图3-14)。同SET指令一样,一些底层的机器指令有多个名字。条件跳转只能是直接跳转。

3.6.4 跳转指令的编码

跳转指令有几种不同的编码,但是最常用都是PC相对的(PC-relative)。
也就是,它们会将目标指令的地址与紧跟在跳转指令后面那条指令的地址之间的差作为编码。这些地址偏移量可以编码为1、2或4个字节。
第二种编码方法是给出“绝对“地址,用4个字节直接指定目标。汇编器和链接器会选择适当的跳转目的编码。

当执行PC相对寻址时,程序计数器的值是跳转指令后面的那条指令的地址,而不是跳转指令本身的地址。

跳转指令提供了一种实现条件执行(if)和几种不同循环结构的方式。

3.6.5 用条件控制来实现条件分支

将条件表达式和语句从C语言翻译成机器代码,最常用的方式是结合有条件和无条件跳转。
在这里插入图片描述
汇编代码的实现(图3-16c)首先比较了两个操作数(第2行),设置条件码。
如果比较的结果表明x大千或者等于y,那么它就会跳转到第8行,增加全局变量ge_cnt,计算x-y作为返回值并返回。
由此我们可以看到absdiff_se对应汇编代码的控制流非常类似千gotodiff—se的goto代码。

3.6.6 用条件传送来实现条件分支

实现条件操作的传统方法是通过使用控制的条件转移。
当条件满足时,程序沿着一条执行路径执行,而当条件不满足时,就走另一条路径。这种机制简单而通用,但是在现代
处理器上,它可能会非常低效。

一种替代的策略是使用数据的条件转移
这种方法计算一个条件操作的两种结果,然后再根据条件是否满足从中选取一个。只有在一些受限制的情况中,这种策略才可行,但是如果可行,就可以用一条简单的条件传送指令来实现它,条件传送指令更符合现代处理器的性能特性。
我们将介绍这一策略,以及它在x86-64上的实现。
在这里插入图片描述
处理器通过使用流水线(pipelining)来获得高性能在流水线中,一条指令的处理要经过一系列的阶段,每个阶段执行所需操作的一小部分(例如,从内存取指令、确定指令类型、从内存读数据、执行算术运算、向内存写数据,以及更新程序计数器)。
这种方法通过重叠连续指令的步骤来获得高性能,例如,在取一条指令的同时,执行它前面一条指令的算术运算。要做到这一点,要求能够事先确定要执行的指令序列,这样才能保持流水线中充满了待执行的指令
当机器遇到条件跳转(也称为“分支”)时,只有当分支条件求值完成之后,才能决定分支往哪边走。处理器采用非常精密的分支预测逻辑来猜测每条跳转指令是否会执行。只要它的猜测还比较可靠(现代微处理器设计试图达到90%以上的成功率),指令流水线中就会充满着指令。另一方面,错误预测一个跳转,要求处理器丢掉它为该跳转指令后所有指令己做的工作,然后再开始用从正确位置处起始的指令去填充流水线。正如我们会看到的,这样一个错误预测会招致很严重的惩罚,浪费大约15~30个时钟周期,导致程序性能严重下降。

另一方面,无论测试的数据是什么,编译出来使用条件传送的代码所需的时间都是大约8个时钟周期。控制流不依赖于数据,这使得处理器更容易保持流水线是满的。

图3-18列举了x86-64上一些可用的条件传送指令。每条指令都有两个操作数:源寄存器或者内存地址S,和目的寄存器R
与不同的SET(3.6.2节)和跳转指令(3.6.3节)一样,这些指令的结果取决千条件码的值
源值可以从内存或者源寄存器中读取,但是只有在指定的条件满足时,才会被复制到目的寄存器中。
源和目的的值可以是16位、32位或64位长。不支持单字节的条件传送
无条件指令的操作数的长度显式地编码在指令名中(例如movw和movl),汇编器可以从目标寄存器的名字推断出条件传送指令的操作数长度,所以对所有的操作数长度,都可以使用同一个的指令名字。
在这里插入图片描述
同条件跳转不同,处理器无需预测测试的结果就可以执行条件传送。处理器只是读源值(可能是从内存中),检查条件码,然后要么更新目的寄存器,要么保持不变。

在这里插入图片描述
基于条件传送的代码,会对then-expr和else-expr都求值,最终值的选择基于对testexpr的求值。
如果这两个表达式中的任意一个可能产生错误条件或者副作用,就会导致非法的行为。
c longcread(long*xp){ return(xp?*xp:O); }
这个实现是非法的,因为即使当测试为假时,movq指令(第2行)对xp的间接引用还是发生了,导致一个间接引用空指针的错误。所以,必须用分支代码来编译这段代码。
使用条件传送也不总是会提高代码的效率。例如,如果then-expr或者else-expr的求值需要大量的计算,那么当相对应的条件不满足时,这些工作就白费了。编译器必须考虑浪费的计算和由于分支预测错误所造成的性能处罚之间的相对性能。说实话,编译器并不具有足够的信息来做出可靠的决定;例如,它们不知道分支会多好地遵循可预测的模式。
我们对GCC的实验表明,只有当两个表达式都很容易计算时,例如表达式分别都只是一条加法指令,它才会使用条件传送。根据我们的经验,即使许多分支预测错误的开销会超过更复杂的计算,GCC还是会使用条件控制转移。
所以,总的来说,条件数据传送提供了一种用条件控制转移来实现条件操作的替代策略。它们只能用于非常受限制的情况,但是这些情况还是相当常见的,而且与现代处理器的运行方式更契合。

3.6.7 循环

C语言提供了多种循环结构,即do-while、while和for。
汇编中没有相应的指令存在,可以用条件测试和跳转组合起来实现循环的效果。
GCC和其他汇编器产生的循环代码主要基千两种基本的循环模式。

3.6.7.1 do-while 循环

	dobody-statementwhile (test-expr) ;
--> 翻译后loop :body-statementt = test-expr ;if (t)goto loop ;

在这里插入图片描述
条件跳转指令jg(第7行)是实现循环的关键指令,它决定了是需要继续重复还是退出循环。

3.6.7.2 while循环

while (test-expr)body-statement

在第一次执行body-statement之前,它会对test-expr求值,循环有可能就中止了。

有很多种方法将while循环翻译成机器代码,GCC在代码生成中使用其中的两种方法。这两种方法使用同样的循环结构,与do-while一样,不过它们实现初始测试的方法不同。

第一种翻译方法,我们称之为跳转到中间(jumptomiddle),它执行一个无条件跳转跳到循环结尾处的测试,以此来执行初始的测试。可以用以下模板来表达这种方法,这个模板把通用的while循环格式翻译到goto代码:

	goto test;
loop:body-statement
test:t=test-expr;if(t)gotoloop

第二种翻译方法,我们称之为guarded-do,首先用条件分支,如果初始条件不成立就跳过循环,把代码变换为do-while循环。
当使用较高优化等级编译时,例如使用命令行选项-01,GCC会采用这种策略。
可以用如下模板来表达这种方法,把通用的while循环格式翻译成do-while循环:

t = test-expr;
if(!t)goto done;
dobody-statementwhile(test-expr);
done:
--> 翻译成
t = test-expr;
if(!t)gotodone;
loop:body-statementt=test-expr;if(t)gotoloop;
done:

3.6.7.3 for循环

	for (init-expr ; test-expr ; update-expr)body-statement--> 等同于init-expr ;while (test-expr) {body-statementupdate-expr;--> 跳转到中间策略会得到如下goto代码init-expr;goto test;loop:body-statementupdate-expr;test :t = test-expr;if (t)goto loop;--> guarded-do 策略得到 :init-expr;t = test-expr;if (!t)goto done;loop:body-statementupdate-expr;t = test-expr;if (t)goto loop;done:

3.6.8 switch语句

根据一个整数索引值进行多重分支。
它们不仅提高了C代码的可读性,而且通过使用跳转表(jumptable)这种数据结构使得实现更加高效。
跳转表是一个数组,表项t是一个代码段的地址,这个代码段实现当开关索引值等千1时程序应该采取的动作。
程序代码用开关索引值来执行一个跳转表内的数组引用,确定跳转指令的目标。和使用一组很长的if-else语句相比,使用跳转表的优点是执行开关语句的时间与开关情况的数量无关。
GCC根据开关情况的数量和开关情况值的稀疏程度来翻译开关语句。当开关情况数量比较多(例如4个以上),并且值的范围跨度比较小时,就会使用跳转表。
在这里插入图片描述
数组 jt 包含7个表项,每个都是一个代码块的地址。这些位置由代码中的标号定义,在jt的表项中由代码指针指明,由标号加上飞矿前缀组成。(回想运算符&创建一个指向数据值的指针。在做这个扩展时,GCC的作者们创造了一个新的运算符&&,这个运算符创建一个指向代码位置的指针。)

原始的C代码有针对值100、102-104和106的清况,但是开关变量n可以是任意整数。编译器首先将n减去100,把取值范围移到0和6之间,创建一个新的程序变量,在我们的C版本中称为index。补码表示的负数会映射成无符号表示的大正数,利用这一事实,将index看作无符号值,从而进一步简化了分支的可能性。因此可以通过测试index是否大千6来判定index是否在0~6的范围之外。
在这里插入图片描述
执行switch语句的关键步骤是通过跳转表来访问代码位置。jmp指令的操作数有前缀" * ",表明这是一个间接跳转,操作数指定一个内存位置,索引由寄存器%rsi给出,这个寄存器保存着index的值。

3.7 过程

过程是软件中一种很重要的抽象。
它提供了一种封装代码的方式,用一组指定的参数和一个可选的返回值实现了某种功能。然后,可以在程序中不同的地方调用这个函数。
不同编程语言中,过程的形式多样:函数(function)、方法(method)、子例程(subroutine)、处理函数(handler)等等,但是它们有一些共有的特性。

要提供对过程的机器级支持,必须要处理许多不同的属性。为了讨论方便,假设过程p调用过程Q,Q执行后返回到P。
这些动作包括下面一个或多个机制:

  • 传递控制。
    在进入过程Q的时候,程序计数器必须被设置为Q的代码的起始地址,然后在返回时,要把程序计数器设置为P中调用Q后面那条指令的地址。
  • 传递数据。
    P必须能够向Q提供一个或多个参数,Q必须能够向P返回一个值。
  • 分配和释放内存。
    在开始时,Q可能需要为局部变量分配空间,而在返回前,又必须释放这些存储空间。

3.7.1 运行时栈

C语言过程调用机制的一个关键特性(大多数其他语言也是如此)在于使用了栈数据结构提供的后进先出的内存管理原则
当Q运行时,它只需要为局部变量分配新的存储空间,或者设置到另一个过程的调用。
另一方面,当Q返回时,任何它所分配的局部存储空间都可以被释放。
因此,程序可以用栈来管理它的过程所需要的存储空间,栈和程序寄存器存放着传递控制和数据、分配内存所需要的信息。
当P调用Q时,控制和数据信息添加到栈尾。当P返回时,这些信息会释放掉。

x86-64的栈向低地址方向增长,而栈指针%rsp指向栈顶元素。(push / popq)

当x86-64过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间。这个部分称为过程的栈帧(stackfram)。
在这里插入图片描述
当前正在执行的过程的帧总是在栈顶。
当过程P调用过程Q时,会把返回地址压入栈中,指明当Q返回时,要从P程序的哪个位置继续执行。我们把这个返回地址当做P的栈帧的一部分,因为它存放的是与P相关的状态。
Q的代码会扩展当前栈的边界,分配它的栈帧所需的空间。在这个空间中,它可以保存寄存器的值,分配局部变量空间,为它调用的过程设置参数。
大多数过程的栈帧都是定长的,在过程的开始就分配好了。但是有些过程需要变长的帧。
通过寄存器,过程P可以传递最多6个整数值(也就是指针和整数),但是如果Q需要更多的参数,P可以在调用Q之前在自己的栈帧里存储好这些参数。

为了提高空间和时间效率,x86-64过程只分配自己所需要的栈帧部分。例如,许多过程有6个或者更少的参数,那么所有的参数都可以通过寄存器传递。因此,图3-25中画出的某些栈帧部分可以省略。实际上,**许多函数甚至根本不需要栈帧。当所有的局部变量都可以保存在寄存器中,而且该函数不会调用任何其他函数(有时称之为叶子过程,此时把过程调用看做树结构)时,就可以这样处理。**例如,到目前为止我们仔细审视过的所有函数都不需要栈帧。

3.7.2 转移控制

将控制从函数P转移到函数Q只需要简单地把程序计数器(PC)设置为Q的代码的起始位置。不过,当稍后从Q返回的时候,处理器必须记录好它需要继续P的执行的代码位置。
在x86-64机器中,这个信息是用指令call Q调用过程Q来记录的。该指令会把地址A压入栈中,并将PC设置为Q的起始地址。压入的地址A被称为返回地址,是紧跟在call指令后面的那条指令的地址。对应的指令ret会从栈中弹出地址A,并把PC设置为A。
下表给出的是call和ret指令的一般形式:
在这里插入图片描述
call指令有一个目标,即指明被调用过程起始的指令地址。
同跳转一样,调用可以是直接的,也可以是间接的。在汇编代码中,直接调用的目标是一个标号,而间接调用的目标是*后面跟一个操作数指示符,使用的是图3-3中描述的格式之一。
在这里插入图片描述
可以看到,这种把返回地址压入栈的简单的机制能够让函数在稍后返回到程序中正确的点。C语言(以及大多数程序语言)标准的调用/返回机制刚好与栈提供的后进先出的内存管理方法吻合。

3.7.3 数据传送

过程调用还可能包括把数据作为参数传递,而从过程返回还有可能包括返回一个值。x86-64中,大部分过程间的数据传送是通过寄存器实现的。当过程P调用过程Q时,P的代码必须首先把参数复制到适当的寄存器中。类似地,当Q返回到P时,P的代码可以访问寄存器%rax
中的返回值。

可以通过寄存楛最多传递6个整型(例如整数和指针)参数。寄存器的使用是有特殊顺序的,寄存器使用的名字取决千要传递的数据类型的大小,如图3-28所示。会根据参数在参数列表中的顺序为它们分配寄存器。可以通过64位寄存器适当的部分访问小千64位的参数。
在这里插入图片描述
如果一个函数有大于6个整型参数,超出6个的部分就要通过栈来传递。
在这里插入图片描述
图3-29b中给出proc生成的汇编代码。前面6个参数通过寄存器传递,后面2个通过栈传递,就像图3-30中画出来的那样。
可以看到,作为过程调用的一部分,返回地址被压入栈中。因而这两个参数位千相对千栈指针距离为8和16的位置。
在这段代码中,我们可以看到根据操作数的大小,使用了ADD指令的不同版本:al(long)使用addq,a2(int)使用addl,a3(short)使用addw,而a4(char)使用addb。请注意第6行的movl指令从内存读入4字节,而后面的addb指令只使用其中的低位一字节。
在这里插入图片描述

3.7.4 栈上的局部存储

到目前为止我们看到的大多数过程示例都不需要超出寄存器大小的本地存储区域。
不过有些时候,局部数据必须存放在内存中,常见的情况包括:

  • 寄存器不足够存放所有的本地数据。
  • 对一个局部变蜇使用地址运算符’&',因此必须能够为它产生一个地址。
  • 某些局部变量是数组或结构,因此必须能够通过数组或结构引用被访问到。

在这里插入图片描述
来看一个处理地址运算符的例子,图3-31a中给出的两个函数。图3-31b展示了caller是如何用栈帧来实现这些局部变量的。
caller的代码开始的时候把栈指针减掉了16;实际上这就是在栈上分配了16个字节
S表示栈指针的值,可以看到这段代码计算&arg2为S+8(第5行),而&argl为S。因此可以推断局部变量argl和arg2存放在栈帧中相对千栈指针偏移量为0和8的地方。
当对swap_add的调用完成后,caller的代码会从栈上取出这两个值(第8~9行),计算它们的差,再乘以swap—add在寄存器%rax中返回的值(第10行)。
最后,该函数把栈指针加16,释放栈帧(第11行)。
通过这个例子可以看到,运行时栈提供了一种简单的、在需要时分配、函数完成时释放局部存储的机制。

3.7.5 寄存器中的局部存储空间

寄存器组是唯一被所有过程共享的资源。
被调用者不会覆盖调用者稍后会使用的寄存器值。
为此,x86-64采用了一组统一的寄存器使用惯例,所有的过程(包括程序库)都必须遵循。根据惯例,**寄存器%rbx、%rbp和%r12~%r15被划分为被调用者保存寄存器。**有了这条惯例,P的代码就能安全地把值存在被调用者保存寄存器中(当然,要先把之前的值保存到栈上),调用Q,然后继续使用寄存器中的值,不用担心值被破坏。
所有其他的寄存器,除了栈指针%rsp,都分类为调用者保存寄存器。这就意味着任何函数都能修改它们。可以这样来理解“调用者保存”这个名字:过程P在某个此类寄存器中有局部数据,然后调用过程Q。因为Q可以随意修改这个寄存器,所以在调用之前首先保存好这个数据是P(调用者)的责任。
在这里插入图片描述

3.7.6 递归过程

每个过程调用在栈中都有它自己的私有空间,因此多个未完成调用的局部变量不会相互影响。此外,栈的原则很自然地就提供了适当的策略,当过程被调用时分配局部存储,当返回时释放存储。
在这里插入图片描述

3.8 数组分配和访问

C语言的一个不同寻常的特点是可以产生指向数组中元素的指针,并对这些指针进行运算。在机器代码中,这些指针会被翻译成地址计算。
优化编译器非常善于简化数组索引所使用的地址计算。不过这使得C代码和它到机器代码的翻译之间的对应关系有些难以理解。

3.8.1 基本原则

对于数据类型T和整型常数N,声明如下:

	TA[N];

起始位置表示为 x A 。这个声明有两个效果。
首先,它在内存中分配一个L * N字节的连续区域,这里L是数据类型T的大小(单位为字节)。
其次,它引入了标识符A,可以用A来作为指向数组开头的指针,这个指针的值就是 x A 。
可以用0 ~ N-1的整数索引来访问该数组元素。数组元素i会被存放在地址为x A + L*i 的地方。

x86-64的内存引用指令可以用来简化数组访问。例如,假设E是一个int型的数组,而我们想计算 E[i],在此,E的地址存放在寄存器 %rdx中,而 i 存放在寄存器%rcx中。然后,指令

movl (%rdx, %rcx, 4), %eax

会执行地址计算xE + 4i,读这个内存位置的值,并将结果存放到寄存器%eax中。
允许的伸缩因子1、2、4和8覆盖了所有基本简单数据类型的大小。

3.8.2 指针运算

C语言允许对指针进行运算,而计算出来的值会根据该指针引用的数据类型的大小进行伸缩。也就是说,如果p是一个指向类型为T的数据的指针,p的值为xp,那么表达式 p + i 的值为xp+L*i,这里L是数据类型T的大小。
单操作数操作符’ & ‘和’ * '可以产生指针和间接引用指针。也就是,对千一个表示某个对象的表达式Expr,&Expr是给出该对象地址的一个指针。对于一个表示地址的表达式AExpr,*AExpr给出该地址处的值。因此,表达式Expr与*&Expr是等价的。
可以对数组和指针应用数组下标操作。数组引用 A[i] 等同千表达式*(A+i)。它计算第 i 个数组元素的地址,然后访问这个内存位置。
在这里插入图片描述

3.8.3 嵌套的数组

当我们创建数组的数组时,数组分配和引用的一般原则也是成立的。

	例如,声明intA[5][3];等价于下面的声明typedef int row3_t[3];row3_t A[5];

数据类型row3_t被定义为一个3个整数的数组。数组A包含5个这样的元素,每个元素需要12个字节来存储3个整数。整个数组的大小就是4X5X3=60字节。
数组A还可以被看成一个5行3列的二维数组,用 A [0] [0] 到 A [4] [2]来引用。数
组元素在内存中按照“行优先"的顺序排列,意味着第0行的所有元素,可以写作A[0],后面跟着第1行的所有元素(A[i]),以此类推,如图3-36所示。

要访问多维数组的元素,编译器会以数组起始为基地址,(可能需要经过伸缩的)偏移量为索引,产生计算期望的元素的偏移量,然后使用某种MOV指令。通常来说,对于一个声明如下的数组:
在这里插入图片描述
在这里插入图片描述

3.8.4 定长数组

	#define N 16typedef int fix_matrix [N][N];

在这里插入图片描述

在这里插入图片描述

3.8.5 变长数组

	int A[expr1][expr2];--> 要访问 n X n 数组的元素i,jint var_ele (long n, int A[n][n], long i, long j) {return A[i][j];}

在这里插入图片描述
这个地址的计算类似千定长数组的地址计算(参见3.8.3节),不同点在千

  1. 由于增加了参数n,寄存器的使用变化了;
  2. 用了乘法指令来计算n·i(第2行),而不是用leaq指令来计算3i。
    因此引用变长数组只需要对定长数组做一点儿概括。动态的版本必须用乘法指令对i伸缩n倍,而不能用一系列的移位和加法。在一些处理器中,乘法会招致严重的性能处罚,但是在这种情况中无可避免。

3.9 异质的数据结构

3.9.1 结构

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

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

这个结构包括4个字段:两个4字节int、一个由两个类型为int的元素组成的数组和一个8字节整型指针,总共是24个字节:
在这里插入图片描述
为了访问结构的字段,编译器产生的代码要将结构的地址加上适当的偏移。例如,假设struct rec*类型的变最r放在寄存器%rdi中。那么下面的代码将元素r->i复制到元素r->j:
在这里插入图片描述
在这里插入图片描述

3.9.2 联合

联合提供了一种方式,能够规避C语言的类型系统,允许以多种类型来引用一个对象。
联合声明的语法与结构的语法一样,只不过语义相差比较大。它们是用不同的字段来引用相同的内存块。
在这里插入图片描述

3.9.3 数据对齐

许多计算机系统对基本数据类型的合法地址做出了一些限制,要求某种类型对象的地址必须是某个值K(通常是2、4或8)的倍数。、这种对齐限制简化了形成处理器和内存系统之间接口的硬件设计。
例如,假设一个处理器总是从内存中取8个字节,则地址必须为8的倍数。如果我们能保证将所有的double类型数据的地址对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

无论数据是否对齐,x86-64硬件都能正确工作。不过,Intel还是建议要对齐数据以提高内存系统的性能。对齐原则是任何K字节的基本对象的地址必须是K的倍数。可以看到这条原则会得到如下对齐:
在这里插入图片描述
对于包含结构的代码,编译器可能需要在字段的分配中插入间隙,以保证每个结构元素都满足它的对齐要求。而结构本身对它的起始地址也有一些对齐要求。
在这里插入图片描述

3.10 在机器级程序中将控制与数据结合起来

3.10.1 理解指针

指针和它们映射到机器代码的关键原则

  1. 每个指针都对应一个类型。这个类型表明该指针指向的是哪一类对象。
  2. 每个指针都有一个值。这个值是某个指定类型的对象的地址。特殊的NULL(0)值表示该指针没有指向任何地方
  3. 指针用" & "运算符创建。这个运算符可以应用到任何lvalue类的C表达式上,lvalue意指可以出现在赋值语句左边的表达式。这样的例子包括变量以及结构、联合和数组的元素。
  4. " * " 操作符用于间接引用指针。其结果是一个值,它的类型与该指针的类型一致。间接引用是用内存引用来实现的,要么是存储到一个指定的地址,要么是从指定的地址读取
  5. 数组与指针紧密联系。一个数组的名字可以像一个指针变量一样引用(但是不能修改)。数组引用(例如a[3])与指针运算和间接引用(例如*(a+3))有一样的效果。
  6. 数组引用和指针运算都需要用对象大小对偏移量进行伸缩。当我们写表达式p+i,这里指针p的值为p,得到的地址计算为p+L*i,这里L是与p相关联的数据类型的大小。
  7. 将指针从一种类型强制转换成另一种类型,只改变它的类型,而不改变它的值。强制类型转换的一个效果是改变指针运算的伸缩。例如,如果p是一个char类型的指针,它的值为p,那么表达式(int)p+7计算为p+28,而(int*)(p+7)计算为p+7。(回想一下,强制类型转换的优先级高千加法。)
  8. 指针也可以指向函数。这提供了一个很强大的存储和向代码传递引用的功能,这些引用可以被程序的某个其他部分调用。

3.10.2 GDB

todo

3.10.3 内存越界引用和缓冲区溢出

我们已经看到,C对千数组引用不进行任何边界检查,而且局部变量和状态信息(例如保存的寄存器值和返回地址)都存放在栈中。
这两种情况结合到一起就能导致严重的程序错误,对越界的数组元素的写操作会破坏存储在栈中的状态信息。

一种特别常见的状态破坏称为缓冲区溢出(bufferoverflow)。通常,在栈中分配某个字符数组来保存一个字符串,但是字符串的长度超出了为数组分配的空间。
缓冲区溢出的一个更加致命的使用就是让程序执行它本来不愿意执行的函数。

3.10.4 对抗缓冲区溢出攻击

缓冲区溢出攻击的普遍发生给计算机系统造成了许多的麻烦。现代的编译器和操作系统实现了很多机制,以避免遭受这样的攻击,限制入侵者通过缓冲区溢出攻击获得系统控制的方式。

  1. 栈随机化
    栈随机化的思想使得栈的位置在程序每次运行时都有变化。因此,即使许多机器都运行同样的代码,它们的栈地址都是不同的。
    实现的方式是:程序开始时,在栈上分配一段0~n字节之间的随机大小的空间,例如,使用分配函数alloca在栈上分配指定字节数量的空间。
    程序不使用这段空间,但是它会导致程序每次执行时后续的栈位置发生了变化。
    分配的范围n必须足够大,才能获得足够多的栈地址变化,但是又要足够小,不至千浪费程序太多的空间。
    在Linux系统中,栈随机化已经变成了标准行为。它是更大的一类技术中的一种,这类技术称为地址空间布局随机化(Address-SpaceLayoutRandomization),或者简称ASLR

  2. 栈破坏检测
    计算机的第二道防线是能够检测到何时栈已经被破坏。破坏通常发生在当超越局部缓冲区的边界时。在C语言中,没有可靠的方法
    来防止对数组的越界写。但是,我们能够在发生了越界写的时候,在造成任何有害结果之前,尝试检测到它。
    最近的GCC版本在产生的代码中加入了一种栈保护者(stackprotector)机制,来检测缓冲区越界。
    其思想是在栈帧中任何局部缓冲区与栈状态之间存储一个特殊调用者的栈帧的金丝雀(canary)值,这个金丝雀值,也称为哨兵值(guard value),是在程序每次运行时随机产生的,因此,攻击者没有简单的办法能够知道它是什么。
    在恢复寄存器状态和从函数返回之前,程序检查这个金丝雀值是否被该函数的某个操作或者该函数调用的某个函数的某个操作改变了。如果是的,那么程序异常中止。
    栈保护很好地防止了缓冲区溢出攻击破坏存储在程序栈上的状态。它只会带来很小的性能损失,特别是因为GCC只在函数中有局部char类型缓冲区的时候才插入这样的代码。当然,也有其他一些方法会破坏一个正在执行的程序的状态,但是降低栈的易受攻击性能够对抗许多常见的攻击策略。

  3. 限制可执行代码区域
    最后一招是消除攻击者向系统中插入可执行代码的能力。
    一种方法是限制哪些内存区域能够存放可执行代码。
    在典型的程序中,只有保存编译器产生的代码的那部分内存才需要是可执行的。其他部分可以被限制为只允许读和写。
    正如第9章中会看到的,虚拟内存空间在逻辑上被分成了页(page),典型的每页是2048或者4096个字节。硬件支持多种形式的内存保护,能够指明用户程序和操作系统内核所允许的访问形式。
    许多系统允许控制三种访问形式:读(从内存读数据)、写(存储数据到内存)和执行(将内存的内容看作机器级代码)。以前,x86体系结构将读和执行访问控制合并成一个1位的标志,这样任何被标记为可读的页也都是可执行的。栈必须是既可读又可写的,因而栈上的字节也都是可执行的。已经实现的很多机制,能够限制一些页是可读但是不可执行的,然而这些机制通常会带来严重的性能损失。

3.10.5 支持变长栈帧

todo
 


# 五、总结 todo
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Hive【Hive(三)查询语句】

    前言 今天是中秋节&#xff0c;早上七点就醒了&#xff0c;干啥呢&#xff0c;大一开学后空教室紧缺&#xff0c;还不趁着假期来学校等啥呢。顺便偷偷许个愿吧&#xff0c;希望在明年的这个时候&#xff0c;秋招不知道赶不赶得上&#xff0c;我希望拿几个国奖&#xff0c;蓝桥杯…...

    2023/9/29 18:38:15
  2. Cocos Creator3.8 实战问题(二)cocos creator编辑器中绑定事件引发的bug

    问题描述&#xff1a; 编辑器里面多个按钮绑定同一个脚本&#xff0c;并配置事件&#xff0c;脚本中有其他消息监听&#xff0c;引起逻辑混乱。 问题原因&#xff1a; 编辑器里面多个按钮绑定同一个脚本&#xff0c; 每次绑定一个按钮事件&#xff0c;就会导致加载一次脚本 &…...

    2023/9/29 18:36:26
  3. 3种Renko图表形态FPmarkets3秒轻松判断价格走势

    Renko图表形态在交易中的应用并不逊色于其他技术分析方法。相较于普通的烛台图表&#xff0c;使用Renko图表时&#xff0c;有些经典模式更容易被发现和识别&#xff0c;FPmarkets总结这些模式包括&#xff1a; 首先是头和肩膀形态。这是一种价格反转形态&#xff0c;由两个较小…...

    2023/9/29 18:35:53
  4. 使用不同尺寸的传感器拍照时,怎么保证拍出同样视场范围的照片?

    1、问题背景 使用竞品机做图像效果对比时&#xff0c;我们通常都会要求拍摄的照片要视场范围一致&#xff0c;这样才具有可比性。之前我会考虑用同样焦距、同样分辨率的设备去拍照对比就可以了&#xff0c;觉得相机的视场范围只由镜头焦距来决定。 但如果对于不同尺寸的传感器…...

    2023/9/29 18:35:10
  5. 基于css变量轻松实现网站的主题切换功能

    我们经常看到一些网站都有主题切换&#xff0c;例如vue官方文档。那他是怎么实现的呢&#xff1f; 检查元素&#xff0c;发现点击切换时&#xff0c;html元素会动态的添加和移除一个class:dark&#xff0c;然后页面主题色就变了。仔细想想&#xff0c;这要是放在以前&#xff0…...

    2023/9/29 18:32:02
  6. Vue 实现表单的增删改查功能及表单的验证

    前言&#xff1a; 上一篇我们已经将前端表单的数据和后端的数据交互了&#xff0c;今天我们就继续开发功能来实现表单的增删改查功能及表单的验证 一&#xff0c;表单的增删改查功能 新增 去官网找模版&#xff1a; 1.1添加新增按钮&#xff1a; 1.2添加新增弹窗点击事件&am…...

    2023/9/29 18:31:45
  7. 刷题打卡day39第九章 动态规划: 62.不同路径 、 63. 不同路径 II

    62.不同路径 开始进入二维dp数组的部分。 需要注意的是&#xff1a;二维vectpr数组的定义方式&#xff0c;及初始化&#xff1a; vector<vector<int>> dp(m, vector<int>(n, 0)); 定义一个m行n列的二维数组&#xff0c;初始化为0. class Solution { pub…...

    2023/9/25 15:31:54
  8. 怎么选择项目协作工具,最详细的指南来了!

    现代企业要在当前的气候中生存&#xff0c;就必须适应。COVID-19疫情对传统的工作方式提出了质疑&#xff0c;并强调了远程和混合工作模式的重要性。如今&#xff0c;企业必须采用敏捷的策略&#xff0c;以便更轻松地应对挑战&#xff0c;并为员工提供更大程度的灵活性。敏捷状…...

    2023/9/5 13:09:01
  9. 最新版高可用hadoop集群搭建及问题排查

    最新版高可用hadoop集群搭建及问题排查 注释 注&#xff1a;本文只针对高可用hadoop集群部署做讲解&#xff0c;如需系统学习hadoop&#xff0c;可参考gitee文档&#xff1a;大数据入门指南 集群架构 集群搭建 前置步骤 所有节点执行 配置hosts&#xff1a; vim /etc/hos…...

    2023/7/8 16:48:22
  10. Linux中wget、yum与apt-get用法及区别

    Linux中wget、yum与apt-get用法及区别wget、yum与apt-get区别一、Centos包管理工具 yum1.1 yum源配置1.2 yum命令的基本使用二、Ubuntu包管理工具 apt-get2.1 apt-get基本使用三、下载工具wgetwget、yum与apt-get区别 yum与apt-get都是包管理工具&#xff0c;就像python中的pip…...

    2023/9/12 0:40:59
  11. Visual SVN Server的下载和使用

    摘要 代码管理和文件下载管理服务器Visual SVN Server使用相比如git操作更方便&#xff0c;不用指令就可以完成&#xff0c;其次能够上传大文件十分方便 一 安装Visual SVN Server 下载msi文件 进入Visual SVN Server官网下载64bit文件&#xff0c;官网如下&#xff1a; ht…...

    2023/9/26 14:02:48
  12. CPP 多重继承

    C中的多重继承是指一个类可以同时从多个父类继承成员。多重继承在C中是一种重要的特性&#xff0c;它可以使得程序员更加灵活地设计和组织类的继承关系&#xff0c;使得代码的复用性和可维护性得到提高。 C中的多重继承可以通过在类定义中使用逗号分隔多个基类来实现。例如&am…...

    2023/5/9 14:15:05
  13. 物业项目经理需要考什么证书 怎么报名

    物业项目经理需要考物业项目经理资格证书&#xff0c;这个证书属于物业项目经理必须持有的证书&#xff0c;是项目管理和对外招投标必备证书&#xff0c;同时也是项目管理人员任职资格证。 1物业项目经理证书怎么报考 1.确定物业项目经理的报考条件&#xff0c;年龄是硬性要求&…...

    2023/9/22 23:14:07
  14. Lesson9.网络基础1

    网络协议初识 所谓的协议就是人们为了通信的一种约定 操作系统要进行协议管理,必然会先描述,再组织协议本质就是软件,软件是可以"分层"协议在设计的时候,就是被层状的划分的, 为什么要划分成为层状结构 场景复杂功能解耦(便于人们进行各种维护)OSI七层模型 局域网中…...

    2023/9/23 18:41:07
  15. GIL的前世今生以及原理

    线程是操作系统进行计算和调度的最小的单位&#xff0c;程序运行在这里&#xff0c; 每一个线程有属于自己的上下文。进程是比线程更大一点的单位&#xff0c; 每个进程有自己的内存之类的&#xff0c;每个进程有好几个线程&#xff0c;共享内存&#xff0c;就是可以读写同样的…...

    2023/9/29 17:30:39
  16. 决策树ID3算法

    1. 决策树ID3算法的信息论基础 机器学习算法其实很古老&#xff0c;作为一个码农经常会不停的敲if, else if, else,其实就已经在用到决策树的思想了。只是你有没有想过&#xff0c;有这么多条件&#xff0c;用哪个条件特征先做if&#xff0c;哪个条件特征后做if比较优呢&#…...

    2023/9/25 13:57:22
  17. 短信宝接入发送短信实测 Java

    1丶注册短信宝 2 代码分为授权跟参数实体 授权实体 Data public class SmsBaoAuthorization {//http://api.smsbao.com/sms private String url;//在短信宝注册的用户名private String username;//在短信宝注册的密码 private String password; } 参数实体 Data pub…...

    2023/9/16 22:28:25
  18. 63 ALTERNATIVE I/O MODELS

    1 Overview 补充 1.1 Level-Triggered and Edge-Triggered Notification Before discussing the various alternative I/O mechanisms in detail, we need to distinguish two models of readiness notification for a file descriptor: Level-triggered notification: A f…...

    2023/9/2 13:28:49
  19. ssm+vue在线课程培训系统java

    在线培训系统的需求和管理上的不断提升&#xff0c;在线培训系统的潜力将无限扩大&#xff0c;在线培训系统在业界被广泛关注&#xff0c;本网站及对此进行总体分析&#xff0c;将在线培训系统信息管理的发展提供学校的管理帮助更大。 根据现有的模块&#xff0c;除管理员对系统…...

    2023/9/29 17:07:16
  20. asio(十一)、udp异步服务器

    官网教程&#xff1a;https://think-async.com/Asio/asio-1.26.0/doc/asio/tutorial/tutdaytime6.html udp异步服务器 int main() {try{创建一个服务器对象来接受传入的客户端请求&#xff0c;并运行io_context对象。 asio::io_context io_context;udp_server server(io_cont…...

    2023/9/29 17:16:49
  21. Golang流媒体实战之一:体验开源项目lal

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 关于《Golang流媒体实战》 因为工作需要&#xff0c;开始了流媒体开发学习&#xff0c;于是打算选择一个Go版本的开源流媒体服务器作为学习方向lal是个不错的…...

    2023/9/15 21:56:34
  22. Autosar诊断-简介和功能概述

    文章目录前言一、OSI-Layers二、首字母缩略词1.Terms-术语2.Abbreviations -缩写三、与其他模块的依赖关系总结前言 DCM SWS描述了AUTOSAR基本软件模块DCM&#xff08;诊断通信管理器&#xff09;的功能、API和配置。DCM模块为诊断服务提供了一个通用API。DCM模块的功能由外部…...

    2023/9/8 20:35:56
  23. 数字藏品的未来及发展趋势

    随着互联网的普及以及数字文化的日益发展&#xff0c;数字藏品作为一种全新的收藏方式正在逐步兴起。数字藏品可以是数字版权、数字艺术品、数字音乐以及数字视频等形式&#xff0c;这些藏品通过数字化技术保存下来&#xff0c;并在互联网上进行传播和交易。数字藏品的发展趋势…...

    2023/9/8 22:02:22
  24. SpringBoot 使用Druid实现数据库连接池并结合AOP实现多数据源

    SpringBoot 使用Druid实现数据库连接池并结合AOP实现多数据源 一. 配置Druid二. 实现多数据源三. 添加注解四. 业务代码五. 测试六. 总结Spring Boot是一个快速开发Spring应用程序的框架,而Druid是一个高性能的数据库连接池,可以提高数据库访问的效率。在Spring Boot中使用Dr…...

    2023/9/22 10:08:17
  25. 高端PACS源码,商业级项目应用PACS源码

    PACS源码 PACS系统源码 医学影像数字化PACS源码&#xff0c;带三维后处理功能。基于VC MSSQL开发&#xff0c;有演示&#xff0c;文档齐全&#xff0c;商业级项目应用源码。 文末获取联系&#xff01; PACS影像存取与传输系统以实现医学影像数字化存储、诊断为核心任务&…...

    2023/9/23 19:16:47
  26. ros海龟画五角星

    海龟转动是逆时针方向 实现:重复移动,转角 z: 1.2566371" //此处72 = 1.2566371 此处216= 3.7699113 一。 首先终端运行roscore 二。 rosrun turtlesim turtlesim_node...

    2023/9/23 14:40:30

最新文章

  1. HBase高阶(一)基础架构及存储原理

    一、HBase介绍 简介 HBase是Hadoop生态系统中的一个分布式、面向列的开源数据库&#xff0c;具有高可伸缩性、高性能和强大的数据处理能力。广泛应用于处理大规模数据集。 HBase是一种稀疏的、分布式、持久的多维排序map 稀疏&#xff1a;对比关系型数据库和非关系型数据库&a…...

    2023/9/29 18:39:34
  2. 关于操作系统与内核科普

    关于操作系统与内核科普 一.什么是操作系统 操作系统是管理计算机硬件与软件资源的计算机程序。它为计算机硬件和软件提供了一种中间层。 操作系统是一种软件&#xff0c;主要目的有三种&#xff1a; 一.管理计算机资源&#xff0c;这些资源包括CPU&#xff0c;内存&#xff0…...

    2023/9/29 18:39:11
  3. 【Java 进阶篇】MySQL多表查询之外连接详解

    在MySQL数据库中&#xff0c;多表查询是一种常见且强大的功能&#xff0c;允许您在多个表之间执行联接操作&#xff0c;从而检索、过滤和组合数据。在本篇博客中&#xff0c;我们将深入探讨多表查询的一种类型&#xff0c;即外连接&#xff08;Outer Join&#xff09;&#xff…...

    2023/9/29 18:38:05
  4. python二维码识别tesseract

    window安装tesseract 下载路径&#xff1a; https://digi.bib.uni-mannheim.de/tesseract/ 选择 双击安装在D:\sore\teeseract-OCR后&#xff1a; 配置环境变量 配置环境变量Path&#xff1a;D:\sore\teeseract-OCR 配置语言包的环境变量TESSDATA_PREFIX&#xff1a; D:\s…...

    2023/9/29 18:37:53
  5. 指示剂Fluorescein Alkynylamino ATP,185971-89-5

    产品简介&#xff1a;FITC-Alkynylamino-ATP是一种具有荧光性质的ATP类似物&#xff0c;可用于细胞和组织中生物发光和荧光检测。其结构中包含荧光染料FITC&#xff0c;通过炔基与含有叠氮基团的分子进行点击化学反应&#xff0c;生成良好荧光强度的产物。 FITC-Alkynylamino-A…...

    2023/9/29 18:36:42
  6. 什么是Local Storage和Session Storage?它们之间有什么区别?

    聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是 Local Storage 和 Session Storage&#xff1f;Local Storage&#xff08;本地存储&#xff09;Session Storage&#xff08;会话存储&#xff09; ⭐ 区别⭐ 示例⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的…...

    2023/9/29 18:36:04
  7. 【数据结构】树、二叉树的概念和二叉树的顺序结构及实现

    目录 前言&#xff1a;一、树的概念及结构1.树的概念2.树的相关概念3.树的存储4.树在实际中的运用 二、二叉树概念及结构1.概念2.特殊的二叉树&#xff08;1&#xff09;满二叉树&#xff08;2&#xff09;完全二叉树 3.二叉树的性质4.二叉树的存储(1)顺序存储(2)链式存储 三、…...

    2023/9/29 18:35:39
  8. 【Java 进阶篇】MySQL数据库范式详解

    范式是数据库设计中的一种理论方法&#xff0c;旨在通过减少数据冗余来提高数据存储的有效性和完整性。在MySQL数据库中&#xff0c;范式设计是一个重要的概念&#xff0c;它有助于组织和管理数据&#xff0c;确保数据的一致性和可靠性。本文将深入探讨数据库范式&#xff0c;包…...

    2023/9/29 18:35:15
  9. 【剑指Offer】7.重建二叉树

    题目 给定节点数为 n 的二叉树的前序遍历和中序遍历结果&#xff0c;请重建出该二叉树并返回它的头结点。 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}&#xff0c;则重建出如下图所示。 提示: 1.vin.length pre.length 2.pre 和 vin 均无重复…...

    2023/9/29 18:35:04
  10. 【C++入门到精通】C++入门 —— set multiset (STL)

    阅读导航 前言一、set简介二、std::set1. std::set简介2. std::set的使用- 基本使用- std::set的模板参数列表- std::set的构造函数- std::set的迭代器- std::set容量与元素访问函数 3. set的所有函数&#xff08;表&#xff09; 三、std::multiset1. std::multiset简介 四、st…...

    2023/9/29 18:33:57
  11. Docker安装部署,拉取镜像-详细过程

    Docker安装 Docker官网&#xff1a;https://www.docker.com/ 国内源安装Docker&#xff0c;使用阿里云的 http://mirrors.aliyun.com 安装必要的工具 [rootdocker-3 ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 获取docker-ce的yum源 [rootdocker-3…...

    2023/9/29 18:33:28
  12. freertos中函数调用和启动第一个任务(栈相关!!!!!!)

    本内容仅就一些较难理解的点讲解&#xff0c;请结合其它文章实用 在函数调用时&#xff0c;m3的处理器使用r0-r3共四个寄存器传参&#xff0c;其余的使用栈传参。 但是&#xff0c;如果传入的参数是全局变量&#xff0c;则不需传参&#xff0c;因为全局变量在函数内部是可见的…...

    2023/9/29 18:32:10
  13. 马尔萨斯《人口原理》读后

    200 多年前的书&#xff0c;很多人都说旧的东西过时了&#xff0c;但我觉得它只是被修正了&#xff0c;内核并不过时。毕竟&#xff0c;静态存量分析这本身就不符合现实&#xff0c;用现在的话说&#xff0c;建模就错了&#xff0c;但马尔萨斯的理论核心并不仅仅是一个模型&…...

    2023/9/29 18:31:49
  14. Docker 容器跨主机通信 - Flannel

    Author&#xff1a;rab 目录 前言一、架构及环境二、服务部署2.1 Etcd 部署2.2 Flannel 部署2.3 Docker 网络配置 三、容器通信验证及路由分析3.1 通信验证3.2 路由转发分析3.3 数据分发分析 总结 前言 今天是中秋佳节&#xff0c;首先在此祝大家“中秋快乐&#xff0c;阖家团…...

    2023/9/29 18:30:40
  15. 机器学习 09 随机森林

    待补充...

    2023/9/29 18:28:43
  16. 嵌入式软硬件

    在这里插入图片描述...

    2023/9/29 18:28:05
  17. Redis 哈希(Hash)数据类型和命令(数据类型 二)

    基本概念 Hash是一个键值对的集合&#xff0c;其中每个键都是唯一的。每个键都可以关联多个字段和值&#xff0c;这使得Hash非常适合存储对象或结构化数据。 常用命令 存储、获取、删除&#xff1a;hset、hget、hdel # 添加键为name值为lin hset student name lin # 获取 h…...

    2023/9/29 18:27:53
  18. Chrome扩展V2到V3的变化

    Chrome扩展manifest V3变化、升级迁移指南_chrome_ZK645945-华为云开发者联盟 (csdn.net) 1.background //V2 "background": "background.js"//V3 "background": {"service_worker": "background.js"} 2.executeScript …...

    2023/9/29 18:27:43
  19. 关于layui upload上传组件上传文件无反应的问题

    最近使用layui upload组件时&#xff0c;碰到了上传文件无反应的问题&#xff0c;感到非常困惑。 因为使用layui upload组件不是一次两次了&#xff0c;之前每次都可以&#xff0c;这次使用同样的配方&#xff0c;同样的姿势&#xff0c;为什么就不行了呢&#xff1f; 照例先…...

    2023/9/29 18:27:20
  20. 函数式编程-Stream流(三更草堂)

    函数式编程-Stream流 1. 概述1.1 为什么学&#xff1f;1.2 函数式编程思想1.2.1 概念1.2.2 优点 2. Lambda表达式 --->可以考虑先构造匿名内部类2.1 概述2.2 核心原则2. 3 基本格式例一&#xff0c;例二:例三例四&#xff08;泛型lambda化&#xff09;例五 2.4 省略规则 尚硅…...

    2023/9/29 18:26:56
  21. 【ChatGPT】AI 大模型的幕后英雄 GPU King NVIDIA : 英伟达公司为什么会成功?—— 人工智能领域的领导者

    【ChatGPT】AI 大模型的幕后英雄 GPU King NVIDIA : 英伟达公司为什么会成功? 文章目录 【ChatGPT】AI 大模型的幕后英雄 GPU King NVIDIA : 英伟达公司为什么会成功?前言第一章:英伟达公司的创立和早期历史第二章:英伟达公司的成功转型第三章:英伟达公司的产品和技术显…...

    2023/9/29 1:52:16
  22. chatgpt在哪用?详谈一下gpt的各方面

    ChatGPT是一种人工智能技术&#xff0c;它可以通过自然语言交互回答各种问题。这种技术已经被广泛应用于各个领域和场景中&#xff0c;帮助人们更好地获取知识和信息。那么&#xff0c;ChatGPT在哪里使用呢&#xff1f;下面我们来探讨一下。 一.chatgpt在哪用 打开任意的浏览器…...

    2023/9/29 12:43:45
  23. 3款好用的IntelliJ IDEA ChatGPT插件

    今天&#xff0c;给大家介绍3款好用的IntelliJ IDEA ChatGPT插件,各有千秋&#xff0c;可以帮我自动写代码&#xff0c;自动改善代码性能和安全并且生成视图对比&#xff0c;以及解释代码&#xff0c;优化代码&#xff0c;生成单元测试用例&#xff0c;更牛逼的是直接可以根据描…...

    2023/9/29 1:59:14
  24. ChatGPT平替工具claude,无需梯子,保姆级安装教程

    前言 最近炒的最火的就属chatgpt了&#xff0c;他的强大功能让每个人忍不住都想去尝试一下&#xff0c;但是高大上的门槛&#xff0c;让很多人望而却步&#xff0c;目前在国内还没有开放ChatGPT的注册和使用&#xff0c;“科学上网”这道难关就难住了许多人&#xff0c;特别是…...

    2023/9/29 12:43:44
  25. ChatGPT 国内镜像网站独家汇总:发现最优秀的人工智能对话体验

    欢迎来到我们的 ChatGPT 镜像网站汇总博客&#xff01;在这个令人激动的人工智能时代&#xff0c;ChatGPT 作为一款顶尖的语言模型&#xff0c;已经引起了全球范围内的热议。但是&#xff0c;您是否曾经为了找到最佳的 ChatGPT 使用体验而苦苦搜寻&#xff1f;别担心&#xff0…...

    2023/9/29 0:40:27
  26. ChatGPT自然语言处理的新里程碑

    ChatGPT中文网是一个面向中国用户的聊天机器人网站&#xff0c;旨在为国内用户提供一个自然的环境、有趣、实用的聊天体验。它使用最新的自然语言处理技术来帮助用户更好地理解他们的聊天对话&#xff0c;还可以帮助用户解决日常生活中的问题&#xff0c;提供有趣的谈话内容以及…...

    2023/9/29 6:44:17
  27. 【利用ChatGPT学习英语口语(包括如何安装插件的详细教程)】

    内容目录 一、插入ChatGPT语音播放插件1. 打开谷歌浏览器——右上角三点——更多工具——扩展程序2. 点击扩展程序三个横杠——打开chrome 应用商店3. 搜索Voice Control for ChatGPT——点击如下图第一个——添加至Chrome——添加扩展程序4. 成功安装&#xff0c;打开ChatGPT有…...

    2023/9/29 6:44:15
  28. 学习笔记:吴恩达ChatGPT提示工程

    以下为个人笔记&#xff0c;原课程网址Short Courses | Learn Generative AI from DeepLearning.AI 01 Introduction 1.1 基础LLM 输入 从前有一只独角兽&#xff0c;输出 它和其他独角兽朋友一起住在森林里输入 法国的首都在哪&#xff1f;输出 法国的首都在哪&#xf…...

    2023/9/29 5:22:15
  29. ChatGPT对未来编程语言发展的影响与展望

    目录 一、引言1.ChatGPT的介绍与背景介绍背景 2.编程语言发展的重要性和挑战重要性挑战 二、ChatGPT在编程领域的应用1.自然语言处理技术在编程中的应用现状2. ChatGPT作为编程辅助工具的潜力与优势 三、ChatGPT对编程语言发展的影响1. 创新编程模式的涌现2. 语言设计与交互方式…...

    2023/9/29 6:44:10
  30. 一文带你读懂什么是ChatGPT?

    文章目录 前言ChatGPT的发展历史ChatGPT初体验ChatGPT的未来展望提高聊天机器人的对话能力改进聊天机器人的生成能力提高聊天机器人的个性化能力加强聊天机器人的安全性 总结福利彩蛋 前言 橙子送书第1期&#xff01;文末参与活动&#xff0c;即可包邮到家。为即将或正在使用Ja…...

    2023/9/29 6:44:07
  31. 我,ChatGPT,打钱

    「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 一、ChatGPT是个啥&#xff1f; chat&#xff1a;表示“聊天”。 GPT&#xff1a;则是G…...

    2023/9/29 6:44:05
  32. ChatGPT|LangChain介绍

    什么是LangChain&#xff1f; 使用ChatGPT大家可能都是知道prompt&#xff0c; &#xff08;1&#xff09;想像一下&#xff0c;如果我需要快速读一本书&#xff0c;想通过本书作为prompt&#xff0c;使用ChatGPT根据书本中来回答问题&#xff0c;我们需要怎么做&#xff1f…...

    2023/9/28 20:38:12
  33. 让chatGPT使用Tensor flow Keras组装Bert,GPT,Transformer

    让chatGPT使用Tensor flow Keras组装Bert,GPT,Transformer implement Transformer Model by Tensor flow Kerasimplement Bert model by Tensor flow Kerasimplement GPT model by Tensor flow Keras 本文主要展示Transfomer, Bert, GPT的神经网络结构之间的关系和差异。网络上…...

    2023/9/29 6:44:00
  34. chatgpt生成【2023高考作文】北京卷一 - 续航

    “续航”一词&#xff0c;原指连续航行&#xff0c;今天在使用中被赋予了新的含义&#xff0c;如为青春续航、科技为经济发展续航等。 请以“续航”为题目&#xff0c;写一篇议论文。 要求&#xff1a;论点明确&#xff0c;论据充实&#xff0c;论证合理&#xff1b;语言流畅&a…...

    2023/9/29 4:04:21
  35. 三分钟搭建一个自己的 ChatGPT (从开发到上线)

    原文链接&#xff1a;https://icloudnative.io/posts/build-chatgpt-web-using-laf/ 视频教程&#xff1a;https://www.bilibili.com/video/BV1cx4y1K7B2/ 前提条件&#xff1a;你需要准备一个 ChatGPT 账号并且生成一个 API Key (这一步可以问 Google ) 云函数教学 首先需要登…...

    2023/9/29 12:43:43
  36. 自制ChatGPT批量生成文章多线程 多Key Python脚本

    本文转载自&#xff1a;自制ChatGPT批量生成文章多线程 多Key Python脚本 更多内容请访问钻芒博客&#xff1a;https://www.zuanmang.net 简单 多线程GPT3.5模型&#xff1a; 特有需求&#xff0c;生成文章后会先保存txt到文章中&#xff0c;程序跑完之后会在生成一个文章汇…...

    2023/9/29 4:22:28
  37. [chatGPT攻略] 如何检测文本内容是否由ChatGPT生成 ?

    [chatGPT攻略] 如何检测文本内容是否由ChatGPT生成 ? 在 ChatGPT 爆火的两个月内&#xff0c;学生就已经自发用这种工具做作业、写论文偷懒&#xff0c;编剧会用它编故事试试出乎人意料的故事走向&#xff0c;文案编辑用它来给自己打工。 在用工具给自己省事这件事上&#xf…...

    2023/9/28 20:43:17
  38. IDEA装上这3款ChatGPT插件,彻底炸裂!!

    今天&#xff0c;给大家介绍3款好用的IntelliJ IDEA ChatGPT插件,各有千秋&#xff0c;可以帮我自动写代码&#xff0c;自动改善代码性能和安全并且生成视图对比&#xff0c;以及解释代码&#xff0c;优化代码&#xff0c;生成单元测试用例&#xff0c;更牛逼的是直接可以根据描…...

    2023/9/29 6:38:17
  39. ChatGPT是什么?

    目录 1.什么是ChatGPT? 2.ChatGPT的前世今生 3.ChatGPT收费标准 4.ChatGPT能做什么 1.什么是ChatGPT? ChatGPT英文全名&#xff1a;Chat Generative Pre-trained Transformer&#xff0c;中文名&#xff1a;聊天生成型预训练变换模型&#xff09;, 顾名思义&#xff0c;…...

    2023/9/29 6:43:45
  40. chatgpt赋能python:如何取出带有4的整数

    如何取出带有4的整数 Python是一门功能强大的编程语言&#xff0c;可以轻松解决复杂的编程问题。在本文中&#xff0c;我们将介绍如何使用Python编程语言从一个整数列表中取出所有带有4的整数。我们将从介绍如何创建一个整数列表开始&#xff0c;然后编写Python代码以实现我们…...

    2023/9/29 5:20:37
  41. ChatGPT+智能家居在AWE引热议 OpenCPU成家电产业智能化降本提速引擎

    作为家电行业的风向标和全球三大消费电子展之一&#xff0c;4月27日-30日&#xff0c;以“智科技、创未来”为主题的AWE 2023在上海新国际博览中心举行&#xff0c;本届展会展现了科技、场景等创新成果&#xff0c;为我们揭示家电与消费电子的发展方向。今年展馆规模扩大至14个…...

    2023/9/29 6:43:40
  42. 聊聊开源的类ChatGPT工作——ChatGLM

    这是”聊聊开源的类ChatGPT工作“的第二篇&#xff0c;写第一篇[7]的时候&#xff0c;当时恰巧MOSS开源&#xff0c;就顺手写了MOSS。但要问目前中文领域的“开源”的语言模型谁更强&#xff0c;公认的还是ChatGLM-6B&#xff08;以下简称ChatGLM&#xff09;。 下面是官方对C…...

    2023/9/29 6:43:38
  43. 我发现3个国内一直能用的免费版ChatGPT 免登免注册无广告 再不赶紧保存就没啦!

    &#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…...

    2023/9/29 6:43:35
  44. 我用ChatGPT写2023高考语文作文(三):新课标I卷

    2023年 新课标I卷 适用地区&#xff1a;山东、福建、湖北、江苏、广东、湖南、河北、浙江 好的故事&#xff0c;可以帮我们更好地表达和沟通&#xff0c;可以触动心灵、启迪智慧&#xff1b;好的故事&#xff0c;可以改变一个人的命运&#xff0c;可以展现一个民族的形象……故…...

    2023/9/29 6:43:33
  45. 漫画:ChatGPT的应用场景有哪些?

    第一&#xff0c;文案创作 ChatGPT可以通过用户输入的提示&#xff0c;创作出相应的文本内容。 媒体作者可以给ChatGPT提出要求&#xff0c;让ChatGPT根据某一主题生成的媒体文案。 广告策划人员可以把要推广的商品信息输入给ChatGPT&#xff0c;让ChatGPT针对此商品生成一段广…...

    2023/9/29 6:43:31
  46. Openai+Deeplearning.AI: ChatGPT Prompt Engineering(六)

    想和大家分享一下最近学习的Deeplearning.AI和openai联合打造ChatGPT Prompt Engineering在线课程.以下是我写的关于该课程的前五篇博客&#xff1a; ChatGPT Prompt Engineering(一)ChatGPT Prompt Engineering(二)ChatGPT Prompt Engineering(三)ChatGPT Prompt Engineering…...

    2023/9/29 6:43:28
  47. chatgpt赋能python:Python在一个程序里调用另一段程序

    Python在一个程序里调用另一段程序 随着Python程序的复杂度越来越高&#xff0c;有时候一个程序难以处理所有的任务。这时候我们可能需要将任务拆分成多个脚本来执行。但是&#xff0c;这会导致代码的复杂性增加&#xff0c;同时也会增加可读性和维护成本。这时候&#xff0c;…...

    2023/9/29 6:43:26
  48. 人人实现ChatGPT自由,手把手教你零撸部署自己聊天私服

    我们知道chat gpt最近非常的火爆&#xff0c;朋友圈啊&#xff0c;短视频到处都是chat gpt身影。 但是网上看到各种教程资源&#xff0c;都是不是百分一百的免费&#xff0c;毫无保留教给你的&#xff0c;要么是卖账号 要么是割韭菜的。其实没有什么可以学习的&#xff0c;使用…...

    2023/9/29 6:43:23
  49. ChatGpt写高考作文——2023北京卷

    题目一&#xff1a; “续航”一词&#xff0c;原指连续航行&#xff0c;今天在使用中被赋予了新的含义&#xff0c;如为青春续航、科技为经济发展续航等。 请以“续航”为题目&#xff0c;写一篇议论文。 要求&#xff1a;论点明确&#xff0c;论据充实&#xff0c;论证合理&a…...

    2023/9/29 6:43:21
  50. ChatGPT时代:ChatGPT全能应用一本通

    摘要 ChatGPT是一款开创性的人工智能语言模型&#xff0c;将人类语言理解和生成的能力推向了新的高度。作为一个全能的应用&#xff0c;ChatGPT能够在各个领域提供帮助和指导&#xff0c;从教育到医疗&#xff0c;从娱乐到商业。本文将探讨ChatGPT时代的到来&#xff0c;以及其…...

    2023/9/29 1:58:24
  51. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  52. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  53. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  54. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  55. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  56. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  57. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  58. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  59. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  60. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  61. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  62. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  63. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  64. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  65. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  66. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  67. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  68. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  69. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  70. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57