程序细节 C Primer Plus

第2章 第二遍 程序细节

既然已经浏览了一遍程序清单2.1,我们就来仔细分析这个程序。我们再一次来考察程序中的单行语句。这次我们以每一行代码为出发点,深入探讨隐藏在代码背后的细节,以便为更全面地了解C语言编程特性打下基础。

一、#include 指示和头文件

#include <stdio.h>

这是程序的第一行。该语句的作用相当于您在文件中该行所在的位置键入了文件stdio.h的完整内容。实际上,它是一种剪切和粘贴操作,这样可以方便地在多个程序间共享公用的信息。

#include语句是C预处理器指令 ( preprocessor directive ) 的一个例子。通常, C 编译器在编译前要对源代码做一些准备工作: 这称为预处理 ( preprocessing )。

stdio.h 文件作为所有 C 编译包的一部分提供,它包含了有关输入和输出函数 ( 例如 printf ( ))的信息以供编译器使用。这个名字代表标准输入输出头文件 ( standard input/output header )。在 C 世界中,人们称出现在文件顶部的信息集合为头 ( header ), C 实现通常都带有许多头文件。

最重要的是头文件包括了建立最终的可执行程序时编译器需要用到的信息。例如,它们可以定义常量,或者说明函数名以及该函数如何使用。但是函数的实际代码被包含在一个预编译代码库文件中,而不是在头文件中。编译器的链接部分负责找到您所需要的库代码。简言之,头文件指引编译器把您的程序正确地组合在一起。

ISO/ANSI C 已经对必须提供哪些头文件制定了标准。有些程序需要包含stdio.h而有些则不需要。一个具体 C 实现的文档应该包括对 C 函数库中函数的描述。这些描述指出了函数所需要的头文件。例如,对printf ( ) 的描述指明需要使用 stdio.h。不包括合适的头文件也许不会影响一个具体的程序,但是最好不要这么做。本书每次用到库函数时,都使用 ISO/ANSI 标准为这些函数指定的包含文件。

为什么不内置输入输出语句

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

二、 main( ) 函数

int main (void)

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

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

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

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

main ()

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

您还将看到另一种形式:

void main ()

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

三、注释

/*一个简单的 C 程序* /

包含在/* */之间的部分是程序注释。使用注释的目的是使人们(包括您自己)更容易理解您的程序。C语言的注释的一个好处就是可以放在任意的地方,甚至是和它要解释的语句在同一行。一个较长的注释形式:

/* 这就是有效的C的注释 */

/* 将注释分成两行写,

也是可以的。 */

/*

也可以这样写。

*/

/*但这是无效的注释,因为没有结束标记

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

// 这种注释必须被限制在一行内。

int rigue; //这种注释也可以写在此处。

因为一行的结尾就标志着注释的结束,所以这种形式只在注释的开始需要注标志符号。这种更新的形式是针对老形式存在的问题提出的。假设您有下列代码:

/*

希望有效。

*/

x = 100;

y = 200;

/* 下面是其他内容 */

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

/*

希望有效。

y = 200;

/* 下面是其他内容 */

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

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

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

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

{

...

}

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

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

声明

int num;

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

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

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

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

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

int main ( ) //traditional rules

{

int doors;

int dogs;

doors = 5;

dogs = 3;

// other statements

}

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

int main ( ) //C99 rules

{

// some statements

int doors;

doors = 5; // first use of doors

// more statements

int dogs;

dogs = 3; // first use of dogs

// other statements

}

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

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

1.数据类型

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

2.名字的选择

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

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

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

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

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

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

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

3.声明变量的四点好处

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

● 把所有变量放在一起,可以让读者更容易掌握程序的内容。如果您赋予变量有意义的名字(例如用 taxrate 代替 r )将会更好地达到这个目的。如果名字不能表达清楚,可以用注释变量所代表的意思。通过这种方式使程序更易读是良好编程的基本技巧之一。

● 在您开始编写程序之前,考虑一下需要声明的变量会促使您做一些计划工作。程序需要在开始得到什么信息?到底想让程序得出什么结果?表示数据的最好方式是什么?

● 声明变量可以帮助避免程序出现一类很难发现的细微错误,既变量名的错误拼写。例如,假设某种语言缺少变量声明,而您写了一个语句:

RADIUS1 = 20.4

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

CIRCUM = 6.28 * RADIUS1,

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

● 如果您没有声明所有变量,将不能编译您的 C 程序。如果前面的三个原因还不足以打动您,这个原因总可以让您认真地考虑一下了。

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

赋值

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

带有普通的边框:

First Row
Second Row

带有粗的边框:

First Row
Second Row

带有很粗的边框:

First Row
Second Row

<a href="http://www.w3school.com.cn/html/" accesskey="h">HTML 教程</a>

<a href="http://www.w3school.com.cn/css/" accesskey="c">CSS 教程</a>

HTML 教程

CSS 教程

<b>注释:</b>请使用Alt + <i>accessKey</i> (或者 Shift + Alt + <i>accessKey</i>) 来访问带有指定快捷键的元素。

<nav>

<a href="#">Home</a>

<a href="#">Previous</a>

<a href="#">Next</a>

</nav>

Internet Explorer 9, Firefox, Opera, Chrome,Safari及标准浏览器 支持 <nav> 标签。

<nav> 标签定义导航链接的部分。

HTML 4.01 与 HTML 5 之间的差异

<nav> 标签是 HTML 5 中的新标签。

提示和注释

提示:如果文档中有“前后”按钮,则应该把它放到 <nav> 元素中。

全局属性

<nav> 标签支持 HTML 中的全局属性。

事件属性

<nav> 标签支持 HTML 中的事件属性。