阅读 ‧ 电子书库 为什么不内置输入输出语句
也许您想知道为什么不自动包含像输入输出这样基本的语句。一个答案是并非所有的程序都要用到I/O(输入/输出)包,并且C语言的一个基本设计原则是避免不必要的成分。这个经济地使用资源的原则使得C语言在嵌入式编程中非常流行,例如,为一个控制自动燃料系统的芯片编写程序。顺便说一句,#include甚至不是C语言的语句!第一列中的#符号表明这一行是在编译器接手之前由C预处理器处理的语句。以后您将碰到更多预处理器指令的例子,第16章“C预处理器和C库”将对它们做更详细的讲解。

二、main()函数

阅读 ‧ 电子书库

接下来的这行代码声明了一个main函数。的确,main是一个极其普通的名字,但它是惟一的选择。一个C程序(我们将不考虑一些例外的情况)总是从被称为main()的函数开始执行的。您可以对您所用的其他函数任意命名,但是main()必须是开始的函数。那么圆括号的功能呢?它们表明main()是一个函数。很快您将学到更多的函数。但现在,就请记住这个函数是C程序的基本模块。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

int指明了main()函数的返回类型。这意味着main()函数返回值的类型是整数。返回到哪里呢?返回给操作系统。我们将在第6章“C控制语句:循环”中再来讨论这个问题。

函数名后面的圆括号一般包含传递给函数的信息。这个简单的例子没有传递任何信息,因此圆括号内包含了单词void(在第11章“字符串和字符串函数”中,将介绍可以将信息从操作系统传递给main()函数的第二种形式)。

如果浏览老版本的C代码,您将发现程序常常以:

阅读 ‧ 电子书库

这种形式开始。C90标准勉强允许这种形式,但是C99标准不允许。因此即使您当前的编译器允许,也不要这么做。

您还将看到另一种形式:

阅读 ‧ 电子书库

有些编译器允许这种形式,但是还没有任何标准考虑接受它。因而,编译器不必接受这种形式,并且许多编译器也不这样做。再者说,如果坚持使用标准形式,那么当您把程序从一个编译器移到另一个编译器时也不会有问题。

三、注释

阅读 ‧ 电子书库

包含在/**/之间的部分是程序注释。使用注释的目的是使人们(包括您自己)更容易理解您的程序。C语言的注释的一个好处就是可以被放在任意的地方,甚至是和它要解释的语句在同一行。一个较长的注释可以单放一行,或者是多行。在/*和*/之间的所有内容都会被编译器忽略掉。下面是一些正确和不正确的注释形式:

阅读 ‧ 电子书库

C99增加了另一种风格的注释,它被普遍用在C++和Java里。这种新形式使用//符号,但这种注释被限制在一行里:

阅读 ‧ 电子书库

因为一行的结尾就标志着注释的结束,所以这种形式只在注释的开始处需要注释标志符号。

这种更新的形式是针对老形式存在的问题提出的。假设您有下列代码:

阅读 ‧ 电子书库

阅读 ‧ 电子书库

下面假设您决定删除第四行,结果不小心也删掉了第三行(*/)。代码将变成下面的样子:

阅读 ‧ 电子书库

现在编译器把第一行的/*和第四行的*/组合起来,使整个四行都变成一个注释,包括应作为代码的那一行。因为//形式只能在一行起作用,所以不会发生这种导致代码消失的问题。

某些编译器可能不支持C99的这一特性。还有的编译器可能需要更改设置,以支持C99的这一特性。

考虑到死板地保持一致性可能会产生令人乏味的效果,本书中使用了两种注释形式。

四、花括号,程序体和代码块

阅读 ‧ 电子书库

在程序清单2.1中,花括号划定了main函数的界线。通常,所有的C函数都使用花括号来表示函数体的开始与结束。它们的存在是必不可少的,因此不能丢掉它们。仅有花括号{}能起到这种作用,小括号()和中括号[]都不行。

花括号还可以用来把函数中的语句聚集到一个单元或代码块中。如果您熟悉Pascal、ADA、Modula-2,或者是Algol,您将清楚花括号在那些语言中同样用作开始与结束。

五、声明

阅读 ‧ 电子书库

程序中的这一行叫做声明语句(declaration statement)。该声明语句是C语言中最重要的功能之一。这个特殊的例子声明两件事情。第一,在函数中您有一个名为num的变量。第二,int说明num是一个整数,也就是说这个数没有小数点或者小数部分(int是一种数据类型)。编译器使用这个信息为变量num在内存中分配一个合适的存储空间。句末的分号指明这一行是C语言的一个语句或指令。分号是语句的一部分,不像在Pascal中那样只是两句之间的分隔符。

