《C和指针》读书笔记第二章基本概念
写在前面,由于学过C语言,导致想要跳跃式地翻阅《C和指针》,后来发现这实为错误,对于这本经典著作,要做的是从头到尾保持体系的完整性。
《C和指针》配套代码请移步网站:Pointers on C
作者Kenneth A. Reek的个人网站
文章目录
- 2.1环境
- 2.1.1 翻译
- 2.1.2 执行
- 2.2 词法规则
- 2.2.1 字符
- 2.2.2 注释
- 2.2.3 自由形式的源代码
- 2.2.4 标识符
- 2.2.5 程序的形式
- 2.7 问题
2.1环境
在ANSI C的任何一种实现中,存在两种不同的环境。第1种是翻译环境(translation environment),在这个环境里,源代码被转换为可执行的机器指令。第2种是执行环境(execution environment),它用于实际执行代码。标准明确说明,这两种环境不必位于同一台机器上。例如,交叉编译器(cross compiler)就是在一台机器上运行,但它所产生的可执行代码运行于不同类型的机器上。操作系统也是如此。标准同时讨论了独立环境(freestanding environment),就是不存在操作系统的环境。
2.1.1 翻译
翻译阶段由几个步骤组成,组成一个程序的每个(有可能有多个)源文件通过编译过程分别转换为目标代码(object code)。然后,各个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。链接器同时也会引入C函数库种任何被该程序所用到的函数,而且它也可以搜索程序员个人的程序库,将其中需要使用的函数也链接到程序中。
编译过程本身也由几个阶段组成,首先是预处理器处理。在这个阶段,预处理器在源代码上执行一些文本操作。例如,用实际值代替由#define指令定义的符号以及读入由#include指令包含的文件的内容。
然后,源代码经过解析(parse),判断它的语句的意思。第2个阶段是产生绝大多数错误和警告信息的地方。随后,便产生目标代码。目标代码是机器指令的初步形式,用于实现程序的语句。如果我们在编译程序的命令行中加入了要求进行优化的选项,优化器(optimizer)就会对目标代码进一步进行处理,使它效率更高。优化过程需要额外的时间,所以在程序调试完毕并准备生成正式产品之前一般不进行这个过程。至于目标代码是直接产生的,还是先以汇编语言语句的形式存在,然后再经过一个独立的阶段编译成目标文件,对我们来说并不重要。
一 文件名约定
c源代码通常保存在以.c扩展名命名的文件中。由#include指令包含到c源程序的文件被称为头文件,通常具有扩展名.h.
至于目标文件名,不同的环境可能具有不同的性质。例如,在UNIX系统中,它们的扩展名是.o,但在MS-DOS系统中,它们的扩展名是.obj。
二 编译和链接
用于编译和链接c程序的特定命名在不同的系统中各不相同,但许多都和这里所描述的两种系统差不多。在绝大多数UNIX系统中,C编译器被称为cc,它可以用多种不同的方法来调用。
1 编译并链接一个完全包含于一个源文件的C程序:
这条命令产生一个称为a.out的可执行文件。中间会产生一个名为program.o的目标文件,但它在链接过程完成后会被删除。
2 编译并链接几个C源文件:
cc main.c sort.c lookup.c当编译的源文件超过一个时,目标文件便不会被删除。这就允许你对程序进行修改后,只对那些进行过改动的源文件进行重新编译,如下一条命令所示。
3 编译一个C源文件,并把它和现存的目标文件链接在一起:
cc main.o lookup.o sort.c4 编译单个C源文件,并产生一个目标文件(本例中为program.o),以后再进行链接:
cc -c program.c5 编译几个C源文件,并为每个文件产生一个目标文件
cc -c main.c sort.c lookup.c6 链接几个目标文件
cc main.o sort.o lookup.o2.1.2 执行
程序的执行过程也需要经历几个阶段。首先,程序必须载入到内存中。在宿主环境(也就是具有操作系统的环境),这个任务由操作系统完成。那些不是存储在堆栈中的尚未初始化的变量将在这个时候得到初始值。
然后,程序的执行便开始了。在宿主环境中,通常一个小型的启动程序于程序链接在一起。它负责处理一系列日常事务,如收集命令行参数以便使程序能够访问它们。接着,便调用main函数。
现在,便开始执行程序代码。在绝大多数机器里,程序将使用一个运行时堆栈,它用于存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储于静态内存中的变量在程序的整个执行过程中将一直保留它们的值。
2.2 词法规则
一个ANSI C程序由声明和函数组成。函数定义了需要执行的工作,而声明则描述了函数和函数将要操作的数据类型(有时候是数据本身)。
2.2.1 字符
标准并没有规定C环境必须使用哪种特定的字符集,但它规定字符集必须包括英文所有的大写和小写字母,数字0到9,以及下面这些符号:
换行符用于标志源代码每一行的结束,当正在执行的程序的字符输入就绪时,它也用于标志每个输入行的结尾。 如果运行时环境需要,换行符也可以是一串字符,但它们被当作单个字符处理。字符集还必须包括空格、水平制表符、垂直制表符和格式反馈字符。这些字符加上换行符,通常被称作空白字符,因为当它们被打印出来时,在页面上出现的是空白而不是各种记号。
标准还定义了几个三字母词(trigrph),三字母词就是几个字符的序列,合起来表示另一个字符。 三字母词使C环境可以在某些缺少一些必需字符的字符集上实现。
| ??( | [ |
| ??) | ] |
| ??! | | |
| ??< | { |
| ??> | } |
| ??’ | ^ |
| ??= | # |
| ??/ | \ |
| ??- | ~ |
两个问号开头再尾随一个字符一般不会出现在其他表达式中,所以把三字母词用这种形式来表示,这样就不致引起误解。
当你在编写某些C源代码时,你在一些上下文环境里想要使用某个特定的字符,却可能无法如愿,因为该字符在这个环境里有特别的意义。 例如,双引号“” 用于界定字符串常量,你如何在一个字符串常量内部包含一个双引号呢? K&R C 定义了 几个转义序列(escape sequence) 或字符转义(character escape),用于克服这个难题。 ANSI C 在它的基础上又增加了几个转义序列。转义序列由一个反斜杠\加上一个或多个其他字符组成。 下面列出的每个转义序列代代表反斜杠后面的那个字符,但并未给这个字符增加特别的意义。
| \? | 在书写连续多个问号时使用,防止它们被解释为三字母词 |
| \’’ | 用于表示一个字符串常量内部的双引号 |
| \’ | 用于表示字符常量’ |
| \\ | 用于表示一个反斜杠,防止它被解释为一个转义序列符 |
有许多字符并不在源代码中出现,但它们在格式化程序输出或操纵终端显示屏时非常有用。C语言也提供了一些这方面的转义符,方便你在程序中使用它们。
| \a | 警告字符。它将奏响终端铃声或产生其他一些可听见或可看见的信号 |
| \b | 退格键 |
| \f | 进纸字符 |
| \n | 换行符 |
| \r | 回车符 |
| \t | 水平制表符 |
| \v | 垂直制表符 |
| \ddd | 表示1~3个八进制数字。这个转义符表示的字符就是给定的八进制数值所代表的字符 |
| \xddd | 用于表示十六进制数字 |
2.2.2 注释
C语言的注释以字符/*开始,以字符 * / 结束,中间可以包含除 */ 之外的任何字符。所有的注释都会被预处理器拿掉,取而代之的时一个空格。因此,注释可以出现在任何空格可以出现的地方。
下面这种嵌套是非法的
2.2.3 自由形式的源代码
C是一种自由形式的语言,也就是说并没有规则规定什么地方可以书写语句,一行中可以出现多少条语句,什么地方应该留下空白以及应该出现多少空白等。唯一的规则就是相邻的标记之间必须出现一至多个空白字符(或注释),不然它们可能被解释成单个标记。因此,下面语句是等价的
y=x+1;y= x + 1;y =x + 1至于下面这组语句,前3条语句是等价的,但第4条语句是非法的
int x;int x;int /*comment */ x; 合法intx; //非法对于第三条语句进行实验
#include<stdio.h>int main() {/*/**/int /*这是个什么玩意?*/ x=1;printf("Hello,world:%d\n",x); }运行结果
Hello,world:12.2.4 标识符
标识符(identifier)就是变量、函数、类型等的名字。它们由大小写字母、数字和下划线组成,但不能以数字开头。C是一种大小写敏感的语言,所以abc,Abc,abC和ABC是4个不同的标识符。标识符的长度没有限制,但标准允许编译器忽略第31个字符以后的字符。标准同时允许编译器对用于表示外部名字(也就是由链接器操纵的名字)的标识符进行限制,只识别前六位不区分大小写的字符。
下列C语言关键字是被保留的,它们不能作为标识符使用
auto do goto signed unsigned break double if sizeof void case else int static volatile char enum long struct while const extern register switch continue float return typedef default for short union2.2.5 程序的形式
一个C程序可能保存于一个或多个源文件中。虽然一个源文件可以包含超过一个的函数,但每个函数都必须完整地出现于同一个源文件中。标准并没明确规定,但一个C程序的源文件应该包含一组相关的函数,这才是较为合理的组织形式。这种做法还有一个优点,就是它使实现抽象数据类型称为可能。
2.7 问题
3.输出
"Blunder??!??"代码
printf("\"Blunder?\?!??\"");总结
以上是生活随笔为你收集整理的《C和指针》读书笔记第二章基本概念的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: win10中cmd如何编译和运行c/c+
- 下一篇: 《大话数据结构》读书笔记-栈与队列