C Primer Plus (第6版) 读书笔记_Chapter 2

本章介绍以下内容:
 ■ 运算符:=
 ■ 函数:main()、printf()
 ■ 编写一个简单的 C 程序
 ■ 创建整型变量,为其赋值并在屏幕上显示其值
 ■ 换行符
 ■ 如何在程序中写注释,创建包含多个函数的程序,发现程序的错误
 ■ 什么是关键字
 
    C 程序是什么样子的?浏览本书,能看到许多示例。初见 C 程序会觉得有些古怪,程序中有许多 { 、cp->tort 和 *ptr++ 这样的符号。然而,在学习 C 的过程中,对这些符号和 C 语言特有的其他符号会越来越熟悉,甚至会喜欢上它们。如果熟悉与 C 相关的其他语言,会对 C 语言有似曾相识的感觉。本章,我们从演示一个简单的程序示例开始,解释该程序的功能。同时,强调一些 C 语言的基本特性。
 
2.1 简单的 C 程序示例
   我们来看一个简单的 C  程序,如 程序清单 2.1 所示。该程序演示了用 C 语言编程的一些基本特性。请先通读程序清单 2.1,看看自己是否能明白该程序的用途,再认真阅读后面的解释。
 
 
 1 #include <stdio.h>
 2 
 3 int main(void)                            /* 一个简单的 C 程序 */
 4 {
 5     int num;                              /* 定义一个名为 num 的变量 */
 6     num = 1;                              /* 为 num 赋一个值 */
 7     
 8     printf("I am a simple ");             /* 使用 printf() 函数 */
 9     printf("computer.\n");
10     printf("My favorite number is %d because it is first.\n", num);
11     // favorite 喜爱的     first 第一
12     
13     getchar();            // 暂停等待用户输入
14     return 0;
15 
16 }
 
 
   如果你认为该程序会在屏幕上打印一些内容,那就对了!光看程序也许并不知道打印的具体内容,所以,运行该程序,并查看结果。首先,用你熟悉的编辑器(或者编译器提供的编辑器)创建一个包含程序清单 2.1所有内容的文件。给该文件命名,并 .c 作为扩展名,以满足当前系统对文件名的要求,例如,可以使用 first.c。现在,编译并运行该程序(查看第 1 章,复习该步骤的具体内容)。如果一切运行正常,该程序的输出应该是:
 
I am a simple computer.
My favorite number is 1 because it is first.
 
总而言之,结果在意料之中,但是程序中的 \n 和 %d 是什么?程序中有几行代码看起来有点奇怪。接下来,我们逐行解释这个程序。
 
程序调整
    程序的输出是否在屏幕上一闪而过?某些窗口环境会在单独的窗口运行程序,然后在程序运行结束后自动关闭窗口。如果遇到这种情况,可以在程序中添加额外的代码,让窗口等待用户按下一个键后才关闭。一种方法是,在程序的 return 语句前添加一行代码:
    getchar();
    这行代码会让程序等待击键,窗口会在用户按下一个键后才关闭。在第 8 章中会详细介绍 getchar() 的内容
 
 
2.2.1 第 1 遍:快速概要
    本节简述程序中的每行代码的作用。下一节详细讨论代码的含义。
    #include <stdio.h>     <— 包含另一个文件
    该行告诉编译器把 stdio.h 中的内容包含在当前程序中。stdio.h 是 C 编译器软件包的标准部分,它提供键盘输入和屏幕输出的支持。
    int main(void)    <— 函数名
    C 程序包含一个或多个函数,它们是 C 程序的基本模块。程序清单 2.1 的程序中有一个名为 main() 的函数。圆括号表明 main() 是一个函数名。 int 表明 main() 函数返回一个整数,void 表明 main() 不带任何参数。这些内容我们稍后详述。现在,只需记住 int 和 void 是标准 ANSI C 定义 main() 的一部分(如果使用 ANSI C 之前的编译器,请省略 void;考虑到兼容的问题,请尽量使用较新的 C 编译器)。
    /* 一个简单的 C 程序 */     <— 注释
    注释在  /* 和 */ 两个符号之间,这些注释能提高程序的可读性。注意,注释只是为了帮助读者理解程序,编译器会忽略它们。
    {                        <— 函数体开始
    左花括号表示函数定义开始,右花括号(}) 表示函数定义结束。
    int num;            <— 声明
    该声明,将使用一个名为 num 的变量,而且 num 是 int (整数)类型。
    num = 1;           <— 赋值表达式语句
    语句 num = 1; 把值 1 赋给名为 num 的变量。
    printf("I am a simple ");        <— 调用一个函数
    该语句使用 printf() 函数,在屏幕上显示 I am a simple,光标停在同一行。printf() 是标准的 c 库函数。在程序中使用函数叫做 调用函数。
    printf(“computer.\n”);            <— 调用另一个函数
    接下来调用的这个 printf() 函数在上条语句打印出来的内容后面加上“computer.”,代码 \n 告诉计算机另起一行,即把光标移至下一行。
    printf("My favorite number is %d because it is first.\n", num);
    最后调用的 printf() 把 num 的值(1)内嵌在用双引号括起来的内容中一并打印。%d 告诉计算机以何种形式输出 num 的值,打印在何处。
    return 0;                    <— return 语句
    C 函数可以给调用方提供(或返回)一个数。目前,可暂时把该行看作是结束 main() 函数的要求。
    }                                <— 结束
    必须以右花括号表示程序结束。
 
 