单词int是C语言的一个关键字,它代表C中最基本的一个数据类型。关键字是用来表达语言的单词,您不能将它们用于其他目的。例如,不能把int用作一个函数或者是变量的名字。然而,这些关于关键字的限制在该语言之外就不起作用了,所以把一只猫或者一个很可爱的小孩叫作int是可以的(尽管在某些地区,当地的习俗或者法律可能不允许这种选择)。

本例中的单词num是一个标识符(identifier),也就是您为一个变量、函数或其他实体所选的名字。这样该声明把一个特殊的标识符和计算机内存中的一个特殊的位置联系起来,同时确定了该位置存储的信息类型(也即数据类型)。

在C语言中,所有变量都必须在使用之前定义。这就意味着您必须提供程序中要用到的所有变量名的列表,并且指出每个变量的数据类型。声明变量被认为是一个好的编程技术,在C语言中必须这样做。

传统上,C语言要求必须在一个代码块的开始处声明变量,在这之前不允许任何其他语句。也就是说,main()函数将如下所示:

阅读 ‧ 电子书库

现在C99遵循C++的惯例,允许把声明放在代码块中的任何位置。然而,在首次使用变量之前仍然必须先声明它。因此,如果您的编译器支持这种功能,您的代码就可以像下面这样:

阅读 ‧ 电子书库

为了和旧系统更好地兼容,本书将遵守初始的约定(为了让某些新的编译器支持C99特性,你需要去开启设置)。

现在您可能有三个问题。首先,数据类型是什么?第二,可以选择什么样的名字?第三,为什么必须对变量进行声明?下面来看这些问题的答案。

1.数据类型

C语言可以处理多个数据种类(或类型),例如整数、字符和浮点数。把一个变量声明为整数类型或字符类型是计算机正确地存储、获取和解释该数据的基本前提。在下一章中您将学到各种各样的可用类型。

2.名字的选择

您应该尽量使用有意义的变量名(例如,如果你的程序用来数羊,那么使用sheep_count而不是x3)。如果名字不能表达清楚,可以用注释解释变量所代表的意思。通过这种方式使程序更易读是良好编程的基本技巧之一。

能够使用的字符的数量与C语言的不同实现有关。C99标准允许一个标识符最多可以有63个字符,除了外部标识符(见第12章“存储类、链接和内存管理”),后者只识别31个字符。与C90分别要求的31个字符和6个字符相比较,这是一个相当可观的进步,而更旧的编译器通常最多只允许8个字符。实际上,您使用的字符数量可以超过规定的最大值,但是编译器不会识别额外的字符。因此,如果一个系统最大字符数为8,那么Shakespeare和shakespencil将被看作是一个名字,因为它们的前8个字符相同(如果您想要一个以63个字符为限的例子,您可以自己编造)。

可供使用的字符有小写字母、大写字母、数字和下划线(_)。第一个字符必须是字母或者下划线。表2.1给出了一些例子。

表2.1           正确和错误的名字

 

 

正确的名字 错误的名字
wiggles $Z]**
cat2 2cat
Hot_Tub Hot-Tub
taxRate tax rate
_kcab don't

操作系统和C库通常使用以一个或两个下划线开始的名字(例如_kcab),因此您自己最好避免这种用法。标准的标识符通常都以一个或两个下划线开始,例如,库标识符就是这样。这样的标识符都是保留的。这也就是说使用它们虽然不是语法错误,但是这样会导致名字的混乱。

C语言的名字是区分大小写的,即把一个大写字母和与之对应的小写字母看作是不同的。因此,stars不同于Stars或STARS。

为了使C语言更加国际化,C99通过Universal Character Names(或称UCN)机制提供了一套扩展的字符集。附录B的“参考资料7:扩展的字符支持”将讨论这个增加部分。

3.声明变量的四点好处

有一些老的语言,例如FORTRAN和BASIC的最初形式都允许不声明变量而直接使用。那么,为什么C语言不采用这种简单易行的方法呢?有如下几个原因:

● 把所有变量放在一起,可以让读者更容易掌握程序的内容。如果您赋予变量有意义的名字(例如用taxrate代替r)将会更好地达到这个目的。如果名字不能表达清楚,可以用注释解释变量所代表的意思。通过这种方式使程序更易读是良好编程的基本技巧之一。
● 在您开始编写程序之前,考虑一下需要声明的变量会促使您做一些计划工作。程序需要在开始得到什么信息?到底想让程序得出什么结果?表示数据的最好方式是什么?
● 声明变量可以帮助避免程序中出现一类很难发现的细微错误,即变量名的错误拼写。例如,假设在某种语言中缺少变量声明,而您写了一个语句:

阅读 ‧ 电子书库

并且在程序的另一个地方错误地键入了:

阅读 ‧ 电子书库

