首页 > 解决方案 > 编译后的 c 可执行文件如何知道如何表示特定内存地址处的一系列位?

问题描述

我知道在编译期间 C 变量被替换为内存地址,并且带有类型信息的符号表无法在编译后继续存在。

我试图了解在编程执行期间如何存储和检索(表示)数据。我了解变量映射到内存地址,并且我的预编译代码指定了类型(位数以及如何读取它们)。但是,如果在代码编译成可执行文件时类型丢失了,机器代码(.exe)如何知道特定内存地址的数据大小以及如何表示这些位?

我见过许多类似的问题,但答案总是被认为 CPU 不关心或不知道这些位系列打算代表什么。然而,这并不是我要问的。我想知道可执行程序如何知道重要的位数以及如果类型(整数、字符、双精度、字符串或位字段)未知,如何读取(表示)它们?

标签: ccompilation

解决方案


编译器生成机器指令,专门执行它想要的操作。(这可以分多个步骤完成,用编译器自己的语言生成中间代码,然后将其转换为汇编语言,然后将其汇编为机器指令。)

机器指令有特定的形式,例如:

  • 从地址 1234 加载 32 位到寄存器 8
  • 将寄存器 7 中的 64 位存储到地址 4320 中。
  • 从当前程序计数器之外 9000 字节的内存中将 32 位加载到寄存器 3 中。
  • 将寄存器 4 中的 8 位存储到堆栈指针之外 124 字节的内存中。
  • 从寄存器 3 添加到寄存器 4。
  • 比较寄存器 3 和寄存器 4。
  • 如果最后一个比较指示有符号大于,则分支。
  • 将寄存器 4 的内容右移 5 位(“逻辑上”,填充零)。
  • 将寄存器 4 的内容右移 5 位(“算术”,复制符号位)。

因此,如果您的程序正在读取一个char值,编译器会生成一条指令来加载 8 位。如果您的程序正在读取一个int值,编译器会生成一条指令来加载 32 位(假设int在您的 C 实现中是 32 位)。

在许多指令中,处理器不需要知道数据类型是什么。加载 32 位就是加载 32 位,无论这些位是int指针还是指针。即使在添加有符号或无符号整数时,也设计了位模式,以便一个加法指令适用于有符号或无符号整数。

有些指令需要知道数据类型。有符号和无符号整数不能用相同的指令进行比较。对于有符号的 32 位int,位 0xffffffff 表示 -1,小于 0(位 0x00000000),但对于无符号int位,这些位表示大于零的 4,294,967,295。因此,对于x < y,如果它们是有符号的,编译器会生成一条指令,如果它们是无符号的,则编译器会生成一条不同的指令。(实际上,对于多种数据类型可能有一个比较指令,但它会产生多个位,指示各种可能的结果,另一条指令根据结果测试这些位和分支。如果比较指令不是针对数据类型定制的,那么测试和分支指令是。)


推荐阅读