开头这句话非常喜欢, 抄下来:

毫无疑问, 学习一门编程语言的基础只是不如编写程序有趣. 但是, 不知道语言的基础会使你在编写程序时缺少乐趣.

环境

ANSI C 的任何一种实现都存在两种环境

  1. 翻译环境(translation environment) 源代码被转换为可执行的机器指令
  2. 执行环境(execution environment) 用于实际执行代码

标准明确说明, 两种环境不必存于同一台机器上, 交叉编译器(cross compiler) 就是 在一台机器上运行, 产生的胆码可运行于不同的类型的机器上. 操作系统也是如此.

翻译

翻译阶段有几个步骤组成

  1. 将源文件通过编译过程分别转换为 目标代码(object code)
  2. 链接器(linker) 捆绑目标代码, 生成一个单一而完整的可整型程序.

链接器同时也会引入标准 C 函数库中任何被该程序所用到的函数, 而且它也可以搜索 程序员个人的程序库, 将其中所需要的函数链接到程序中.

编译过程本身也有几个阶段组成

  1. 预处理器(preprocessor)处理, 替换指定, 读入文件等;
  2. 解析(parse) 源码, 产生绝大多数错误和警告
  3. 产生目标代码, 目标代码是机器指令的初步形式,.
  4. 如果编译命令加入了要求进行优化的选项, 优化器(optimizer)会对目标代码进行优化

文件名约定

源码保存在以 .c 扩展名命名的文件.

头文件以 .h 为扩展名

目标文件 Unix 为 .o, MS-DOS 则为 .obj

编译和链接

1. 编译并链接

cc program.c

2. 编译并链接几个源文件

cc main.c sort.c lookup.c

3. 编译一个源文件, 并把他和现存的目标文件链接在一起:

cc main.o lookup.o sort.c

4. 编译单个文件, 并产生目标文件, 以后在进行链接:

cc -c program.c

5. 编译多个源文件, 并为每个文件产生一个目标文件:

cc -c main.c sort.c lookup.c

6. 链接几个目标文件:

cc main.o sort.o lookup.o

执行

程序的执行过程也需要经历几个阶段:

  1. 操作系统将程序载入到内存. 那些不是存储在堆栈中的尚未初始化的变量将在这个时候得到初始值
  2. 操作系统通常将一个小型的启动程序与程序链接在一起. 它负责处理一系列日常事务,如手机命名参数以便使程序能够访问它们.
  3. 调用 main 函数
  4. 运行程序代码. 在绝大多数机器里, 程序将使用一个运行时*堆栈*(stack), 用于存储函数的局部变量和返回地址. 同事也可以使用 静态(static) 内存, 存储与静态内存中的变量在程序的整个执行过程中将一直保留它们的值.
  5. 终止程序. 启动程序将再次获得控制权, 并执行各种不同的日常任务: 关闭文件,释放内存.

词法规则

词法规则就像英语中的拼写规则, 决定你在源程序中如何形成单独的字符片段, 也就是 标记(token)

字符

标准没有规定C环境必须使用的字符集, 但规定字符集必须豪阔英语所有的大小写字母, 数组和一些符号.

标准还定义了几个 三字母词(trigrph), 就是几个字符序列, 合起来表示另一个字符.

??(  [          ??<   {         ??=   #
??)  ]          ??>   }         ??/   \
??!  |          ??'   ^         ??-   ~

看了下 gcc 的help , 编译的时候 -trigrph 选项可以开启此特性

转义序列(escape sequence)或 字符转义(character escape)有一个反斜杠\加上一 个或过个其他字符组成.

注释

C 语言中/* 表示注释的开始 */ 表示注释的结尾, 不支持嵌套注释.

所有的注释都会被预处理器拿掉,取而呆滞的是一个空格.

自由形式的源代码

C 是一种自由形式的语言, 也就是说并没有规则规定什么地方可以书写语句.

标识符

标识符(identifier) 就是变量, 函数, 类型等的名字. 由大小写字母, 数字和下划线 组成, 但不能以数字开头. 并且大小写敏感.

标识符的长度没有限制, 但是标准允许编译器忽略第 31 个字符以后的字符.

程序的形式

每个函数必须完整的出现在同一个源文件中.

程序风格