2.2.2 第 2 遍:程序细节
    浏览完程序清单 2.1 后,我们来仔细分析这个程序,再次强调,本节将逐行分析程序中的代码,以每行代码为出发点,深入分析代码背后的细节,为更全面地学习 C 语言编程的特性夯实基础。
    1. #include 指令 和 头文件
    #include <stdio.h>
    这是程序的第 1 行。#include <stdio.h> 的作用相当于把 stdio.h 文件中的所有内容都输入该行所在的位置。实际上,这是一种 “拷贝-粘贴” 的操作。include 文件提供了一种方便的途径共享许多程序共有的信息。
     #include 这行代码是一条 C 预处理器指令(preprocessor directive)。通常,C 编译器在编译前会对源代码做一些准备工作,即 预处理(preprocessing)。
    所有的 C 编译器软件包都提供 stdio.h 文件。该文件中包含了供编译器使用的输入和输出函数(如,printf() )信息。该文件名的含义是 标准输入/输出 头文件。通常,在 C 程序顶部的信息集合被称为 头文件(header)。
    在大多数情况下,头文件包含了编译器创建最终可执行程序要用到的信息。例如,头文件中可以定义一些常量,或者指明函数名以及如何使用它们。但是,函数的实际代码在一个预编译代码的库文件中。简而言之,头文件帮助编译器把你的程序正确地组合在一起。
   ANSI/ISO C 规定了 C 编译器必须提供哪些头文件。有些程序要包含 stdio.h,而有些不用。特定 C 实现的文档中应该包含了对 C 库函数的说明。这些说明确定了哪些函数需要包含哪些头文件。例如,要使用 printf() 函数,必须包含 stdio.h 头文件。省略必要的头文件可能不会影响某一特定程序,但是最好不要这样做。本书每次用到库函数,都会用 #include 指令包含 ANSI/ISO 标准指定的头文件。
 
注意     为何不内置输入和输出
    读者一定很好奇,为何不把输入和输出这些基本功能内置在语言中。原因之一是,并非所有的程序都会用到 I/O (输入/输出)包。轻装上阵表现了 C 语言的哲学。正是这种经济使用资源的原则,使得 C 语言成为流行的嵌入式编程语言(例如,编写控制汽车自动燃油系统或蓝光播放机芯片的代码)。 #include 中的 # 符号表明,C 预处理器在编译器接手之前处理这条指令。本书后面章节中会介绍更多预处理器指令的示例,第 16 章将更详细地讨论相关内容。
 
 
    2. main() 函数
    int main(void);
    程序清单 2.1 中第 2 行表明该函数名为 main。的确,main 是一个极其普通的名称,但是这是唯一的选择。 C 程序一定从 main() 函数开始执行(目前不必考虑例外的情况)。除了 main() 函数,你可以任意命名其他函数,而且 main() 函数必须是开始的函数。圆括号有什么功能呢?用于识别 main() 是一个函数。很快你将学更多的函数。就目前而言,只需记住 函数是 C 程序的基本模块。
    int 是 main() 函数的返回类型。这表明 main() 函数返回的值是整数。返回到哪里?返回给操作系统。我们将在第 6 章中再来探讨这个问题。
    通常,函数名后面的圆括号中包含一些传入函数的信息。该例中没有传递任何信息。因此,圆括号内是单词 void (第 11 章将介绍把信息从 main() 函数传回操作系统的另一种形式)。
    如果浏览旧式的 C 代码,会发现程序以如下形式开始:
     main()
    C90 标准勉强接受这种形式,但是 C99 和 C11 标准不允许这样些。因此,即使你使用的编译器允许,也不要这样写。
    你还会看到下面这种形式:
     void main()
    一些编译器允许这样写,但是所有的标准都未认可这种写法。因此,编译器不必接受这种形式,而且许多编译器都不能这样写。需要强调的是,只要坚持使用标准形式,把程序从一个编译器移至另一个编译器时就不会出现什么问题。
    3. 注释
    /* 一个简单的程序 */
     在程序中,被 /**/两个符号括起来的部分是程序的注释。写注释能让他人(包括自己)更容易明白你所写的程序。C 语言注释的好处之一是,可将注释放在任意地方,甚至是与要解释的内容在同一行。较长的注释可单独放一行或多行。在 /* 和 */ 之间的内容都会被编译器忽略。下面列出了一些有效和无效的注释形式:
   /* 这是一条注释 */
   /* 这也是一条注释,
      被分成两行。 */
   /*
    也可以这样写注释
   */
    
    /* 这条注释无效,因为缺少了结束标记
 
     C99 新增了另一种风格的注释,普遍用于 C++ 和 JAVA。这种新风格使用 // 符号创建注释,仅限于单行。
    // 这种注释只能写成一行。
    int rigue;        // 这种注释也可置于此。
    因为一行末尾就标志着注释的结束,所以这种风格的注释只需在注释开始处标明 // 符号即可。
    这种新形式的注释是为了解决旧式注释存在的潜在问题。假设有下面的代码:
 
1 /*
2     希望能运行。
3 */
4 x = 100;
5 y = 200;
6 /* 其他内容已省略 */

 接下来,假设你决定删除第 4 行,但不小心把第 3 行删除了(*/)。代码如下:

1 /*
2     希望能运行
3 y = 200;
4 /* 其他内容已省略 */
现在,编译器把第 1 行的 /* 和 第 4 行的 */ 配对,导致 4 行代码全都成了注释(包括应作为代码的那一行)。而 // 形式的注释只对单行有效,不会导致这种“消失代码”的问题。
    一些编译器可能不支持这一特性。还有一些编译器需要更改设置,才能支持 C99 或 C11 的特性。
    考虑只用一种注释风格过于死板乏味,本书在示例中采用两种风格的注释。
 
    4. 花括号、函数体和块
    {
        ...
    }
 
    程序清单 2.1 中,花括号把 main() 函数括起来。 一般而言,所有的 C 函数都使用花括号标记函数体的开始和结束。这是规定,不能省略。只有花括号({})能起这种作用,圆括号(()) 和 方括号 ([]) 都不行。
     花括号还可用于把函数中的多条语句合并为一个单元或块。如果读者熟悉 Pascal 、ADA、Modula-2 或者 Algol,就会明白花括号在 C 语言中的作用类似于这些语言中的 begin 和 end。
 
    5. 声明
    int num;
    程序清单 2.1 中,这行代码叫作 声明 (declaration)。声明是 C 语言最重要的特性之一。在该例中, 声明完成了两件事。其一,在函数中有一个名为 num 的变量(variable)。其二,int 表明 num 是一个整数(即,没有小数点或小数部分的数)。int 是一种数据类型。编译器使用这些信息为 num 变量在内存中分配存储空间。分号在 C 语言中是大部分语句和声明的一部分,不像在 Pascal 中只是语句间的分隔符。
     int 是 C 语言的一个关键字(keyword),表示一种基本的 C 语言数据类型。关键字是语言定义的单词,不能做其他用途。例如,不能用 int 作为函数名或变量名。但是,这些关键字在该语言以外不起作用,所以把一只猫或一个可爱的小孩叫 int 是可以的(尽管某些地方的当地习俗或法律可能不允许)。
     示例中的 num 是一个标识符(identifier),也就是一个变量、函数或其他实体的名称。因此,声明把特定标识符与计算机内存中的特定位置联系起来,同时也确定了储存在某位置的信息类型或数据类型。
     在 C 语言中,所有变量都必须先声明才能使用。这意味着必须列出程序中用到的所有变量名及其类型。
    以前的 C 语言,还要求把变量声明在块的顶部,其他语句不能在任何声明的前面。也就是说,main() 函数体如下所示:
