CSAPP第二章 信息表示与处理(二) 整数表示

2.2 整数表示

C语言整数取值范围

C 数据类型 32 位系统取值范围 64 位系统取值范围
[signed] char −27-2^{7}−27 ~ 27−12^{7} - 127−1 −27-2^{7}−27 ~ 27−12^{7} - 127−1
unsigned char 000 ~ 28−12^{8} - 128−1 000 ~ 28−12^{8} - 128−1
short −215-2^{15}−215 ~ 215−12^{15} - 1215−1 −215-2^{15}−215 ~ 215−12^{15} - 1215−1
unsigned short 000 ~ 216−12^{16} - 1216−1 000 ~ 216−12^{16} - 1216−1
int −231-2^{31}−231 ~ 231−12^{31} - 1231−1 −231-2^{31}−231 ~ 231−12^{31} - 1231−1
unsigned int 000 ~ 232−12^{32} - 1232−1 000 ~ 232−12^{32} - 1232−1
long −231-2^{31}−231 ~ 231−12^{31} - 1231−1 −263-2^{63}−263 ~ 263−12^{63} - 1263−1
unsigned long 000 ~ 232−12^{32} - 1232−1 000 ~ 264−12^{64} - 1264−1
long long −263-2^{63}−263 ~ 263−12^{63} - 1263−1 −263-2^{63}−263 ~ 263−12^{63} - 1263−1
unsigned long long 000 ~ 264−12^{64} - 1264−1 000 ~ 264−12^{64} - 1264−1
  • char = 8 位
  • short = 16 位
  • int = 32 位
  • long long = 64 位
  • 对于 有符号 n 位整数 :取值范围为
    −2n−1到2n−1−1 -2^{n-1} \quad \text{到} \quad 2^{n-1} - 1 −2n−1到2n−1−1

  • 对于 无符号 n 位整数 :取值范围为
    0到2n−1 0 \quad \text{到} \quad 2^{n} - 1 0到2n−1

  • 唯一随机器位数变化的是 long

    • 32 位系统:long 为 32 位 → 范围含 2312^{31}231
    • 64 位系统(Linux/macOS):long 为 64 位 → 范围含 2632^{63}263

取值范围不是对称的,负数的范围比整 数的范围大1。

C 标准 (如 ISO/IEC 9899)不规定具体位数 ,而是规定每种整型类型必须至少支持的最小取值范围。这是为了保证可移植性。

C 数据类型 最小值 最大值
signed char -127 (−27+1-2^7 + 1−27+1) 127 (27−12^7 - 127−1)
unsigned char 0 255 (28−12^8 - 128−1)
short -32 767 (−215+1-2^{15} + 1−215+1) 32 767 (215−12^{15} - 1215−1)
unsigned short 0 65 535 (216−12^{16} - 1216−1)
int -32 767 (−215+1-2^{15} + 1−215+1) 32 767 (215−12^{15} - 1215−1)
unsigned 0 65 535 (216−12^{16} - 1216−1)
long -2 147 483 647 (−231+1-2^{31} + 1−231+1) 2 147 483 647 (231−12^{31} - 1231−1)
unsigned long 0 4 294 967 295 (232−12^{32} - 1232−1)
int32_t -2 147 483 648 (−231-2^{31}−231) 2 147 483 647 (231−12^{31} - 1231−1)
uint32_t 0 4 294 967 295 (232−12^{32} - 1232−1)
int64_t -9 223 372 036 854 775 808 (−263-2^{63}−263) 9 223 372 036 854 775 807 (263−12^{63} - 1263−1)
uint64_t 0 18 446 744 073 709 551 615 (264−12^{64} - 1264−1)

!NOTE

C 和 C++ 都支持有符号(默认)和无符号数。Java 只支持有符号数。

无符号整数

对于一个 www 位的整数数据类型,位向量表示为 x⃗=xw−1,xw−2,...,x0\vec{x} = x_{w-1}, x_{w-2}, \\dots, x_0x =xw−1,xw−2,...,x0。将位向量看作二进制表示的数,即可获得其无符号表示。

编码的定义

使用函数 B2UwB2U_wB2Uw (Binary to Unsigned) 来表示:

B2U_w(\vec{x}) \doteq \sum_{i=0}^{w-1} x_i 2^i

$$

  • xix_ixi :位向量中的第 iii 位,取值为 0 或 1。
  • 2i2^i2i :第 iii 位对应的权重(由 2 的幂组成)。

映射示例

通过将每个值为 1 的位对应的权重相加得到最终的十进制数值:

位向量 x3,x2,x1,x0x_3, x_2, x_1, x_0x3,x2,x1,x0 计算过程 十进制结果
000100010001 0⋅23+0⋅22+0⋅21+1⋅20=0+0+0+10\cdot2^3 + 0\cdot2^2 + 0\cdot2^1 + 1\cdot2^0 = 0+0+0+10⋅23+0⋅22+0⋅21+1⋅20=0+0+0+1 1
010101010101 0⋅23+1⋅22+0⋅21+1⋅20=0+4+0+10\cdot2^3 + 1\cdot2^2 + 0\cdot2^1 + 1\cdot2^0 = 0+4+0+10⋅23+1⋅22+0⋅21+1⋅20=0+4+0+1 5
101110111011 1⋅23+0⋅22+1⋅21+1⋅20=8+0+2+11\cdot2^3 + 0\cdot2^2 + 1\cdot2^1 + 1\cdot2^0 = 8+0+2+11⋅23+0⋅22+1⋅21+1⋅20=8+0+2+1 11
111111111111 1⋅23+1⋅22+1⋅21+1⋅20=8+4+2+11\cdot2^3 + 1\cdot2^2 + 1\cdot2^1 + 1\cdot2^0 = 8+4+2+11⋅23+1⋅22+1⋅21+1⋅20=8+4+2+1 15

取值范围

无符号数能够表示的数值范围由位数 www 决定:

  • 最小值 (UMinwUMin_wUMinw) :位向量为 00...000\\dots000...0 时,对应整数 0
  • 最大值 (UMaxwUMax_wUMaxw) :位向量为 11...111\\dots111...1 时,对应整数 2w−12^w - 12w−1。
    • 公式:UMaxw≐∑i=0w−12i=2w−1UMax_w \doteq \sum_{i=0}^{w-1} 2^i = 2^w - 1UMaxw≐∑i=0w−12i=2w−1
    • 例如:4位最大值为 24−1=152^4 - 1 = 1524−1=15。

编码的唯一性 (双射)