无意地用字母l代替了数字1。那么这种语言将创建一个新的变量RADIUS1,并且使用它可能有的任何值(可能是零,也可能是垃圾数据)。CIRCUM将被赋予一个错误值,您可能需要花费大量的时间试图找出原因。在C语言中这种情况不会发生(除非您非常不明智地声明两个如此相似的变量名),因为当没有被声明过的RADIUS1出现时,编译器将会提出警告。
● 如果您没有声明所有变量,将不能编译您的C程序。如果前面的三个原因还不足以打动您,这个原因总可以让您认真地考虑一下了。

既然需要声明变量,那么在哪里声明它们?如前所述,C99以前的C要求在一个代码块的开始处声明变量。遵循这条规则的好处就是把所有变量声明分组放在一起,会更易于了解程序所要做的事情。当然,像C99现在所允许的那样把变量声明分散放置也有好处,那就是在准备为变量赋值之前声明变量,这样就不会忘记给变量赋值。但实际上,许多编译器还不支持C99的这一规则。

六、赋值

阅读 ‧ 电子书库

图2.2 赋值语句是C语言的基本操作之一

这行程序是一个赋值语句(assignment statement)。赋值语句是C语言的基本操作之一。这个特殊例子的意思是“把值1赋给变量num”。前面的int num;语句在计算机内存中为变量num分配了空间,该赋值语句在那个地方为变量存储了一个值。如果您想的话,以后您还可以给num赋另一个值;这就是把num称为变量的原因。注意赋值语句赋值的顺序是从右到左。同样,该语句也用分号结束,如图2.2所示。

七、printf()函数

阅读 ‧ 电子书库

所有这些行都使用了C语言的一个标准函数:printf()。圆括号表明printf是一个函数名。圆括号中包括的内容是从函数main()传递到函数printf()的信息。例如,第一行把I am a simple传递给printf()函数。这样的信息被称为参数(argument),更完整的名称是函数的实际参数(actual argument)(请参见图2.3所示)。printf()函数如何处理这个参数?程序将识别两个双引号之间的内容并把它们显示在屏幕上。

阅读 ‧ 电子书库

图2.3 带有一个参数的printf()函数

第一行printf()语句是如何在C语言中调用(call)或请求(invoke)一个函数的例子。只须键入函数的名字,把所需的一个或多个参数放进圆括号中。当程序运行到这一行时,控制权将转给该函数(在这个例子中是printf())。当函数完成了它所要做的工作,控制权将返回给原来的函数(调用函数),在这个例子中是main()。

那么下一个printf()行呢?引号中有字符\n,但并没有输出它们!发生了什么事情呢? \n字符的意思是开始新的一行。\n组合(依次键入这两个字符)代表一个称为换行符(newline character)的字符,它意味着“在下一行的最左边开始新的一行”。换句话说,打印换行字符的效果和在普通键盘上按下回车键一样。当键入printf()的这个参数时,为什么不直接按回车键呢?因为那将被看作是直接针对编辑器的命令,而不是作为存在源代码中的指令。也就是说,当您按回车键时,编辑器退出您正在输入的当前行,并开始新的一行。而换行符则影响程序的输出如何显示。

换行符是转义字符(Escape Sequence)的一个例子。转义字符通常用于代表难于表达的或是无法键入的字符。其他的例子比如\t代表Tab键,\b代表退格键。每个转义字符都用斜线字符(\)开始。在第3章“数据和C”中我们将回来讨论这个问题。

这样就解释了三个printf()语句只产生了两行输出的原因:第一个print指令中没有换行字符,而第二个和第三个都有。

最后一个printf()行中又有一个奇怪的问题:当输出这一行时%d起什么作用?回忆一下,这一行的输出结果是:

阅读 ‧ 电子书库

啊哈!当这一行输出时,数字1被符号组合%d代替了,而1是变量num的值。%d是一个占位符,其作用是指出输出num值的位置。该行和下面的BASIC语句类似:

阅读 ‧ 电子书库

实际上,C比BASIC所做的事情多一些。%告诉程序把一个变量在这个位置输出,d告诉程序将输出一个十进制(以10为基数)整数变量。printf()函数允许多种输出变量格式,包括十六进制(以16为基数)整数和带小数点的数。实际上,printf()中的f暗示着这是一种格式化(formating)的输出函数。每一种数据都有自己的说明符,本书在介绍新的数据类型时,也会介绍与之相应的说明符。

八、Return语句

阅读 ‧ 电子书库

return语句(返回语句)是程序的最后一个语句。在int main(void)中int表示main()函数的返回值应该是一个整数。C标准要求main()这样做。带有返回值的C语言函数要使用一个return语句,该语句包括关键字return,后面紧跟着要返回的值,然后是一个分号。对于main()函数来说,如果您漏掉了return语句,则大多数编译器将对您的疏忽提出警告,但仍将编译该程序。此时,您可以暂时把main()中的return语句看作是保持逻辑连贯性所需的内容。但对于某些操作系统(包括DOS和UNIX)而言,它有实际的用途。第11章将具体讨论这个话题。