1 int main()        // 旧规矩
2 {
3     int doors;
4     int dogs;
5     doors = 5;
6     dogs = 3;
7     // 其他语句
8 }

    C99 和 C11 遵循 C++ 的惯例,可以把声明放在块中的任何位置。尽管如此,首次使用变量之前一定要先声明它。因此,如果编译器支持这一新特性,可以这样编写上面的代码:

 1 int main()            // 目前的 C 规则
 2 {
 3     // 一些语句
 4     int doors;
 5     doors = 5;        // 第 1 次 使用 doors
 6     // 其他语句
 7     int dogs;
 8     dogs = 3;        // 第 1 次 使用 dogs
 9     // 其他语句    
10 
11 }
 为了与旧系统更好地兼容,本书沿用最初的规则(即,把变量声明都写在块的顶部)
    现在,读者可能有 3 个问题:什么是数据类型?如何命名?为何要声明变量?请往下看。
 
数据类型
    C 语言可以处理多种类型的数据,如 整数、字符和浮点数,把变量声明为整型或字符类型,计算机才能正确地储存、读取和解释数据。下一章将详细介绍 C 语言中的各种数据类型。
 
命名
    给变量命名时要使用有意义的变量名或标识符(如,程序中需要一个变量数羊,该变量名应该时 sheep_count 而不是 x3)。如果变量名无法清楚地表达自身的用途,可在注释中进一步说明。这是一种良好的编程习惯和编程技巧。
    C99 和 C11 允许使用更长的标识符名,但是编译器只识别前 63 个字符。对于外部标识符(参阅第 12 章),只允许使用 31 个字符。(以前 C90 只允许 6 个字符,这是一个很大的进步。旧式编译器通常最多只允许使用 8 个字符。)实际上,你可以使用更长的字符,但是编译器会忽略超出的字符。也就是说,如果有两个标识符名都有 63 个字符,只有一个字符不同,那么编译器会识别这是两个不同的名称。如果两个标识符都是 64 个字符,只有最后一个字符不同,那么编译器可能将其视为同一个名称,也可能不会。标准并未定义在这种情况下会发生什么。
     可以用 小写字母、大写字母、数字和下划线(_)来命名。而且,名称的第 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 和 C11 根据通用字符名(即 UCN)机制添加了扩展字符集。其中包含了除英文字母以外的部分字符。欲了解详细内容,请参阅附录B的 “参考资料 VII:扩展字符支持”
 
声明变量的 4 个理由
    一些更老的语言(如,FORTRAN 和 BASIC 的最初形式)都允许直接使用变量,不必先声明。为何 C 语言不采用这种简单易行的方法?原因如下:
■ 把所有的变量放在一处,方便读者查找和理解程序的用途。如果变量名都是有意义的(如,taxtate 而不是 r),这样做的效果很好。如果变量名无法表述清楚,在注释中解释变量的含义。这种方法让程序的可读性更高。
■ 声明变量会促使你在编写程序之前做一些计划。程序在开始时要获得哪些信息?希望程序如何输出,表示数据最好的方式是什么?
■ 声明变量有助于发现隐藏在程序中的小错误,如变量名拼写错误。例如,假设在某些不需要声明就可以直接使用变量的语言中,编写如下语句:
RADIUS1 = 20.4;
在后面的程序中,误写成:
CIRCUM = 6.28 * RADIUSl
你不小心把数字 1 打成小写字母 l 。这些语言会创建一个新的变量 RADIUSl,并使用该变量中的值(也许是 0,也许是垃圾值),导致赋给 CIRCUM 的值是错误值。你可能要花很久时间才能查出原因。这样的错误在 C 语言中不会发生(除非你很不明智地声明了两个极其相似的变量),因为编译器在发现未声明的 RADIUSl 是会报错。
■ 如果事先未声明变量,C 程序将无法通过编译。如果前几个理由还不足以说服你,这个理由总可以让你认真考虑一下了。
 
    如果要声明变量,应该声明在何处?前面提到过,C99 之前的标准要求把声明都置于块的顶部,这样规定的好处是:把声明放在一块更容易理解程序的用途。C99 允许在需要时才声明变量,这样做的好处是:在给变量赋值之前声明变量,就不会忘记给变量赋值。但是实际上,许多编译器都还不支持C99。
 
6. 赋值
    num = 1;
    程序清单中的这行代码是赋值表达式语句。赋值是 C 语言的基本操作之一。该行代码的意思是 “把值 1 赋给变量 num”。在执行 int num; 声明时,编译器在计算机内存中为变量 num 预留了空间,然后在执行这行赋值表达式语句时,把值储存在之前预留的位置。可以给 num 赋不同的值,这就是 num 之所以被称为 变量 (variable) 的原因。注意,该赋值表达式语句从右侧把值赋到左侧。另外,该语句以分号结尾。
 
 
