CS110 计算机体系结构 2 梦开始的地方

aaaaa Lv4

CS110 这门课本身讲述顺序相当合理,我将会大致沿用上课的顺序进行介绍,即和我的笔记大概一致。在少数我认为顺序不合理的地方,我会自行调整顺序,不过内容大致一致,因此读者只需要阅读本教程或笔记二者其一即可。


Great ideas in CA

本来想着把这个标题翻译成“计算机体系结构中的伟大思想”,但是又觉得有点画风突变的感觉,我不希望把 CA 做成像文科一样的课程。总之,如果你现在不理解以下这些内容也没关系——等到学完整个课程,你一定会有全新的认识。

  • Abstraction (Layers of Representation / Interpretation) —— 抽象(表示/解释的分层)
  • Moore’s Law (designing through trends) —— 摩尔定律(通过趋势进行设计)
  • Make the common case fast (Amdahl’s Law) —— 让常见情况变快(阿姆达尔定律)
  • Principle of locality (memory hierarchy) —— 局部性原理(内存层次结构)
  • Parallelism (pipeline as special case) —— 并行性(流水线作为特例)
  • Performance measurement and improvement —— 性能测量与提升
  • Dependability via redundancy —— 通过冗余实现可靠性

顺带说一句,考试的时候会可能会涉及以上内容,请读者务必抄到 Cheatsheet 上

这是整个课程梦开始的地方,最后我们会再回到这里的。会的。


C 语言

从哪里开始讲呢?我们按照从上至下的顺序,也就是从我们使用的高级程序设计语言之一:C语言,开始讲。

相信各位读者对于 C 语言的语法都很了解(否则请参考C 语言教程 | 菜鸟教程,不过这门课其实对 C 代码没有过高的要求,能看懂、区分出各类 UB,CE 和 RTE 即可)。作为几乎在所有平台上都能使用的语言,C 语言有更完整的变量类型定义、更好的内存管理,需要经过编译过程执行。接下来我们先简单复习一下 C 语言的内存管理相关内容。这些内容可能看上去有些碎片化,不过还请读者挑选其中记不熟的内容抄到 Cheatsheet 上。

C 语言的一些基础知识

严格来说,C 语言的指针类型大小是和系统有关的,但是方便起见,我们在本课程中统一指针大小为 4 byte,即 32 位——这就是我们常说的“32位计算机”里的“32位”的来源——32 bit 长度的地址。一般的计算机是 byte addressing 的,即每个确定的地址(如 0x1145DCBC)对应内存里的一个确定的字节。

一般来说,C 语言中的数组和指针是等效的(如 ar[2] 等效于 *(ar+2)),但是也有例外:

1
2
char string1[] = "abc"; // 可以修改
char *string2 = "abc"; // 不能修改

在字符串中,存储时一般会在字符串尾部添加 \0 来标记字符串长度,但是也有特例:

1
2
3
4
char s[] = "abc", t[3] = "abc";
// 等价于
char s[] = {'a', 'b', 'c', '\0'},
t[] = {'a', 'b', 'c'};

回到数组,主函数传参时,采用参数数量 + 参数数组的格式传入:

1
int main(int argc, char *argv[])

其中 argc 表示参数数量 Argument Count(即命令行上空格隔开字符串的数量),argv Argument Vector 为一个指向这些字符串指针的数组。例如:

1
% clang -ansi test001.c -o test001.out

此时 argc = 5argv = ["clang", "-ansi", "test001.c", "-o", "test001.out"]

这启发我们设计数组作为参数传入函数时,务必同时传入数组长度,否则如果发生越界访问,会极难发现。

在涉及指针运算时,存在内存对齐问题。在内存读取时,是每个 byte 作为独立单位读取的(即我们前面讲到的 byte addressing 导致的),因此数据需要对齐 Alignment。

  • 对于整型 int 数据(4 byte),要求存储时需要最低位存储在 4 byte 的整倍数的内存地址上;
  • 对于短整型 short 数据(2 byte),要求存储时需要最低位存储在 2 byte 的整倍数的内存地址上。

对于指针的运算,C 语言只定义了如下运算:

  • 加减整数
  • 指针作差
  • 指针比较
  • NULL 比较

其它运算均为不合法运算。

另外,C 语言定义数组越界1位处必为合法地址,但是对这个地址解引用是非法的。这是为了方便检查访问越界,同时不破坏存储的其它数据。

C 语言的内存管理

C 语言程序在运行时的内存分为 4 个区域:栈区 stack,堆区 heap,静态数据区域 static data,代码区 code。这四个区域各有各的用处和特性:

Snipaste_2026-03-22_10-22-09

  • 栈区 stack:存放连续地址的栈帧(包含函数的返回地址(调用者)、函数参数、函数内部定义的局部变量);函数被调用时入栈,函数终止时弹栈。栈区一般从高地址向低地址生长。
  • 堆区 heap:运行时不连续分配的内存区域,由 malloc() calloc() realloc() free() 手动管理空间;一般比栈区空间更大(但是也要记得释放空间,防止内存泄漏)。堆区一般从低地址向高地址生长。(不用担心栈区和堆区撞在一起,后续内存分页会解决这个问题)
  • 静态数据区域 static data:存储全局/静态变量,其中又分为三个区域:
    • .rodata 区:是只读的 read-only,存储字符串字面量、(被自动/手动初始化的)const 修饰的全局/静态变量
    • .data 区:存储非零初始化全局变量/静态变量
    • .bss 区:Block started by symbol,存储未初始化显式初始化为 0全局/静态变量
  • 代码区 code:存储代码,运行时一般只读。

记不住没关系,看一眼下面的程序,你就能理解了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int g_data = 1 // 非零初始化的全局变量 g_data,放在 .data 区
int g_bss;             // 未初始化的全局变量 g_bss 自动初始化为 0,放在 .bss 区
const char *ro_str = "RO";         // "RO" 放在 .rodata 区, 而 ro_str 放在 .data 区

// 这个函数 demo() 放在 code 区
void demo(int stack_arg) {         // 这个参数 stack_arg 放在栈区
   int stack_var = 2;             // 局部变量 stack_var 放在栈区
   static int s_data = 3;         // 静态变量 s_data 放在 .data 区
   void *heap = malloc(8);        // 指针 heap 是栈区的局部变量,而 heap 指向的区域在堆区
   free(heap);                    // 释放堆区空间
}

int main() {
   demo(0);
   return 0;
}

至此,关于 C 语言的基本知识就介绍得差不多了。对于有 C 语言基础的读者,这部分应该只是复习性质的;对于没有基础的读者,请完全理解以上这一节的内容,然后你的水平就和其他人站在同一起跑线上了。


程序:从代码到执行

在本课程讨论的范围内,程序从代码到执行的过程大致可以分为四步:编译 Compilation - 汇编 Assembly - 链接 Linking - 加载 Loading。

Snipaste_2026-07-20_14-57-27

具体的过程在此处不做详细解释,等到我们深入了解完 RISC-V 这门汇编语言,再回来讲每个部分分别做了什么。读者此处只需要大致了解这个过程,以防后续深入某一部分时“只缘身在此山中”。


回到目录

  • 标题: CS110 计算机体系结构 2 梦开始的地方
  • 作者: aaaaa
  • 创建于 : 2026-07-20 14:00:00
  • 更新于 : 2026-07-20 15:38:29
  • 链接: https://redefine.ohevan.com/2026/07/20/零基础速通系列/CS110 计算机体系结构/零基础速通:CS110_计算机体系结构_2/
  • 版权声明: 版权所有 © aaaaa,禁止转载。