在“第一个程序”一节中,读者应该已经尝试对Hello world程序做各种改动看编译运行结果,其中有些改动会导致编译出错,有些改动会影响程序的输出,有些改动则不影响程序的输出,下面我们总结一下。首先,注释可以跨行,也可以穿插在程序之中,看下面的例子。
例 2.1. 带更多注释的Hello World
#include <stdio.h> /* * comment1 * main: generate some simple output */ int main(void) { printf(/* comment2 */"Hello, world.\n"); /* comment3 */ return 0; }
第一个注释跨了四行,头尾两行是表示注释的/*
和*/
,中间的两行开头的*号(Asterisk)并没有特殊含义,只是为了看起来整齐,这不是语法规则而是大家都遵守的C代码风格(Coding Style)之一。
使用注释需要注意两点:
C语言的发展历史大致上分为三个阶段:Old Style C、C89和C99。Ken Thompson和Dennis Ritchie发明C语言时有很多语法和现在并不一样,但为了向后兼容性(Backward Compatibility),这些语法仍然在C89和C99中保留下来了,本书不使用Old Style C,但在必要的地方会加以说明。C89是最早的C语言规范,于1989年提出,1990年先由ANSI(美国国家标准委员会,American National Standards Institute)推出ANSI版本,后来被接纳为ISO国际标准(ISO/IEC 9899:1990),因而有时也称为C90,最经典的C语言教材[K&R]就是基于这个版本的,C89是目前最广泛采用的C语言标准,大多数编译器都完全支持C89。C99标准(ISO/IEC 9899:1999)是在1999年推出的,加入了许多新的特性,但目前仍没有得到广泛支持,在C99推出之后相当长的一段时间里,连gcc也没有完全实现C99的所有特性。C99标准详见[C99]。本书内容以C89为主,在必要的地方会说明一下C99的新特性,但是不建议使用。
C标准的目的是为了精确定义C语言,而不是为了教别人怎么编程,C标准在表达上追求准确和无歧义,却十分不容易看懂,[Standard C]和[Standard C Library]是对C89及其修订版本的阐释(可惜作者没有随C99更新),比C标准更容易看懂,另外,参考[C99 Rationale]也有助于加深对C标准的理解。
像"Hello, world.\n"
这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。注意,程序的运行结果并没有双引号,printf
打印出来的只是里面的一串字符Hello, world.
,因此双引号是字符串字面值的界定符(Delimiter),夹在双引号中间的一串字符才是它的内容。注意,打印出来的结果也没有\n
这两个字符,这是为什么呢?在“自然语言和形式语言”一节中提到过,C语言规定了一些转义序列(Escape Sequence),这里的\n
并不表示它的字面意思,也就是说并不表示\和n这两个字符本身,而是合起来表示一个换行符(Line Feed)。例如我们写三条打印语句:
printf("Hello, world.\n"); printf("Goodbye, "); printf("cruel world!\n");
运行的结果是第一条语句单独打到第一行,后两条语句都打到第二行。为了节省篇幅突出重点,以后的例子通常省略include
和main
这些Boilerplate,但读者在练习时需要加上这些构成一个完整的程序才能编译通过。C标准规定的转义字符有以下这些:
如果在字符串字面值中要表示单引号'和问号?,既可以使用转义序列\'和\?,也可以直接用字符'和?,而要表示\或"则必须使用转义序列,因为\字符表示转义而不表示它的字面含义,"表示字符串的Delimiter而不表示它的字面含义。可见转义序列有两个作用:一是把普通字符转义成特殊字符,例如把字母n转义成换行符;二是把特殊字符转义成普通字符,例如\和"是特殊字符,转义后取它的字面值。
C语言规定了几个控制字符,不能用键盘直接输入,因此采用\加字母的转义序列表示。\a是响铃字符,在字符终端下显示这个字符的效果是PC喇叭发出嘀的一声,在图形界面终端下的效果取决于终端的实现。在终端下显示\b和按下退格键的效果相同。\f是分页符,主要用于控制打印机在打印源代码时提前分页,这样可以避免一个函数跨两页打印。\n和\r分别表示Line Feed和Carriage Return,这两个词来自老式的英文打字机,Line Feed是跳到下一行(进纸,喂纸,有个喂的动作所以是feed),Carriage Return是回到本行开头(Carriage是卷着纸的轴,随着打字慢慢左移,打完一行就一下子移回最右边),如果你看过欧美的老电影应该能想起来这是什么。用老式打字机打完一行之后需要这么两个动作,\r\n,所以现在Windows上的文本文件用\r\n做行分隔符,许多应用层网络协议(如HTTP)也用\r\n做行分隔符,而Linux和各种UNIX上的文本文件只用\n做行分隔符,所以很多初学者弄不清楚这两个字符有什么区别。在终端下显示\t和按下Tab键的效果相同,用于在终端下定位表格的下一列,\v用于在终端下定位表格的下一行。\v比较少用,\t比较常用,以后将“水平制表符”简称“制表符”或Tab。请读者用printf
语句试试这几个控制字符的作用。
注意"Goodbye, "
末尾的空格,字符串字面值中的空格也算一个字符,也会出现在输出结果中,而程序中别处的空格和Tab多一个少一个往往是无关紧要的,不会对编译的结果产生任何影响,例如不缩进不会影响程序的结果,main
后面多几个空格也没影响,但是int
和main
之间至少要有一个空格分隔开:
int main (void) { printf("Hello, world.\n"); return 0; }
不仅空格和Tab是无关紧要的,换行也是如此,我甚至可以把整个程序写成一行,但是include
必须单独占一行:
#include<stdio.h> int main(void){printf("Hello, world.\n");return 0;}
这样也行,但肯定不是好的代码风格,去掉缩进已经很影响可读性了,写成现在这个样子可读性更差。如果编译器说第2行有错误,也很难判断是哪个语句有错误。所以,好的代码风格要求缩进整齐,每个语句一行,适当留空行。