7. printf() 函数
    printf("I am a simple ");
    printf("computer.\n");
    printf("My favorite number is %d because it is first.\n", num);
    这 3 行都使用了 C 语言的一个标准函数:printf() 。圆括号表明 printf() 是一个函数名。圆括号中的内容是从 main() 函数传递给 printf() 函数的信息。例如,上面的第 1 行 把 I am a simple 传递给 printf() 函数。该信息被称为 参数,或者更确切地说,是函数的实际参数(actual argument),如图 2.3 所示。(在 C 语言中,实际参数(简称实参)是传递给函数的特定值,形式参数(简称形参)是函数中用于储存值的变量。第 5 章中将详述相关内容。)printf() 函数用参数来做什么?该函数会查看双引号中的内容,并将其打印在屏幕上。
    printf("That's mere contrariness");
               --------------------------------
                    ^ 实际参数 ^
    第 1 行 printf() 演示了在 C 语言中如何调用函数。只需输入函数名,把所需的参数填入圆括号即可。
当程序运行到这一行时,控制权被转给已命名的函数(该例中是 printf() )。函数执行结束后,控制权被返回至 主调函数(calling function) 该例中是 main()。
    第 2 行 printf() 函数的双引号中的 \n 字符并未输出。这是为什么?\n 的意思是换行。\n 组合(依次输入这两个字符)代表一个换行符(newline character)。对于 pirntf() 而言,它的意思是“在下一行的最左边开始新的一行”。也就是说,打印换行符的效果与在键盘按下 Enter 键相同。既然如此,为何不在键入 printf() 参数时直接使用 Enter 键?因为编译器可能认为这是直接的命令,而不是储存在源代码中的指令。换句话说,如果直接按下 Enter 键,编辑器会退出当前行并开始新的一行。但是,换行符仅会影响程序输出的显示格式。
    换行符是一个转义序列(escape sequence)。转义序列用于代表难以表示或无法输入的字符。如,\t 代表 Tab 键,\b 代表 Backspace 键(退格键)。每个转义序列都以反斜杠字符(\)开始。我们在第 3 章中再来探讨相关内容。
    这样,就解释了为什么 3 行 printf() 语句只打印出两行:第 1 个 printf()打印的内容中不含换行符,但是第 2 个 和第 3 个 printf() 中都有换行符。
    第 3 个 printf() 还有一些不明之处:参数中的 %d 在打印时有什么作用?先来看该函数的输出:
    My favorite number is 1 because it is first.
    对比发现,参数中的 %d 被数字 1 代替了,而 1 就是变量 num 的值。%d 相当于是一个占位符,其作用是指明输出 num 值的位置。该行和下面的 BASIC 语句很像:
    PRINT "My favorite number is "; num; " because it is first."
    实际上,C 语言的 printf() 比 BASIC 的这条语句做的事情多一些。% 提醒程序,要在该处打印一个变量,d 表明把变量作为十进制整数打印。printf() 函数名中的 f 提醒用户,这是一种 格式化 打印函数。printf() 函数有多种打印变量的格式,包括小数和十六进制整数。后面章节在介绍数据类型时,会详细介绍相关内容。
 8. return 语句
    return 0;
    return 语句【在 C 语言中,return 语句是一种跳转语句】是程序清单 2.1 的最后一条语句。int main(void) 中的 int 表明函数应返回一个整数。C 标准要求 main () 这样做。有返回值的 C 函数要有 return 语句。该语句以 return 关键字开始,后面是待返回的值,并以分号结尾。如果遗漏 main() 函数中的 return 语句。程序在运行至最外面的右花括号(})时会返回 0。 因此,可以省略 main() 函数末尾的 return 语句。但是,不要在其他有 返回值的函数中漏掉它。因此,强烈建议读者养成在 main() 函数中保留 return 语句的好习惯。在这种情况下,可将其看作是统一代码风格。但对于某些操作系统(包括 Linux 和 UNIX),return 语句有实际的用途。第 11 章再详述这个主题。
 
2.3 简单程序的结构
    在看过一个具体的程序示例后,我们来了解一下 C 程序的基本结构。 程序由一个或多个函数组成,必须有 main() 函数。函数由函数头和函数体组成。函数头包括函数名、传入该函数的信息类型和函数的返回类型。通过函数名后的圆括号可识别出函数,圆括号里可能为空,可能有参数。函数体被花括号括起来,由一系列语句、声明组成,本章的程序示例中有一条声明,声明了程序使用的变量名和类型。然后是一条赋值表达式语句,变量被赋给一个值。接下来是 3 条 printf()语句,调用 printf() 函数 3 次。最后,main() 以 return 语句结束。
    
 
    简而言之,一个简单的 C 程序的格式如下:
1 #include <stdio.h>
2 {
3         语句
4          return 0;
5 }
6 
7 (大部分语句都以分号结尾)
 
2.4 提高程序可读性的技巧
     编写可读性高的程序是良好的编程习惯。可读性高的程序更容易理解,以后也更容易修改和更正。提高程序的可读性还有助于你理清编程思路。
    前面介绍过两种提高程序可读性的技巧: 选择有意义的函数名和写注释。注意,使用这两种技巧时应相得益彰,避免重复啰嗦。如果变量名是 width,就不必写注释说明该变量表示宽度,但是如果变量名是 video_routine_4, 就要解释一下该变量名的含义。
    提高程序可读性的第 3 个技巧是: 在函数种用空行分隔概念上的多个部分。例如,程序清单 2.1 中用空行把声明部分和程序的其他部分区分开来。C 语言并未规定一定要使用空行,但是多使用空行能提高程序的可读性。
    提高程序可读性的第 4 个技巧是: 每条语句个占一行。同样,这也不是 C 语言的要求。C 语言的格式比较自由,可以把多条语句放在一行,也可以每条语句独占一行。下面的语句都没问题,但是不好看:
1 int main(void){ int four; four
2 =
3 4
4 ;
5 printf(
6         "%d\n",
7 four);return 0;}
分号告诉编译器一条语句在哪里结束、下一条语句在哪里开始。如果按照本章示例的约定来编写代码,程序的逻辑会更清晰。
 
  
 