函数 B2UwB2U_wB2Uw 是一个双射 (Bijection)

  • 含义
    1. 每个长度为 www 的位向量都有唯一一个整数值与之对应。
    2. 反之,在 0∼2w−10 \sim 2^w-10∼2w−1 范围内的每一个整数,都有唯一一个长度为 www 的位模式与之对应。
  • 反函数 :U2BwU2B_wU2Bw (Unsigned to Binary),即将十进制无符号数转换回二进制位模式。

补码

补码是计算机中表示有符号整数(Signed Integers)最常见的形式。其核心逻辑是将字(word)的最高有效位(MSB)解释为负权重(Negative Weight)

对于一个 www 位的位向量 x⃗=xw−1,xw−2,...,x0\vec{x} = x_{w-1}, x_{w-2}, \\dots, x_0x =xw−1,xw−2,...,x0,其补码值通过函数 B2TwB2T_wB2Tw(Binary to Two's-complement)计算:

B2Tw(x⃗)≐−xw−1⋅2w−1+∑i=0w−2xi⋅2iB2T_w(\vec{x}) \doteq -x_{w-1} \cdot 2^{w-1} + \sum_{i=0}^{w-2} x_i \cdot 2^iB2Tw(x )≐−xw−1⋅2w−1+i=0∑w−2xi⋅2i

  • 符号位 :最高位 xw−1x_{w-1}xw−1 被称为符号位。
    • 当 xw−1=1x_{w-1} = 1xw−1=1 时,表示值为负(权重为 −2w−1-2^{w-1}−2w−1)。
    • 当 xw−1=0x_{w-1} = 0xw−1=0 时,表示值为非负。

计算示例 (w=4w=4w=4):

位模式 权重分解计算 结果值
[0001] −0⋅23+0⋅22+0⋅21+1⋅20=1-0\cdot2^3 + 0\cdot2^2 + 0\cdot2^1 + 1\cdot2^0 = 1−0⋅23+0⋅22+0⋅21+1⋅20=1 1
[0101] −0⋅23+1⋅22+0⋅21+1⋅20=4+1-0\cdot2^3 + 1\cdot2^2 + 0\cdot2^1 + 1\cdot2^0 = 4+1−0⋅23+1⋅22+0⋅21+1⋅20=4+1 5
[1011] −1⋅23+0⋅22+1⋅21+1⋅20=−8+2+1-1\cdot2^3 + 0\cdot2^2 + 1\cdot2^1 + 1\cdot2^0 = -8+2+1−1⋅23+0⋅22+1⋅21+1⋅20=−8+2+1 -5
[1111] −1⋅23+1⋅22+1⋅21+1⋅20=−8+4+2+1-1\cdot2^3 + 1\cdot2^2 + 1\cdot2^1 + 1\cdot2^0 = -8+4+2+1−1⋅23+1⋅22+1⋅21+1⋅20=−8+4+2+1 -1

由于最高位的负权重特性,补码表示的数值范围具有不对称性。

  • 最小值 (TMinwTMin_wTMinw) :位向量为 [10...0]
    • 公式:TMinw=−2w−1TMin_w = -2^{w-1}TMinw=−2w−1
  • 最大值 (TMaxwTMax_wTMaxw) :位向量为 [01...1]
    • 公式:TMaxw=∑i=0w−22i=2w−1−1TMax_w = \sum_{i=0}^{w-2} 2^i = 2^{w-1} - 1TMaxw=∑i=0w−22i=2w−1−1

以 w=4w=4w=4 为例:

  • TMin4=−23=−8TMin_4 = -2^3 = -8TMin4=−23=−8
  • TMax4=23−1=7TMax_4 = 2^3 - 1 = 7TMax4=23−1=7

函数 B2TwB2T_wB2Tw 是一个双射。这意味着:

  1. 每个长度为 www 的位模式对应唯一一个 TMinTMinTMin 到 TMaxTMaxTMax 之间的数字。
  2. 反之,在该范围内的每个数字也都有唯一的 www 位补码编码(由反函数 T2BwT2B_wT2Bw 定义)。

这一特性保证了有符号数在计算机底层表示的确定性,与无符号数原理相似。

!NOTE

补码(Two's Complement)与无符号(Unsigned)编码最本质的区别在于对位向量**最高有效位(MSB)**的权重解释。

对于一个 www 位的位向量 x⃗=xw−1,xw−2,...,x0\vec{x} = x_{w-1}, x_{w-2}, \\dots, x_0x =xw−1,xw−2,...,x0

  • 无符号编码 (B2UwB2U_wB2Uw) :所有位均具有正权重
    B2Uw(x⃗)=∑i=0w−1xi⋅2i B2U_w(\vec{x}) = \sum_{i=0}^{w-1} x_i \cdot 2^i B2Uw(x )=i=0∑w−1xi⋅2i
    其最高位的权重为:+2w−1+2^{w-1}+2w−1

  • 补码编码 (B2TwB2T_wB2Tw) :最高有效位具有负权重
    B2Tw(x⃗)=−xw−1⋅2w−1+∑i=0w−2xi⋅2i B2T_w(\vec{x}) = \mathbf{-x_{w-1} \cdot 2^{w-1}} + \sum_{i=0}^{w-2} x_i \cdot 2^i B2Tw(x )=−xw−1⋅2w−1+i=0∑w−2xi⋅2i
    其最高位的权重为:−2w−1-2^{w-1}−2w−1

当位模式为 1011 时,两者的解释差异如下:

编码方式 最高位权重 计算过程 最终数值
无符号 +23=8+2^3 = 8+23=8 8+0+2+18 + 0 + 2 + 18+0+2+1 11
补码 −23=−8-2^3 = -8−23=−8 −8+0+2+1-8 + 0 + 2 + 1−8+0+2+1 -5

!IMPORTANT

练习题 2.17

假设 w=4w=4w=4,我们能给每个可能的十六进制数字赋予一个数值,假设用一个无符号或者补码表示。请根据这些表示,通过写出等式 (2.1) 和等式 (2.3) 所示的求和公式中的 2 的非零次幂,填写下表:

答案

根据无符号编码 B2UwB2U_wB2Uw(全部权重为正)和补码编码 B2TwB2T_wB2Tw(最高位权重为负)的定义,完成表格如下:

十六进制 二进制 \vec{x} 无符号 B2U_4(\vec{x}) 补码 B2T_4(\vec{x})
0xE [1110] 2^3 + 2^2 + 2^1 = 14 -2^3 + 2^2 + 2^1 = -2
0x0 [0000] 0 0
0x5 [0101] 2^2 + 2^0 = 5 2^2 + 2^0 = 5
0x8 [1000] 2^3 = 8 -2^3 = -8
0xD [1101] 2^3 + 2^2 + 2^0 = 13 -2^3 + 2^2 + 2^0 = -3
0xF [1111] 2^3 + 2^2 + 2^1 + 2^0 = 15 -2^3 + 2^2 + 2^1 + 2^0 = -1

在计算机系统中,针对不同的字长 www,无符号数和补码表示的范围和特殊数值如下表所示:

数值符号 数值含义 字长 w=8w=8w=8 字长 w=16w=16w=16 字长 w=32w=32w=32 字长 w=64w=64w=64
UMaxwUMax_wUMaxw 无符号最大值 0xFF 255 0xFFFF 65 535 0xFFFFFFFF 4 294 967 295 0xFFFFFFFFFFFFFFFF 18 446 744 073 709 551 615
TMinwTMin_wTMinw 补码最小值 0x80 -128 0x8000 -32 768 0x80000000 -2 147 483 648 0x8000000000000000 -9 223 372 036 854 775 808
TMaxwTMax_wTMaxw 补码最大值 0x7F 127 0x7FFF 32 767 0x7FFFFFFF 2 147 483 647 0x7FFFFFFFFFFFFFFF 9 223 372 036 854 775 807
-1 补码表示的 -1 0xFF 0xFFFF 0xFFFFFFFF 0xFFFFFFFFFFFFFFFF
0 数值 0 0x00 0x0000 0x00000000 0x0000000000000000
  • 补码范围的不对称性 :补码能够表示的负数比正数多一个,即 ∣TMin∣=∣TMax∣+1|TMin| = |TMax| + 1∣TMin∣=∣TMax∣+1。这是因为一半的位模式(符号位为 1)表示负数,另一半位模式中,符号位为 0 的一部分表示非负数(包含 0),导致正数比负数少一个。
  • UMaxUMaxUMax 与 TMaxTMaxTMax 的关系 :最大无符号数值大约是补码最大值的两倍再加 1,公式为 UMaxw=2TMaxw+1UMax_w = 2TMax_w + 1UMaxw=2TMaxw+1。
  • 特殊位模式
    • 全 1 序列 :在补码表示中对应数值 −1-1−1,而在无符号表示中对应最大值 UMaxUMaxUMax。
    • 全 0 序列 :在两种表示方式中都对应数值 000。

虽然 C 语言标准并未强制要求使用补码表示有符号整数,但几乎所有现代机器都采用补码。

  • <limits.h> :定义了一组宏来限制不同整型数据的取值范围,如 INT_MAXINT_MINUINT_MAX
  • 可移植性建议:程序员应尽量编写假设典型的取值范围(如补码范围)的代码,以保证在大量机器和编译器上可移植。

与 C 语言不同,Java 对整数类型的取值范围和表示方法有非常严格且统一的要求:

  • 强制补码表示:Java 要求采用补码形式表示整数。
  • 固定范围:其取值范围在所有机器上表现完全一致。
  • 单字节类型 :Java 中的单字节数据类型称为 byte(对应 8 位补码),而非 char

!NOTE

在 C 语言中,某些基本数据类型(如 long)在不同机器上的取值范围可能不同,这会影响程序的可移植性 。为了解决这一问题,ISO C99 标准在 <stdint.h> 中引入了一类确定宽度的整数类型。

  • 类型声明 :形式为 intN_t(有符号)和 uintN_t(无符号),其中 NNN 的典型值为 8、16、32 和 64。
    • 例如:uint16_t 声明一个 16 位无符号变量;int32_t 声明一个 32 位有符号变量。
  • 范围宏定义 :这些类型对应一组宏,定义了每个 NNN 对应的最小值和最大值,如 INTN_MININTN_MAXUINTN_MAX

由于这些类型的具体实现与系统相关,打印它们时需要使用 <inttypes.h> 中定义的,以确保生成的格式字符串在不同编译环境下均正确。

  • 使用方式 :通过宏(如 PRId32PRIu64)来扩展格式化字符串。

  • 示例代码

    c 复制代码
    int32_t x;
    uint64_t y;
    // 使用宏进行跨平台格式化打印
    printf("x = %" PRId32 ", y = %" PRIu64 "\n", x, y);
  • 编译原理 :在 64 位程序中,宏 PRId32 通常展开为 "d",而 PRIu64 展开为 "lu"。C 预处理器会将相邻的字符串常量拼接在一起,最终生成类似 "x = %d, y = %lu\n" 的字符串。

反码

除了最高有效位的权重是 −(2w−1−1)-(2^{w-1}-1)−(2w−1−1) 而不是 −2w−1-2^{w-1}−2w−1,它和补码是一样的。

B2Ow(x⃗)≐−xw−1(2w−1−1)+∑i=0w−2xi2i B2O_w(\vec{x}) \doteq -x_{w-1}(2^{w-1}-1) + \sum_{i=0}^{w-2} x_i 2^i B2Ow(x )≐−xw−1(2w−1−1)+i=0∑w−2xi2i

对于位向量 101110111011

  • w=4w = 4w=4
  • x3=1x_3 = 1x3=1
  • x2=0,x1=1,x0=1x_2 = 0, x_1 = 1, x_0 = 1x2=0,x1=1,x0=1

B2O4(1011)=−1⋅(24−1−1)+(0⋅22+1⋅21+1⋅20)=−1⋅(23−1)+(0+2+1)=−1⋅(8−1)+3=−7+3=−4 \begin{aligned} B2O_4(1011) &= -1 \cdot (2^{4-1} - 1) + (0 \cdot 2^2 + 1 \cdot 2^1 + 1 \cdot 2^0) \\ &= -1 \cdot (2^3 - 1) + (0 + 2 + 1) \\ &= -1 \cdot (8 - 1) + 3 \\ &= -7 + 3 \\ &= -4 \end{aligned} B2O4(1011)=−1⋅(24−1−1)+(0⋅22+1⋅21+1⋅20)=−1⋅(23−1)+(0+2+1)=−1⋅(8−1)+3=−7+3=−4

原码

最高有效位是符号位,用来确定剩下的位应该取负权重还是正权重。

B2Sw(x⃗)≐(−1)xw−1⋅(∑i=0w−2xi2i) B2S_w(\vec{x}) \doteq (-1)^{x_{w-1}} \cdot \left( \sum_{i=0}^{w-2} x_i 2^i \right) B2Sw(x )≐(−1)xw−1⋅(i=0∑w−2xi2i)

对于位向量 101110111011

  • w=4w = 4w=4
  • 最高位(符号位)x3=1x_3 = 1x3=1
  • 剩余数值位 x2=0,x1=1,x0=1x_2 = 0, x_1 = 1, x_0 = 1x2=0,x1=1,x0=1

计算步骤:

  1. 确定符号部分:
    (−1)x3=(−1)1=−1(-1)^{x_3} = (-1)^1 = -1(−1)x3=(−1)1=−1
  2. 确定数值部分(绝对值):
    ∑i=02xi2i=(0⋅22+1⋅21+1⋅20)=0+2+1=3\sum_{i=0}^{2} x_i 2^i = (0 \cdot 2^2 + 1 \cdot 2^1 + 1 \cdot 2^0) = 0 + 2 + 1 = 3i=0∑2xi2i=(0⋅22+1⋅21+1⋅20)=0+2+1=3
  3. 组合结果:
    B2S4(1011)=−1⋅3=−3B2S_4(1011) = -1 \cdot 3 = -3B2S4(1011)=−1⋅3=−3

反码和原码对于数字 0 都有两种不同的编码方式:

  • +0 的表示: 两种方法都将 00...000\\dots000...0 解释为 +0+0+0。
  • -0 的表示: * 在原码 中表示为 10...010\\dots010...0
    • 反码 中表示为 11...111\\dots111...1

有符号与无符号的转换

C语言允许不同数字数据类型间强制类型转换,例如 unsignedint 之间的转换。从位级角度看,转换不改变位模式仅改变对位的解释方式

c 复制代码
// 强制类型转换仅改变解释方式,位表示不变。
// short 转 unsigned short
short int       v = -12345;
unsigned short  uv = (unsigned short) v;
printf("v = %d, uv = %u\n", v, uv);
// 输出 v = -12345, uv = 53191
// -12345 的 16 位补码表示与 53191 的 16 位无符号表示位模式完全相同。
c 复制代码
// 强制类型转换仅改变解释方式,位表示不变。
unsigned u = 4294967295u;  /* UMax_32 */
int tu = (int) u;
printf("u = %u, tu = %d\n", u, tu);
// u = 4294967295, tu = -1

为了精确描述这种位模式不变的转换,我们定义以下函数:

UUU 无符号数、TTT补码表示、 BBB 位表示

  • U2Bw(x)U2B_w(x)U2Bw(x) :将整数 xxx 映射为 www 位无符号数的位表示。
  • T2Bw(x)T2B_w(x)T2Bw(x) :将整数 xxx 映射为 www 位补码形式的位表示。
  • B2Uw(u)B2U_w(u)B2Uw(u) :将位向量 uuu 转换为无符号数。
  • B2Tw(u)B2T_w(u)B2Tw(u) :将位向量 uuu 转换为补码形式的有符号数。

补码转无符号数 (T2UT2UT2U)
T2Uw(x)≐B2Uw(T2Bw(x)) T2U_w(x) \doteq B2U_w(T2B_w(x)) T2Uw(x)≐B2Uw(T2Bw(x))

输入范围:TMinw∼TMaxwTMin_w \sim TMax_wTMinw∼TMaxw,结果范围:0∼UMaxw0 \sim UMax_w0∼UMaxw。

无符号数转补码 (U2TU2TU2T)
U2Tw(x)≐B2Tw(U2Bw(x)) U2T_w(x) \doteq B2T_w(U2B_w(x)) U2Tw(x)≐B2Tw(U2Bw(x))

输入范围:0∼UMaxw0 \sim UMax_w0∼UMaxw,结果范围:TMinw∼TMaxwTMin_w \sim TMax_wTMinw∼TMaxw。

!IMPORTANT

填写下列描述函数 T2U4T2U_4T2U4 的表格。该函数将 444 位补码数值 xxx 转换为对应的无符号数值。

通过保持底层位模式不变,将补码解释为无符号数。对于 4 位字长,2w=24=162^w = 2^4 = 162w=24=16。当 x<0x < 0x<0 时,T2U4(x)=x+16T2U_4(x) = x + 16T2U4(x)=x+16。

位模式 (十六进制) 补码数值 xxx 无符号数值 T2U4(x)T2U_4(x)T2U4(x) 转换逻辑
0x8 −8-8−8 8 −8+16=8-8 + 16 = 8−8+16=8 (TMinT_MinTMin)
0xD −3-3−3 13 −3+16=13-3 + 16 = 13−3+16=13
0xE −2-2−2 14 −2+16=14-2 + 16 = 14−2+16=14
0xF −1-1−1 15 −1+16=15-1 + 16 = 15−1+16=15 (UMaxU_MaxUMax)
0x0 000 0 数值保持不变
0x5 555 5 数值保持不变
  • 对于非负数 (x≥0x \ge 0x≥0),数值保持不变。
  • 对于负数 (x<0x < 0x<0),无符号值等于补码值加上 2w2^w2w。

补码转无符号数

对于满足 TMinw≤x≤TMaxwTMin_w \le x \le TMax_wTMinw≤x≤TMaxw 的补码数值 xxx,其转换为无符号数的公式为:
T2Uw(x)={x+2w,x<0x,x≥0(2.5) T2U_w(x) = \begin{cases} x + 2^w, & x < 0 \\ x, & x \ge 0 \end{cases} \qquad (2.5) T2Uw(x)={x+2w,x,x<0x≥0(2.5)

  • 负数转换 :T2U16(−12345)=−12345+216=53191T2U_{16}(-12345) = -12345 + 2^{16} = 53191T2U16(−12345)=−12345+216=53191
  • 边界情况 :T2Uw(−1)=−1+2w=UMaxwT2U_w(-1) = -1 + 2^w = UMax_wT2Uw(−1)=−1+2w=UMaxw

通过比较无符号数解码公式 B2UB2UB2U 和补码解码公式 B2TB2TB2T,可以发现:

对于位模式 x⃗\vec{x}x ,从 000 到 w−2w-2w−2 位的加权和在两种表示中是完全相同的,它们会在减法中互相抵消

唯一不同的位是最高有效位(MSB)

  • 无符号差值计算 :B2Uw(x⃗)−B2Tw(x⃗)=xw−1(2w−1−(−2w−1))=xw−12wB2U_w(\vec{x}) - B2T_w(\vec{x}) = x_{w-1}(2^{w-1} - (-2^{w-1})) = x_{w-1} 2^wB2Uw(x )−B2Tw(x )=xw−1(2w−1−(−2w−1))=xw−12w

由此得到转换关系式:
B2Uw(T2Bw(x))=T2Uw(x)=x+xw−12w(2.6) B2U_w(T2B_w(x)) = T2U_w(x) = x + x_{w-1} 2^w \qquad (2.6) B2Uw(T2Bw(x))=T2Uw(x)=x+xw−12w(2.6)

在补码表示中,位 xw−1x_{w-1}xw−1 决定了 xxx 的符号:

  • 如果 x<0x < 0x<0(负数),则 xw−1=1x_{w-1} = 1xw−1=1,值增加 2w2^w2w。
  • 如果 x≥0x \ge 0x≥0(非负数),则 xw−1=0x_{w-1} = 0xw−1=0,值保持不变。

无符号数转补码

对于满足 0≤u≤UMaxw0 \le u \le UMax_w0≤u≤UMaxw 的无符号数 uuu,其转换为补码 U2Tw(u)U2T_w(u)U2Tw(u) 的关系如下:
U2Tw(u)={u,u≤TMaxwu−2w,u>TMaxw(2.7) U2T_w(u) = \begin{cases} u, & u \le TMax_w \\ u - 2^w, & u > TMax_w \end{cases} \qquad (2.7) U2Tw(u)={u,u−2w,u≤TMaxwu>TMaxw(2.7)

设位向量为 u⃗\vec{u}u ,其补码表示为 U2Tw(u)U2T_w(u)U2Tw(u)。结合无符号和补码的定义公式,可以得出:

U2Tw(u)=−uw−12w−1+u(2.8) U2T_w(u) = -u_{w-1} 2^{w-1} + u \qquad (2.8) U2Tw(u)=−uw−12w−1+u(2.8)

在 uuu 的无符号表示中,最高位 uw−1u_{w-1}uw−1 决定了 uuu 是否大于 TMaxw=2w−1−1TMax_w = 2^{w-1} - 1TMaxw=2w−1−1:

  • 当 uw−1=0u_{w-1} = 0uw−1=0 时,uuu 在补码表示的范围内。
  • 当 uw−1=1u_{w-1} = 1uw−1=1 时,uuu 超出了补码的正数范围,转换后变为负数。

无符号数与补码之间的相互转换,在位级(Bit-level)表示上是不变的,只是解释位的方式发生了变化。

转换方向 范围条件 结果
通用 0≤x≤TMaxw0 \le x \le TMax_w0≤x≤TMaxw 保持不变
T2U (补码转无符号) x<0x < 0x<0 x+2wx + 2^wx+2w
U2T (无符号转补码) u>TMaxwu > TMax_wu>TMaxw u−2wu - 2^wu−2w

C中的有符号数与无符号数

默认是有符号

C 语言支持所有整型数据类型的有符号无符号 运算。尽管 C 语言标准没有指定有符号数要采用某一种表示,但是几乎所有的机器都使用补码

通常,大多数数字都默认为是有符号的 。例如,声明 123450x1A2B 时,它们被视为有符号数。

要创建无符号常量,必须加上后缀字符 Uu(如 12345U0x1A2Bu)。

类型转换不改变底层定义

C 语言允许无符号数和有符号数之间的转换。大多数系统遵循的原则是:底层的位表示保持不变

  • T2UwT2U_wT2Uw:从补码(有符号)转换为无符号数。
  • U2TwU2T_wU2Tw:从无符号数转换为补码(有符号)。
  • 其中 www 表示数据类型的位数。

显式与隐式转换

显式强制类型转换:通过代码手动指定目标类型。

c 复制代码
int tx, ty;
unsigned ux, uy;

tx = (int) ux;
uy = (unsigned) ty;

隐式类型转换:当一种类型的表达式被赋值给另外一种类型的变量时,转换会自动发生。

c 复制代码
int tx, ty;
unsigned ux, uy;

tx = ux; /* Cast to signed */
uy = ty; /* Cast to unsigned */

printf 输出处理

printf 函数不使用任何类型信息,它仅根据格式指示符来解释内存中的位模式:

  • %d:有符号十进制
  • %u:无符号十进制
  • %x:十六进制
c 复制代码
int x = -1;
unsigned u = 2147483648; /* 2 to the 31st */

printf("x = %u = %d\n", x, x);
printf("u = %u = %d\n", u, u);

/* 32 位机器输出结果:
x = 4294967295 = -1
u = 2147483648 = -2147483648
*/

表达式中的隐式转换规则

当一个运算中同时包含有符号和无符号数时,C 语言会隐式地将有符号参数转换为无符号数 ,并假设这两个数都是非负的。这在执行关系运算(如 <>)时经常会导致非直观的结果。

表达式 类型 求值
0 == 0U 无符号 1
-1 < 0 有符号 1
-1 < 0U 无符号 0*
2147483647 > -2147483647-1 有符号 1
2147483647U > -2147483647-1 无符号 0*
2147483647 > (int) 2147483648U 有符号 1*
-1 > -2 有符号 1
(unsigned) -1 > -2 无符号 1

注:标有 * 的行表示其结果可能由于隐式转换而违背直觉。例如 -1 < 0U,由于 -1 转换为无符号数是巨大的正数,因此结果为假(0)。

!IMPORTANT

假设在采用 补码(Two's Complement) 运算的 32 位 机器上对以下表达式进行求值。

根据 C 语言的 升级规则(Promotion Rule):当一个运算数是无符号的,另一个运算数也会被强制转换为无符号数再进行比较。

请填写下表,描述表达式的类型以及求值结果(1 表示真,0 表示假)。

表达式 类型 求值
-2147483647 - 1 == 2147483648U
-2147483647 - 1 < 2147483647
-2147483647 - 1U < 2147483647
-2147483647 - 1 < -2147483647
-2147483647 - 1U < -2147483647

在 32 位补码机器中:

  • TMin (有符号最小负数) : -2147483647 - 1 = -2147483648,其位模式为 0x80000000
  • TMax (有符号最大正数) : 2147483647,其位模式为 0x7FFFFFFF
  1. -2147483647 - 1 == 2147483648U
  • 计算 :左侧是 int,右侧是 unsigned
  • 转换 :左侧结果 -2147483648 被强制转换为无符号数。
  • 逻辑-2147483648 的位模式 0x80000000 在无符号解释下等于 2312^{31}231,即 2147483648
  • 结果 :类型为 无符号数 ,求值为 1
  1. -2147483647 - 1 < 2147483647
  • 计算 :两边都是 int
  • 逻辑 :普通的有符号数比较,-2147483648 < 2147483647 成立。
  • 结果 :类型为 有符号数 ,求值为 1
  1. -2147483647 - 1U < 2147483647
  • 计算 :左侧包含 1U,导致整个表达式变为无符号比较。
  • 转换 :右侧的 2147483647 被转换为无符号数(值不变)。左侧计算出的位模式 0x80000000 转换为无符号数 2147483648
  • 逻辑 :比较 2147483648 < 2147483647,不成立。
  • 结果 :类型为 无符号数 ,求值为 0
  1. -2147483647 - 1 < -2147483647
  • 计算 :两边都是 int
  • 逻辑 :有符号数比较,-2147483648 < -2147483647 成立。
  • 结果 :类型为 有符号数 ,求值为 1
  1. -2147483647 - 1U < -2147483647
  • 计算 :左侧包含 1U,全式提升为无符号比较。
  • 转换
    • 左侧:-2147483648 的位模式 0x80000000 →\rightarrow→ 无符号 2147483648
    • 右侧:-2147483647 的位模式 0x80000001 →\rightarrow→ 无符号 2147483649
  • 逻辑2147483648 < 2147483649 成立。
  • 结果 :类型为 无符号数 ,求值为 1
表达式 类型 求值
-2147483647 - 1 == 2147483648U 无符号数 1
-2147483647 - 1 < 2147483647 有符号数 1
-2147483647 - 1U < 2147483647 无符号数 0
-2147483647 - 1 < -2147483647 有符号数 1
-2147483647 - 1U < -2147483647 无符号数 1

拓展数字

在一个不同字长的整数之间进行转换,同时保持数值不变,是计算机底层常见的运算。

  • 从小向大转换:从较小的数据类型转换到较大的类型,总是可能的。
  • 从大向小转换:可能发生溢出或截断,导致数值改变。

无符号数的零扩展

将一个无符号数转换为更大的数据类型时,只需在开头添加 0。

原理 :定义宽度为 www 的位向量 u⃗=ww−1,ww−2,...,w0\vec{u} = w_{w-1}, w_{w-2}, \\dots, w_0u =ww−1,ww−2,...,w0 和宽度为 w′w'w′ 的位向量 u⃗′\vec{u}'u ′,其中 w′>ww' > ww′>w。则 u⃗′=0,...,0,ww−1,ww−2,...,w0\vec{u}' = 0, \\dots, 0, w_{w-1}, w_{w-2}, \\dots, w_0u ′=0,...,0,ww−1,ww−2,...,w0

依据:该原理直接遵循无符号数编码的定义,高位的权值为 0,不影响数值。

补码数字的符号扩展

将一个补码数字转换为更大的数据类型时,在表示中添加**最高有效位(符号位)**的值。

原理 :定义宽度为 www 的位向量 x⃗=xw−1,xw−2,...,x0\vec{x} = x_{w-1}, x_{w-2}, \\dots, x_0x =xw−1,xw−2,...,x0 和宽度为 w′w'w′ 的位向量 x⃗′\vec{x}'x ′,其中 w′>ww' > ww′>w。则 x⃗′=xw−1,...,xw−1,xw−1,xw−2,...,x0\vec{x}' = x_{w-1}, \\dots, x_{w-1}, x_{w-1}, x_{w-2}, \\dots, x_0x ′=xw−1,...,xw−1,xw−1,xw−2,...,x0

!NOTE

  • sx = -12345:16 位十六进制为 cf c7
  • x = (int)sx:扩展到 32 位。符号位为 1(c 的二进制是 1100),填充 f,得到 ff ff cf c7
  • usx = 53191:16 位无符号表示同样为 cf c7
  • ux = (unsigned)usx:执行零扩展,填充 0,得到 00 00 cf c7

符号扩展的证明

通过归纳法证明:扩展 1 位且保持数值不变,即可证明扩展任意位。

证明目标
B2Tw+1(xw−1,xw−1,xw−2,...,x0)=B2Tw(xw−1,xw−2,...,x0) B2T_{w+1}(x_{w-1}, x_{w-1}, x_{w-2}, \\dots, x_0) = B2T_w(x_{w-1}, x_{w-2}, \\dots, x_0) B2Tw+1(xw−1,xw−1,xw−2,...,x0)=B2Tw(xw−1,xw−2,...,x0)

推导过程
B2Tw+1(xw−1,xw−1,xw−2,...,x0)=−xw−12w+∑i=0w−1xi2i=−xw−12w+xw−12w−1+∑i=0w−2xi2i其中(−2w+2w−1)xw−1=−2w−1xw−1=−xw−12w−1+∑i=0w−2xi2i=B2Tw(x⃗) B2T_{w+1}(x_{w-1}, x_{w-1}, x_{w-2}, \\dots, x_0) =-x_{w-1} 2^w + \sum_{i=0}^{w-1} x_i 2^i \\ = -x_{w-1} 2^w + x_{w-1} 2^{w-1} + \sum_{i=0}^{w-2} x_i 2^i \\ 其中 (-2^w + 2^{w-1})x_{w-1} = -2^{w-1}x_{w-1} \\ = -x_{w-1} 2^{w-1} + \sum_{i=0}^{w-2} x_i 2^i = B2T_w(\vec{x}) B2Tw+1(xw−1,xw−1,xw−2,...,x0)=−xw−12w+i=0∑w−1xi2i=−xw−12w+xw−12w−1+i=0∑w−2xi2i其中(−2w+2w−1)xw−1=−2w−1xw−1=−xw−12w−1+i=0∑w−2xi2i=B2Tw(x )

新增位的权值为 −2w-2^w−2w,而原符号位权值从 −2w−1-2^{w-1}−2w−1 变为 +2w−1+2^{w-1}+2w−1。两者的综合效果是 −2w+2w−1=−2w−1-2^w + 2^{w-1} = -2^{w-1}−2w+2w−1=−2w−1,保持了原始数值的偏移量。

c 复制代码
#include <stdio.h>
// 32位大端法机器运行
typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
    size_t i;
    for (i = 0; i < len; i++)
        printf(" %.2x", start[i]);
    printf("\n");
}

int main() {
    short sx = -12345;       /* -12345 */
    unsigned short usx = sx; /* 53191 */
    int x = sx;              /* -12345 */
    unsigned ux = usx;       /* 53191 */
		
    printf("sx = %d:\t", sx);
    show_bytes((byte_pointer) &sx, sizeof(short));
    // sx = -12345:     c7 cf
    printf("usx = %u:\t", usx);
    show_bytes((byte_pointer) &usx, sizeof(unsigned short));
    // usx = 53191:     c7 cf
    printf("x = %d:\t", x);
    show_bytes((byte_pointer) &x, sizeof(int));
    // x = -12345:      c7 cf ff ff
    printf("ux = %u:\t", ux);
    show_bytes((byte_pointer) &ux, sizeof(unsigned));
    // ux = 53191:      c7 cf 00 00
    return 0;
}

相对顺序的影响

在 C 语言中,当一个程序同时需要改变数据的大小(如 shortint)以及转换有无符号属性(如 signedunsigned)时,转换的相对顺序会直接影响最终的数值结果。

根据 C 语言标准的要求,当执行此类复合转换时,系统遵循以下顺序:

  • 第一步:改变大小(进行符号扩展或零扩展)。
  • 第二步:符号转换(从有符号转换为无符号,或反之)。

先进行拓展,再进行转换

考虑以下代码段,其中 sx 是一个 16 位有符号短整型,其值为 -12345

c 复制代码
#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
    size_t i;
    for (i = 0; i < len; i++)
        printf(" %.2x", start[i]);
    printf("\n");
}

int main() {
    short sx = -12345;       /* -12345 */
    unsigned uy = sx;        /* Mystery! */
    printf("uy = %u:\t", uy);
    show_bytes((byte_pointer) &uy, sizeof(unsigned));
    // 输出 uy = 4294954951:         c7 cf ff ff
    return 0;
}

在大端法机器上,执行上述代码会产生如下输出:
uy = 4294954951: ff ff cf c7

转换过程详解:

  1. 原始值sx = -12345。其 16 位补码表示为 cf c7
  2. 符号扩展 :由于 sx 是有符号数,转换到 32 位时进行符号扩展 。高 16 位补符号位(1),得到 ff ff cf c7。此时数值在逻辑上仍为 -12345(如果是 32 位有符号 int)。
  3. 解释为无符号 :将 ff ff cf c7 直接解释为 unsigned 类型,得到的十进制数值即为 4,294,954,951
转换路径 等价表达式 内存字节 (大端法) 十进制结果
标准要求路径 (unsigned)(int) sx ff ff cf c7 4,294,954,951
错误路径 (unsigned)(unsigned short) sx 00 00 cf c7 53,191

结论 :事实证明,C 语言会先将 short 符号扩展为 int,然后再将其转换为 unsigned

!IMPORTANT

考虑下面的 C 函数:

c 复制代码
int fun1(unsigned word) {
    return (int) ((word << 24) >> 24);
}

int fun2(unsigned word) {
    return ((int) word << 24) >> 24;
}

假设在一个采用补码运算的机器上以 32 位程序来执行这些函数。还假设有符号数值的右移是算术右移 ,而无符号数值的右移是逻辑右移

  • fun1 :提取参数的最低字节(Byte 0),并将其余 24 位清零 。其结果始终是一个正整数(000 到 255255255)。
  • fun2 :提取参数的最低字节,并进行符号扩展。如果该字节的最高位(第 7 位)是 1,则结果的高 24 位全补 1,将其解释为负数。
w fun1(w) fun2(w)
0x00000076 0x00000076 0x00000076
0x87654321 0x00000021 0x00000021
0x000000C9 0x000000C9 0xFFFFFFC9
0xEDCBA987 0x00000087 0xFFFFFF87

截断数字

截断数字是指减少表示一个数字的位数。例如,当我们将一个 int(32位)强制类型转换为 short(16位)时,高位会被丢弃,这可能会改变数值的大小,这是溢出的一种形式。


截断无符号数

假设将一个 www 位的数 x⃗=xw−1,xw−2,...,x0\vec{x} = x_{w-1}, x_{w-2}, \\dots, x_0x =xw−1,xw−2,...,x0 截断为一个 kkk 位数字时,我们会丢弃高 w−kw-kw−k 位,得到一个位向量 x⃗′=xk−1,xk−2,...,x0\vec{x}' = x_{k-1}, x_{k-2}, \\dots, x_0x ′=xk−1,xk−2,...,x0

令 x=B2Uw(x⃗)x = B2U_w(\vec{x})x=B2Uw(x ),则截断后的值 x′=x mod 2kx' = x \bmod 2^kx′=xmod2k。

该原理背后的直觉是所有被截断的位其权重形式都为 2i2^i2i,其中 i≥ki \geq ki≥k,因此,每一个权重在取模操作下结果都为零。
B2Uw(xw−1,xw−2,...,x0) mod 2k=∑i=0w−1xi2i mod 2k=∑i=0k−1xi2i mod 2k=∑i=0k−1xi2i=B2Uk(xk−1,...,x0) B2U_w(x_{w-1}, x_{w-2}, \\dots, x_0) \bmod 2^k = \left \\sum_{i=0}\^{w-1} x_i 2\^i \\right \bmod 2^k \\= \left \\sum_{i=0}\^{k-1} x_i 2\^i \\right \bmod 2^k \\= \sum_{i=0}^{k-1} x_i 2^i \\ = B2U_k(x_{k-1}, \\dots, x_0) B2Uw(xw−1,xw−2,...,x0)mod2k=i=0∑w−1xi2imod2k=i=0∑k−1xi2imod2k=i=0∑k−1xi2i=B2Uk(xk−1,...,x0)

在此推导中,利用了属性:对于任何 i≥ki \geq ki≥k,2i mod 2k=02^i \bmod 2^k = 02imod2k=0。


截断补码数值

补码截断也具有相似的属性,只不过要将最高位转换为符号位。令 x=B2Uw(x⃗)x = B2U_w(\vec{x})x=B2Uw(x ),x′=B2Tk(x⃗′)x' = B2T_k(\vec{x}')x′=B2Tk(x ′)。则 x′=U2Tk(x mod 2k)x' = U2T_k(x \bmod 2^k)x′=U2Tk(xmod2k)。

  • 在这个公式中,x mod 2kx \bmod 2^kxmod2k 将是 000 到 2k−12^k-12k−1 之间的一个数。
  • 应用函数 U2TkU2T_kU2Tk 产生的效果是把最高有效位 xk−1x_{k-1}xk−1 的权重从 2k−12^{k-1}2k−1 转变为 −2k−1-2^{k-1}−2k−1。

示例将数值 x=53191x = 53191x=53191 从 int 转换为 short

  • 由于 216=65536≥x2^{16} = 65536 \geq x216=65536≥x,我们有 x mod 216=x=53191x \bmod 2^{16} = x = 53191xmod216=x=53191。
  • 当把这个数转换为 16 位的补码时,得到 x′=53191−65536=−12345x' = 53191 - 65536 = -12345x′=53191−65536=−12345。

综上有:

  • 无符号数的截断结果:
    B2Uk(xk−1,xk−2,...,x0)=B2Uw(xw−1,xw−2,...,x0) mod 2k(2.9)B2U_k(x_{k-1}, x_{k-2}, \\dots, x_0) = B2U_w(x_{w-1}, x_{w-2}, \\dots, x_0) \bmod 2^k \quad (2.9)B2Uk(xk−1,xk−2,...,x0)=B2Uw(xw−1,xw−2,...,x0)mod2k(2.9)

  • 补码数字的截断结果:
    B2Tk(xk−1,xk−2,...,x0)=U2Tk(B2Uw(xw−1,xw−2,...,x0) mod 2k)(2.10)B2T_k(x_{k-1}, x_{k-2}, \\dots, x_0) = U2T_k(B2U_w(x_{w-1}, x_{w-2}, \\dots, x_0) \bmod 2^k) \quad (2.10)B2Tk(xk−1,xk−2,...,x0)=U2Tk(B2Uw(xw−1,xw−2,...,x0)mod2k)(2.10)

c 复制代码
#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, size_t len) {
    size_t i;
    for (i = 0; i < len; i++)
        printf(" %.2x", start[i]);
    printf("\n");
}

int main() {
    // 1. 初始值:53191
    // 二进制: 1100 1111 1100 0111 (十六进制: 0x0000CFC7)
    int x = 53191;
    
    // 2. 强制转换为 short (16位)
    // 发生截断:只保留低 16 位。
    // 0xCFC7 在 16 位有符号数中,最高位是 1,代表负数。
    // 根据补码计算:0xCFC7 = -32768 + 20423 = -12345
    short sx = (short) x;  

    // 3. 将 short 扩展回 int (32位)
    // 发生符号扩展:为了保持数值 -12345 不变,高位全部填充符号位 (1)。
    // 结果变为: 0xFFFFCFC7
    int y = sx;

    printf("x  = %d, bytes: ", x);
    show_bytes((byte_pointer) &x, sizeof(int));
    // x  = 53191, bytes:  c7 cf 00 00
    printf("sx = %d, bytes: ", sx);
    show_bytes((byte_pointer) &sx, sizeof(short));
	// sx = -12345, bytes:  c7 cf
    printf("y  = %d, bytes: ", y);
    show_bytes((byte_pointer) &y, sizeof(int));
	// y  = -12345, bytes:  c7 cf ff ff
    return 0;
}

例题

题目

练习题 2.25 考虑下列代码,这段代码试图计算数组 a 中所有元素的和,其中元素的数量由参数 length 给出。

c 复制代码
/* WARNING: This is buggy code */
float sum_elements(float a[], unsigned length) {
    int i;
    float result = 0;
    for (i = 0; i <= length-1; i++)
    result += a[i];
return result;
}

当参数 length 等于 0 时,运行这段代码应该返回 0.0。但实际上,运行时会遇到一个内存错误。请解释为什么会发生这样的情况,并且说明如何修改代码。

答案

  • 原因分析 :因为参数 length 是无符号的,计算 0−10-10−1 将使用无符号运算,这等价于模数加法。结果得到 UMaxUMaxUMax(无符号整数的最大值)。≤\le≤ 比较同样使用无符号数比较,因为任何数都是小于或者等于 UMaxUMaxUMax 的,所以这个比较总是为真!因此,代码将试图访问数组 a 的非法元素。
  • 解决方法 :有两种方法可以改正这段代码:
    1. length 声明为 int 类型。
    2. for 循环的测试条件改为 i < length

题目

练习题 2.26 现在给你一个任务,写一个函数用来判定一个字符串是否比另一个更长。前提是你要用字符串库函数 strlen,它的声明如下:

c 复制代码
/* Prototype for library function strlen */
size_t strlen(const char *s);

最开始你写的函数是这样的:

c 复制代码
/* Determine whether string s is longer than string t */
/* WARNING: This function is buggy */
int strlonger(char *s, char *t) {
    return strlen(s) - strlen(t) > 0;
}

当你以一些示例数据上测试这个函数时,一切似乎都是正确的。进一步研究发现,在头文件 stdio.h 中数据类型 size_t 是定义成 unsigned int 的。

A. 在什么情况下,这个函数会产生不正确的结果?

B. 解释为什么会出现这样不正确的结果。

C. 说明如何修改这段代码好让它能可靠地工作。

答案

2.26 这个例子说明了无符号运算的一个细微的特性,同时也是我们执行无符号运算时不会意识到的属性。这会导致一些非常棘手的错误。

  • A. 错误情况 :当 st 短的时候,该函数会不正确地返回 1。
  • B. 原因解释 :由于 strlen 被定义为产生一个无符号的结果,差和比较都采用无符号运算来计算。当 st 短的时候,strlen(s) - strlen(t) 的差会为负,但是变成了一个很大的无符号数,且大于 0。
  • C. 修改方法 :将测试语句改成 return strlen(s) > strlen(t);。因为这样是直接对两个数值进行比较,没有产生中间运算结果。
相关推荐
mAo静卧窗轩3 个月前
程序人生-Hello’s P2P
csapp
2501_941841684 个月前
HIT-CSAPP2025大作业:程序人生-Hello’s P2P(2024111666-牛启正)
c语言·c·csapp
simon_skywalker5 个月前
CSAPP第二章 信息表示与处理(一) 信息存储
csapp
xinwulinzi1 年前
HIT-csapp大作业:程序人生-HELLO‘s P2P
程序人生·课程设计·csapp·计算机系统·哈工大
Artintel2 年前
[学习笔记]《CSAPP》深入理解计算机系统 - Chapter 6 存储器层次结构
笔记·学习·c·csapp
永恒,怎么可能2 年前
【CSAPP】-linklab实验
csapp
永恒,怎么可能2 年前
【CSAPP】-cachelab实验
csapp
John_Snowww2 年前
CSAPP Lab01——Data Lab完成思路
vscode·csapp·wsl2·cmu15213
念谨2 年前
【目录】CSAPP的实验简介与解法总结(已包含Attack/Link/Architecture/Cache)
csapp·深入理解计算机系统·15213