2.5 进一步使用 C 
    本章的第 1 个程序相当简单,下面的程序清单 2.2 也不太难。
 
程序清单 2.2 fathm_ft.c 程序
 1 // fathm_ft.c -- 把 2 音寻转换为英寸。
 2 
 3 #include <stdio.h>
 4 
 5 int main(void)
 6 {
 7     int feet, fathoms;            // feet 英尺 fathoms 音寻
 8     
 9     fathoms = 2;
10     feet = 6 * fathoms;
11     printf("There are %d feet in %d fathoms!\n", feet, fathoms);
12     printf("Yes, I said %d feet!\n", 6 * fathoms);
13 
14     getchar();            // 暂停屏幕 等待用户输入 
15     return 0;
16 }
17 
18 /************************************
19 输出结果:
20 -------------------------------------
21 There are 12 feet in 2 fathoms!
22 Yes, I said 12 feet!
23 -------------------------------------
24 *************************************/
与程序清单 2.1 相比,以上代码有什么新内容?这段代码提供了程序描述,声明了多个变量,进行了乘法运算,并打印了两个变量的值。下面我们更详细地分析这些内容。
 
2.5.1 程序说明
    程序在开始处有一条注释(使用新的注释风格),给出了文件名和程序的目的。写这种程序说明很简单、不费时,而且在以后浏览或打印程序时有帮助。
 
2.5.2 多条声明
    接下来,程序在一条声明中声明了两个变量,而不是一个变量。为此, 要在声明中用逗号隔开两个变量(feet 和 fathoms)也就是说:
int feet, fathoms;
int feet;
int fathoms;
等价。
 
2.5.3 乘法
    然后,程序进行了乘法运算。利用计算机强大的计算能力来计算 6 乘以 2。C 语言和许多其他语言一样,用 * 表示乘法。因此,语句
     feet = 6 * fathoms;
    的意思是  "查找变量 fathoms 的值,用 6 乘以该值,并把计算结果赋给变量 feet"。
 
2.5.4 打印多个值
    最后,程序以新的方式使用 printf() 函数。如果编译并运行该程序,输出应该是这样:
    There are 12 feet in 2 fathoms!
    Yes, I said 12 feet!
    程序的第一个 printf() 中进行了两次替换。双引号后面的第 1 个变量(feet)替换了双引号中的第 1 个 %d ;双引号后面的第 2 个变量(fathoms)替换了双引号中的第 2 个 %d 。注意,待输出的变量列于双引号的后面。还要注意,变量之间要用逗号隔开。
    第 2 个 printf() 函数说明待打印的值不一定是变量,只要可求值得出合适类型值的项即可,如 6 * fathoms。
    该程序涉及的范围有限,但它是把音寻转换成英寸程序的核心部分。我们还需要把其他值通过交互的方式赋给 feet,其方法在后面章节中介绍。
 
2.6 多个函数
    到目前为止,介绍的几个程序都只使用了 printf() 函数。程序清单 2.3 演示了除了 main() 以外,如何把自己的函数加入程序中。
 
程序清单 2.3 two_func.c 程序
 1 // * two_func.c -- 一个文件中包含两个函数
 2 
 3 #include <stdio.h>
 4 
 5 void butler(void);        /* ANSI/ISO C 函数原型 */
 6 
 7 int main(void)
 8 {
 9     printf("I will summon the butler function.\n");
10     butler();
11     printf("Yes. Bring me some tea and writeable DVDs.\n");
12 
13     getchar();
14     return 0;
15 }
16 
17 void butler(void)        /* 函数定义开始 */
18 {
19 
20     printf("You rang, sir?\n");
21 
22 }
23 
24 /*******************************************
25 输出结果:
26 --------------------------------------------
27 I will summon the butler function.
28 You rang, sir?
29 Yes. Bring me some tea and writeable DVDs.
30 --------------------------------------------
31 
32 ********************************************/
butler() 函数在程序中出现了 3 次。 第 1 次是函数原型(prototype),告知编译器在程序中要使用该函数;第 2 次以 函数调用(function call)的形式出现在 main() 中;最后一次出现在函数定义(function definition)中,函数定义即是函数本身的源代码。下面逐一分析。
     C90 标准新增了函数原型,旧式的编译器可能无法识别(稍后我们将介绍,如果使用这种编译器应该怎么做)。函数原型是一种声明形式,告知编译器正在使用某函数,因此函数原型也被称为函数声明(function declaration)。函数原型还指明了函数的属性。例如,butler() 函数原型中第 1 个 void 表明,butler()函数没有返回值(通常,被调函数会向主调函数返回一个值,但是 butler()函数没有)。第 2 个 void (butler(void)中的 void)的意思是 butler() 函数不带参数。因此,当编译器运行至此,会检查 butler() 是否使用得当。注意,void 在这里的意思是“空的”,而不是“无效”。
    早期的 C 语言支持一种更简单的函数声明,只需指定返回类型,不用描述参数:
    void butler();
    早期的 C 代码中的函数声明就类似上面这样,不是现在的函数原型。C90、C99 和 C11 标准都承认就版本的形式,但是也表明了会逐渐淘汰这种过时的写法。如果要使用以前写的 C 代码,就需要把旧式声明转换成函数原型。
    接下来我们继续分析程序。在 main() 中调用 butler() 很简单,写出函数名和圆括号即可。当 butler() 执行完毕后,程序会继续执行 main() 中的下一条语句。
    程序的最后部分是 butler() 函数的定义,其形式和 main() 相同,都包含函数头和用花括号括起来的函数体。函数头重述了函数原型的信息:butler() 不带任何参数,且没有返回值。如果使用老式编译器,请去掉圆括号中的 void 。
    这里要注意,何时执行 butler() 函数取决于它在 main() 中被调用的位置,而不是 butler() 的定义在文件中的位置。例如,把 butler() 函数的定义放在 main() 定义之前,不会改变程序的执行顺序,butler() 函数仍然在两次 printf() 调用之间被调用。 记住,无论 main() 在程序文件处于什么位置,所有的 C 程序都从 main() 开始执行。【待测试】但是,C 的惯例是把 main() 放在开头,因为它提供了程序的基本框架。
    C 标准建议,要为程序中用到的所有函数提供函数原型。标准 include 文件(包含文件)为标准库函数提供了函数原型。例如,在 C 标准中,stdio.h 文件包含了 printf() 的函数原型。
 
2.7 调试程序
    现在,你可以编写一个简单的 C 程序,但是可能会犯一些简单的错误。程序的错误通常叫做 bug,找出并修正错误的过程叫做调试(debug),程序清单 2.4 是一个有错误的程序,看看你能找出几处。
 
程序清单 2.4 nogood.c 程序
 1 /* nogood.c -- 有错误的程序 */
 2 
 3 #include <stdio.h>
 4 
 5 int main(void)
 6 (
 7     int n, int n2, int n3;
 8     /* 该程序有多处错误
 9     n = 5;
10     n2 = n * n;
11     n3 = n2 * n2;
12     printf("n = %d, n squared = %d, n cubed = %d\n", n, n2, n3)
13     
14     return 0;
15 )
16 
17 /************************************************************
18 编译不通过 
19 语法错误:
20 1) main() 函数的 函数体错用了 () 圆括号,应该改用 {} 花括号
21 2)定义变量格式出错,应该改为:
22    int n, n2, n3;
23    或者
24    int n;
25    int n2;
26    int n3;
27 3) 注释没有成对出现 少了后面的 * /
28 4) printf() 函数最后的少了分号 ;
29 
30 语义错误:
31     程序中的 n3 原意是代表 n 的立方,但是却写成了 n2 * n2,由于
32     n2 里的值是 n 的平方,也就是 25 ,所以 n3 = n2 * n2;
33     等价于 n3 = 25 * 25; 因此得出的结果与程序原意不符
34     应将其修改为 n3 = n2 * n; 或者 n3 = n * n * n;
35 *************************************************************/
C 语言的语法错误指的是,把有效的 C 符号放在错误的地方。检查程序的语法错误是编译器的工作之一。
    语义错误是指意思上的错误。如果遵循了 C 规则,但是结果不正确,那就是犯了语义错误。
 
2.7.3 程序状态
    通过逐步跟踪程序的执行步骤,并记录每个变量,便可监视程序的状态。程序状态(program state)是在程序的执行过程中,某给定点上所有的变量值的集合。它是计算机当前状态的一个快照。
    定位语义错误的另一种方法是:在程序中的关键点插入额外的 printf() 语句,以监视指定变量值的变化。
    检测程序状态的第 3 种方法是使用调试器。调试器(debugger)是一种程序,让你一步一步运行另一个程序,并检查该程序变量的值。
 
2.8 关键字和保留标识符
    关键字是 C 语言的词汇。它们对 C 而言比较特殊,不能用它们作为标识符(如,变量名)。许多关键字用于指定不同的类型,如 int 。还有一些关键字(如,if)用于控制程序中语句的执行顺序。
表 2.2 ISO C 关键字
 
auto extern short while
break float signed _Alignas
case for sizeof _Alignof
char goto static _Atomic
const if struct _Bool
continue inline switch _Complex
default int typedef _Generic
do long union _Imaginary
double register unsigned _Noreturn
else restrict void _Static_assert
enum return volatile _Thread_local

  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
如果使用关键字不当(如,用关键字作为变量名),编译器会将其视为语法错误。还有一些保留标识符(resrved identifier),C 语言已经指定了它们的用途或保留它们的使用权,如果你使用这些标识符来表示其他意思会导致一些问题。因此,尽管它们也是有效的名称,不会引起语法错误,也不能随便使用。保留标识符包括那些以下划线字符开头的标识符和标准库函数名,如 printf()。
 
2.9 关键概念
    理解什么是 C 程序。可以把程序看作是你希望计算机如何完成任务的描述。编译器负责处理一些细节工作,由于编译器不具有真正的智能,所以你必须用编译器能理解的术语表达你的意图,这些术语就是 C 语言标准规定的形式规则
 
2.10 本章小结
    C 程序由一个或多个 C 函数组成。每个 C 程序必须包含一个 main() 函数,这是 C 程序要调用的第 1 个 函数。简单的函数由函数头和后面的一对花括号组成,花括号中是由声明、语句组成的函数体。
    在 C 语言中,大部分语句都以分号结尾。声明为变量创建变量名和标识该变量中储存的数据类型。变量名是一种标识符。赋值表达式语句把值赋给变量,或者更一般地说,把值赋给存储空间。函数表达式语句用于调用指定的已命名函数。调用函数执行完毕后,程序会返回到函数调用后面的语句继续执行。
    一门语言的语法是一套规则,用于管理语言中各有效语句组合在一起的方式。语句的语义是语句要表达意思。
    关键字是 C 语言的词汇
 
 
 
2.11 复习题
1. C 语言的基本模块是什么?
    函数
2. 什么是语法错误?
    C 语言的语法错误指的是,把有效的 C 符号放在错误的地方。
3.什么是语义错误?
    语义错误是指意思上的错误。
4. Indiana Sloth 编写了下面的程序,并征求你的意见。请帮助他评定。
 1 include studio.h
 2 int main{void}  /* 该程序打印一年有多少周 /*
 3 (
 4     int s
 5     s := 56;
 6     printf(There are s weeks in a year.);
 7     return 0;
 8 
 9 
10 
11 ------------------ 以下为修改程序 ------------------------------
12 
13 #include <stdio.h>
14 int main(void)            /* 该程序打印一年有多少周 */
15 {
16     int s;
17     
18     s = 56;
19     printf("There are %d weeks in a year.", s);
20     
21     return 0;
22 }
23 
24 /***********************************************************
25 输出结果:
26 ------------------------------------------------------------
27 There are 56 weeks in a year.
28 ------------------------------------------------------------
29 ************************************************************/

5. 假设下面的 4 个例子都是完整程序中的一部分,它们都输出什么结果?

 1 a.     printf("Baa Baa Black Sheep.");
 2        printf("Have you any wool?\n");
 3 /****************************************************************
 4 输出结果:
 5 -----------------------------------------------------------------
 6 Baa Baa Black Sheep.Have you any wool?
 7 
 8 -----------------------------------------------------------------
 9 ****************************************************************/
10 
11 b.     printf("Begone!\nO Creature of lard!\n");
12 /****************************************************************
13 输出结果:
14 -----------------------------------------------------------------
15 Begone!
16 O Creature of lard!
17 
18 -----------------------------------------------------------------
19 ****************************************************************/
20 
21 c.     printf("What?\nNo/nfish?\n");
22 /****************************************************************
23 输出结果:
24 -----------------------------------------------------------------
25 What?
26 NO/nfish?
27 
28 -----------------------------------------------------------------
29 ****************************************************************/
30 
31 d.     int num;
32        num = 2;
33        printf("%d + %d = %d", num, num, num + num);
34 /****************************************************************
35 输出结果:
36 -----------------------------------------------------------------
37 2 + 2 = 4
38 -----------------------------------------------------------------
39 ****************************************************************/
6. 在 main、int、function、char、= 中,哪些是 C 语言的关键字?
    int        char
 
7. 如何以下面的格式输出变量 words 和 lines 的值(这里,3020 和 350 代表两个变量的值)?
    There were 3020 words and 350 lines.
    printf("There were %d words and %d lines.", words, lines);
 
8. 考虑下面的程序:
 1 #include <stdio.h>
 2 int main(void)
 3 {
 4     int a, b;
 5     
 6     a = 5;
 7     b = 2;        /* 第 7 行 */            // a = 5, b = 2;
 8     b = a;        /* 第 8 行 */            // a = 5, b = 5;
 9     a = b;        /* 第 9 行 */            // a = 5, b = 5;
10     printf("%d %d\n", b, a);
11     return 0;
12 }
13 
14 // 请问,在执行完第 7 、第 8 、第 9 行后,程序的状态分别是什么?
15 
16 /****************************************************
17 输出结果:
18 -----------------------------------------------------
19 5 5
20 -----------------------------------------------------
21 *****************************************************/

9. 考虑下面的程序:

 1 #include <stdio.h>
 2 int main(void)
 3 {
 4     int x, y;
 5     
 6     x = 10;
 7     y = 5;            /* 第 7 行 */           // x = 10, y = 5;      
 8     y = x + y;        /* 第 8 行 */           // x = 10, y = 15;
 9     x = x * y;        /* 第 9 行 */           // x = 150, y = 15; 
10     printf("%d %d\n", x, y);              // 输出为:150 15
11     return 0;                    
12 }
13 
14 // 请问,在执行完第 7 、第 8 、第 9 行后,程序的状态分别是什么?
15 
16 /****************************************************
17 输出结果:
18 -----------------------------------------------------
19 150 15
20 
21 -----------------------------------------------------
22 *****************************************************/

2.12 编程练习

 1 /*
 2  1. 编写一个程序,调用一次 printf() 函数,把你的姓名打印在一行。
 3     再调用一次 printf() 函数,把你的姓名分别打印在两行。
 4     然后,再调用两次 printf()函数,把你的姓名打印在一行。输出应
 5     如下所示
 6 --------------------------------------------------------------
 7 Gustav Mahler        <- 第 1 次打印的内容
 8 Gustav                <- 第 2 次打印的内容
 9 Mahler                <- 仍是第 2 次打印的内容
10 Gustav Mahler        <- 第 3 次和第 4 次打印的内容
11 --------------------------------------------------------------
12 
13 */
14 
15 
16 #include <stdio.h>
17 
18 int main(void)
19 {
20     printf("Gustav Mahler\n");                // Gustav Mahler
21     
22     printf("Gustav\nMahler\n");                // Gustav
23                                             // Mahler
24 
25     printf("Gustav ");                        
26     printf("Mahler\n");                        
27     // Gustav Mahler
28 
29 
30     getchar();
31     return 0;
32 }
33 
34 /***************************************************
35 
36 输出结果:
37 ----------------------------------------------------
38 Gustav Mahler
39 Gustav
40 Mahler
41 Gustav Mahler
42 ----------------------------------------------------
43 
44 ***************************************************/
 1 /*************************************************
 2 2. 编写一个程序,打印你的姓名和地址。
 3 **************************************************/
 4 
 5 #include <stdio.h>
 6 
 7 int main(void)
 8 {
 9 
10     printf("Name: M0usek.\n");
11     printf("Address: China.\n");
12 
13 
14     getchar();
15     return 0;
16 }
17 
18 /***************************************************
19 输出结果:
20 ----------------------------------------------------
21 Name: M0usek.
22 Address: China.
23 
24 ----------------------------------------------------
25 
26 ****************************************************/
 1 /***********************************************************
 2 3. 编写一个程序把你的年龄转换成天数,并显示这两个值。
 3     这里不用考虑闰年的问题。
 4 
 5 年龄转换天数简单实现版:
 6     该程序只是简单的将用户输入的年龄转换成天数,如 用户输入 10 岁 
 7     则将 10 * 365 = 3650 天,只是一个大概数 并不精准。
 8 
 9 *************************************************************/
10 
11 #include <stdio.h>
12 
13 int main(void)
14 {
15     int nAges = 0;                        // 年龄
16     int nDayCount = 0;                    // 天数
17 
18     
19     printf("Please input your ages: ");                
20     scanf_s("%d", &nAges);
21 
22     nDayCount = nAges * 365;            //  年龄转天数
23 
24     printf("%d ages = %d days\n", nAges, nDayCount);    
25 
26     getchar();
27     return 0;
28 }
29 
30 /**********************************************************
31 输出结果:
32 -----------------------------------------------------------
33 Please input your ages: 10
34 10 ages = 3650 days
35 -----------------------------------------------------------
36 **********************************************************/
 1 /********************************************************
 2 4. 编写一个程序,生成以下输出:
 3 
 4 For he's a jolly good fellow!
 5 For he's a jolly good fellow!
 6 For he's a jolly good fellow!
 7 Which nobody can deny!
 8 
 9 除了 main() 函数以外,该程序还要调用两个自定义函数:一个名为 
10 jolly(),用于打印前 3 条消息,调用一次打印一条;另一个函数名
11 为 deny() ,打印最后一条消息。
12 
13 *********************************************************/
14 
15 #include <stdio.h>
16 
17 void jolly(void);        // jolly() 函数声明
18 void deny(void);        // deny()  函数声明
19 
20 
21 int main(void)
22 {
23     jolly();            //    调用 jolly() 函数 
24     jolly();
25     jolly();
26     deny();                // 调用 deny() 函数
27 
28     getchar();
29     return 0;
30 }
31 
32 // jolly() 函数定义
33 void jolly()                
34 {
35     printf("For he's a jolly good fellow!\n");
36 }
37 
38 // deny() 函数定义
39 void deny()
40 {
41     printf("Which nobody can deny!\n");
42 }
43 
44 /******************************************************
45 输出结果:
46 -------------------------------------------------------
47 For he's a jolly good fellow!
48 For he's a jolly good fellow!
49 For he's a jolly good fellow!
50 Which nobody can deny!
51 -------------------------------------------------------
52 *******************************************************/
 1 /*************************************************
 2 5. 编写一个程序,生成以下输出:
 3 
 4 Brazil, Russia, India, China
 5 India, China,
 6 Brazil, Russia
 7 
 8 除了 main()以外,该程序还要调用两个自定义函数:
 9 一个名为 br(),调用一次打印“Brazil, Russia”;
10 另一个名为 ic(),调用一次打印一次 "India, China"。
11 其他内容在 main() 函数中完成。
12 **************************************************/
13 
14 
15 #include <stdio.h>
16 
17 void br();        // 函数声明
18 void ic();        // 函数声明
19 
20 int main(void)
21 {
22     br();
23     printf(", ");
24     ic();
25     printf("\n");
26     ic();
27     printf(",\n");
28     br();
29     printf("\n");
30 
31     getchar();
32     return 0;
33 }
34 
35 void br()
36 {
37     printf("Brazil, Russia");
38 }
39 
40 void ic()
41 {
42     printf("India, China");
43 }
44 
45 /*********************************************************
46 输出结果:
47 ----------------------------------------------------------
48 Brazil, Russia, India, China
49 India, China,
50 Brazil, Russia
51 
52 ----------------------------------------------------------
53 **********************************************************/
 1 /*****************************************
 2 6. 编写一个程序,创建一个整型变量toes,并将 
 3 toes 设置为 10 。程序中还要计算 toes 的两倍
 4 和 toes 的平方。该程序应打印 3 个值,并分别
 5 描述以示区分
 6 ******************************************/
 7 
 8 #include <stdio.h>
 9 
10 int main(void)
11 {
12     int toes = 10;
13 
14     printf("toes = %d\n", toes);
15     printf("toes * 2 = %d\n", toes * 2);
16     printf("toes squared = %d\n", toes * toes);
17 
18 
19     getchar();
20     return 0;
21 }
22 
23 
24 
25 /*********************************************************
26 输出结果:
27 ----------------------------------------------------------
28 toes = 10
29 toes * 2 = 20
30 toes squared = 100
31 ----------------------------------------------------------
32 **********************************************************/
 1 /*********************************************************
 2 7. 许多研究表明,微笑益处多多。编写一个程序,生成以下格式的输出
 3 
 4 Smile!Smile!Smile!
 5 Smile!Smile!
 6 Smile!
 7 
 8 **********************************************************/
 9 
10 #include <stdio.h>
11 
12 void Smile();
13 
14 
15 int main(void)
16 {
17 //     Smile();
18 //     Smile();
19 //     Smile();
20 //     printf("\n");
21 // 
22 //     Smile();
23 //     Smile();
24 //     printf("\n");
25 // 
26 //     Smile();
27 //     printf("\n");
28 
29     ///*---------------------------------
30     for(int i = 0; i<3; i++)
31     {
32         for(int j = 3; j>i; j--)
33         {
34             Smile();
35         }
36         printf("\n");
37     }
38     //-----------------------------------*/
39 
40     getchar();
41     return 0;
42 }
43 
44 void Smile()
45 {
46     printf("Smile!");
47 }
48 
49 /********************************************************
50 输出结果:
51 ----------------------------------------------------------
52 Smile!Smile!Smile!
53 Smile!Smile!
54 Smile!
55 ----------------------------------------------------------
56 *********************************************************/
 1 /******************************************
 2 8. 在 C 语言中,函数可以调用另一个函数。编写一
 3 个程序,调用一个名为 one_three() 的函数。
 4 该函数在一行打印单词“one”,再调用第 2 个 函数
 5 two(),然后在另一行打印单词 “three”。two() 函数
 6 在一行显示单词"two"。main()函数在调用 one_three()
 7 函数前要打印短语"starting now:", 并在调用完毕后显示
 8 短语"done!"因此,该程序的输出应如下所示:
 9 
10 starting now:
11 one
12 two
13 three
14 done!
15 
16 ******************************************/
17 
18 
19 #include <stdio.h>
20 
21 void one_three();
22 void two();
23 
24 
25 int main(void)
26 {
27     printf("starting now:\n");
28     one_three();
29     printf("done!\n");
30 
31     getchar();
32     return 0;
33 }
34 
35 void one_three()
36 {
37     printf("one\n");
38     two();
39     printf("three\n");
40 }
41 
42 void two()
43 {
44     printf("two\n");
45 }
46 
47 /*********************************************************
48 输出结果:
49 ---------------------------------------------------------
50 starting now:
51 one
52 two
53 three
54 done!
55 ---------------------------------------------------------
56 **********************************************************/

猜你喜欢

转载自www.cnblogs.com/Micek/p/9153225.html
今日推荐