10天学会C语言

计算器程序讲义

 

10天学会C语言

☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆

☆★☆★☆  不要完全相信书上及老师所讲的内容  只有自己实作才是真的    ☆★☆★☆

☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆☆★☆★☆

 

参考书目:10小时学会 C 语言[修订版]   施保旭着  儒林出版社  1997 年 6 月初版 3 版

 

内容                                                                page

■ 第一章 C 语言简介与Turbo C 的使用                                2

■ 第二章 C 程序的结构                                              4

■ 第三章 常数与变数                                                10

■ 第四章 基本输出入函式                                            13

■ 第五章 流程图与抉择指令                                          21

■ 第六章 循环与自动重复                                            32

■ 第七章 数组与指针                                                40

■ 第八章 函数与呼叫                                                46

■ 第九章 档案存取                                                  55

 

 

■ 第一章  C 语言简介与Turbo C 的使用

 

   ◎ C 语言的优点:

      ○ 效率高:C 语言的编译器会产生最小的程序代码。

      ○ 可移植性/移植性高:经过些许的修改,可以在不同的平台使用。

      ○ 功能强而有弹性。

      ○ 需要记忆的东西很少,易于写作。

 

   ◎Turbo C 的安装:已安装在学校主机。

      Turbo C 的环境设定:Turbo C 安装的目录必须设定在 PATH 的系统变量。

      如:PATH=C:\TC;C:\DOS;...... 如此 TC 才能正常工作。

 

   ◎Turbo C 的使用

 

      只要设定好PATH 变量,在 DOS 提示号输入 TC ,就可以执行 Turbo C 的

      整合环境了。TC 将编辑(Edit)、编译(Compile)、连结(Link)、除错(Debug)、

      档案管理(File)、...等等的功能整合在一起,所以我们称之为整合环境。

      最好先用 CD的指令,变更工作目录到你要写 C 的目录,再执行 TC,这样

      所产生的档案,就会里这个目录里面,便于备份与管理。

 

   ◎ 移动光标

      方向键 ←↑↓→ 可以用来移动光标。

 

   ◎ 删除文字

      将光标移到你要删除的文字上,再按下 Del 键即可。

      将光标移到要删除文字的右边,再按下 BS 退位键也可以。

 

   ◎ 加载文字文件(C 语言原始码文件)

 

      按下功能键F3 或 按 F10 选 File → Load 就会出现一个询问窗口要求输入文件名:

 

       ┌───── Load File Name ─────┐

       │*.C                                 │

       └──────────────────┘

      其中的文件名可以使用万用字符 * 或 ? ,或直接指定你要的檔名。

      若是使用万用字符,TC 会再秀出一个窗口让你选择所要的档案,

      你可以用方向键移动反白光棒,按 Enter 键则是选择反白的档案。

 

   ◎ 储存编辑的文字文件

 

      按下功能键 F2 或 按 F10 选 File → Save 就会储存目前编辑档案。

      若你想另外取一个档名,并加以存盘,就必须 按 F10 选 File → Writeto

      就会出现一个询问窗口要求输入文件名:

 

       ┌──────  New Name  ──────┐

       │_                                   │

       └──────────────────┘

      输入新的档名,按下 Enter 即可。

 

   ◎ 编译并执行目前的所编辑的程序

      Turbo C 是一种编译语言系统,你所写的程序,经过 TC 的编译(pass 1)及

      连结(pass2)后,产生可执行档(.exe),才能在 PC 上执行。

      按下Ctrl + F9  或 按F10 选 Run → Run ,TC 会编译目前所编辑的程序,

      如果没有错误发生,TC 会立即执行所编辑的程序。

      TC 在执行完程序后,会立刻切换回到 TC 的整合环境,如果你还想看刚才程序

      执行的结果,可以按下 Alt + F5 或 按 F10 选Run → User screen ,就会

      切换到执行画面,再按下任何一键,就会回到 TC 的整合环境。

 

   ◎ 结束Turbo C

      按下Alt + X 或 按 F10 选 File → Quit 便可结束 Turbo C。

      若你还有已编修尚未储存的档案,TC 会问你要不要存。

       ╔══════ Verify ══════╗

       ║NONAME.Cnot saved.  Save? (Y/N)║

       ╚════════════════╝

      要存就按Y ,不想存就按 N 。

 

 

■ 第二章 C 程序的结构

 

   ◎ C 程序的结构:

        ┌────────────┐

        │hello.c                 │←─ 范例文件名

        ├────────────┤

       1│#include      │←─ 范例原始码

       2│main()                  │

       3│{                       │

       4│   printf("Hello!");    │

       5│}                       │

        ├────────────┤

        │Hello!                  │←─ 范例执行结果

        └────────────┘

 

第一列: #include

是用来定义一些函式的原型(prototype)、数据结构(struct)或是常数(constant)。               C 在使用变量之前,该变量都要先行宣告(declear)才可使用,而使用函式也是一样,必须先宣告它的原型才可以。宣告函式的原型是为了让 C 能在编辑时作数据的型别检查,以减少错误的发生。 内建的函式原型定义都放在INCLUDE\*.H 中,用 #include 就会将 INCLUDE\stdio.h 这个档引含。 stdio.h 由档名可知道是跟标准输出入(standard I/O)有关,档内定义了档案输出入、屏幕输出、键盘输入等相关函式的原型、数据结构及常数。 本例中的 printf() 就属于标准输出的函式。  引含 .h 档并不会让你的执行档变大或是变慢,而且还能让编辑器作正确的型别检查,所以要养成写 #include 的习惯。  ☆虽然在某些状况下,不加 #include<> 所产生的执行档,一样可以正常的执行。

 

第二列: main()

main() 就是主程序。程序在执行时,就是由这个函式开始执行。 在 C 语言中,内定的型别是 int ,所以原来的 main() 相当于是   int main(int)

★ 在这里正确的写法应该是  voidmain(void),    因为在这个简单的程序中,没有回传值,也没有输入参数。

☆ 回传值,是指一个函式在执行后传回的数值。

☆ 输入参数,是指一个函式可由参数决定执行的结果,这个部分在第八章中有详细的说明。

 

第三列: {

第五列: }

在第三列及第五列之间,属于 main() 函式的程序代码。

{ 表示程序由此开始, } 表示程序到此结束。

 

第四列: printf("Hello!");

是本程序要求系统做动作的指令,称之为「叙述」。在 C 语言中,每一叙述都以分号(;)做为结束。 在这个程序中,利用缩排的方式,使程序的层次分明,增加可读性。

在 C 语言中,调位字符( 如:空白(space)、定位(tab)及换列字符 )在编译时都会被忽略,所以可适时加入调位字符,使程序好看一点。 要注意的是,别把一个完整的个体拆开,如:main、printf 等,这些字本身是一个完整的个体,不可予以拆开。而在各个个体之间,可以任意加入调位字符。

☆ C 语言中的英文字母是有分大小写的,printf() 与 PrintF() 不同, 内建的函式大多是小写的,你自己写的函式,则可以用大写来做区隔。

 

   ◎printf 的功用

printf() 的功用是在屏幕上输出数据。在 TC 中,在编辑区内输入printf ,再将光标移到 printf 这个字上,按下 Ctrl + F1 就会看到以下Help画面:

 

     ┌────────── Help ───────────┐← 这是 TC 的 Help 窗口

     │                                               │

     │printf: formatted output to stdout            │← printf 是将格式化的

     │                                               │   数据输出到 stdout

     │ intprintf(const char *format, ...);          │← printf 的语法

     │                                               │

     │Prototype in stdio.h                          │← 要用 printf 应该

     │                                               │  #include 的檔

     │Print formats a variable number of arguments  │← 使用说明:不同的格式

     │according to the format, and sends the output │   须要不同的参数,这些

     │ to stdout. Returns the number ofbytes output. │  数据会送到stdout。传

     │ Inthe event of error, it returns EOF.        │   回值是输出的byte数,

     │                                               │   若发生错误则传回 EOF

     │ Seealso     ecvt    fprintf  putc            │← 可参考相关指令:

     │              puts    scanf    vprintf         │   ecvt,fprintf,putc,

     │                                               │  puts,scanf,vprintf

     └────────────────────────┘

 

在用 TC 的整合环境中,只要将光标移到想进一步了解的指令或内建的函式上,按      下 Ctrl + F1 就可以叫出 TC 的Help 说明窗口,得到该指令或函式的相关说明。

 

printf 的语法:  int printf(const char *format, ...);

其中 const char *format 指的是一个格式化的字符串。 const 是常数的意思,在此表示 format 这个字符串指针传入 printf 函式后,它的值不会被改变。

...   指的是不定参数,参数的数目取决于 format 字符串的内容,这些参数,通常是一些你要秀出来的变量。 简单来说:  printf( "输出格式(含控制字符串)" , 要印出的数据 );

      在 C 语言中是用双引号(")来引含字符串,也就是在双引号内的数据,是一个字符串。本章只介绍 %d 这个控制字符串,其它的控制字符串在第四章会讲到。 %d 表示以整数 10 进位的方式秀出数据。在输出格式(含控制字符串) 内有几个%d ,在要印出的数据内就要有几个整数来对应。

 

 

 

        ┌────────────────────────────────┐

        │arith.c                                                         │

        ├────────────────────────────────┤

       1│#include                                              │

       2│voidmain(void)                                                │

       3│{                                                               │

       4│   printf("%d + %d = %d\n", 8 , 2 ,8+2 );                      │

       5│   printf("%d - %d = %d\n", 8 , 2 ,8-2 );                      │

       6│   printf("%d * %d = %d\n", 8 , 2 ,8*2 );                      │

       7│   printf("%d / %d = %d\n", 8 , 2 ,8/2 );                      │

       8│}                                                              │

        ├────────────────────────────────┤

        │8 +2 = 10                                                      │

        │8 -2 = 6                                                      │

        │8 *2 = 16                                                     │

        │8 /2 = 4                                                      │

        └────────────────────────────────┘

 

      我们以第四列的叙述作说明:

 

            printf("%d + %d = %d\n", 8  ,  2  ,  8+2 );

                    ↑    ↑  ↑      │    │    │

                    │    │  └───│──│──┘

                    │    └─────│──┘

                    └────────┘

      在 输出格式(含控制字符串) 内有 3 个 %d ,所以在 要印出的数据 的部分有

      8 , 2 , 及 8+2三个整数对应,所以输出来的结果就是

 

          8 + 2= 10

 

      在 输出格式(含控制字符串) 的最后有 \n 符号,这是一个控制字符,表示要

      更换到下一列,其它的控制字符在第四章会提到。

      若将本例中的 \n 都删除,那秀出的结果会像这样子:

 

          8 + 2 = 108 - 2 = 68 * 2 = 168 / 2 = 4

 

 

   ◎ C 的四则运算

 

      计算机语言用的四则运算符号几乎都是相同的:

 

         四则运算符号   意  义                 范  例           结  果

        ============ ===================== ==============  ==========

             +         加法                   4 + 2              6

             -         减法 ( 或代表负号 )    4 - 2              2

             *         乘法                   4 * 2              8

             /         除法                   4 / 2              2

        ============  =====================  ==============  ==========

         相关运算符号   意  义                 范  例           结  果

        ============ ===================== ==============  ==========

             ++        变数值加1             i++  或  ++i   i 值加 1

              --        变数值减 1             i--  或  --i   i 值减 1

             %         整数除法的余数         4 % 2               0

        ============ ===================== ==============  ==========

 

      在书中提到C 语言没有提供次方的功能,指的是在某些计算机语言可以用 ** 表示

      次方,如:  2 ** 3 ,表示 2 的 3 次方;有的用 ^ 表示,如: 2^ 8 ,表示

      2 的 8 次方。在 C 语言,没有运算符号可以表示次方,但是 C 语言有提供次方

      的函式:pow(), pow( 2 , 3 ) 表示 2 的 3 次方。

 

      一个式子如果有多个运算的话,C 是以先乘除后加减的方法来运算,当然我们也

      可以用括号( ) 来改变这个法则,只要有括号,就优先运算。另外,在 C 语言内

      中括号 [] 及 大括号 { } 是有其它的用途,所以在作数学运算时,只要用括号

      ,就只能用小括号 ( ),小括号可以多层,C 在运算时,是由最内层开始运算。

 

      范例:   ( 1 + 2 * ( 3 + 4 ) ) * 5 - 6 * 7 / 2 + 8

           =  ( 1 + 2 * (   7   )) * 5 - 6 * 7 / 2 + 8

           =  (       15          ) * 5 - 6 * 7 / 2 + 8

           =            75              -  42  / 2 + 8

           =            75              -     21   + 8

           =   62

 

   ◎ 批注(Comments)

 

通常老师会要求初学者在程序的每一列加上批注,这是为了让初学者知道自己在写些什么程序代码,了解为什么要这样写,而不只是照著书 Keyin 程序。写批注有助于自己了解程序的内容,便于日后的修改。但写批注对于某些程序设计师而言可说是一种噩梦,因为写批注所花的时间可能会与写程序的时间相去不远,认为写批注只是在浪费时间。对一个相当好的程序设计师而言,也许写批注真的是浪费时间,因为好的程序代码本身就已经隐含了批注,这也是写程序相当高的境界。 对一般的程序设计师而言,写一些批注还是比较好的作法,特别是某些程序代码是你花了一段时间才想到的「特殊」方法,加上一些批注,说明一下这个「特殊」的方法,以后要修改才能快速进入状况,否则,你可能会佩服自己当时是如何想到的这个方法,又再花一段时间才知道自己在写些什么程序代码。      讲了这么多批注的正反面(正面居多)论调,在 C 语言中要如何写批注呢?只要用 /* 和 */ 将你要的批注内容包起来就可以了。C 在编译时,会将 /* */ 内的数据略去,就如同调位字符一样。唯一的例外是:当 /* */ 在一组双引号 " 内时,它们就属于这组双引号所包含的字符串。  在C++ 语言中则可用//当批注.

 

        ┌────────────────────────────────┐

        │comments.c    or comments.cpp                                   │

        ├────────────────────────────────┤

       1│#include     /* prototype :printf() */               │

       2│voidmain(void)  // main program                                │

       3│{                                                              │

       4│/* 所有的程序代码都变成批注,所以这个程序目前是空的 */            │

       5│/*printf("%d + %d = %d\n", 8 , 2 , 8+2 );    */                │

       6│/*printf("%d - %d = %d\n", 8 , 2 , 8-2 );                      │

       7│   printf("%d * %d = %d\n", 8 , 2 ,8*2 );                      │

       8│   printf("%d / %d = %d\n", 8 , 2 ,8/2 );  //  division        │

       9│*/                                                             │

      10│}    // end of program                                          │

        ├────────────────────────────────┤

        └────────────────────────────────┘

 

 

   ◎ 巢状批注(Nested Comments)

 

        ┌────────────────────────────────┐

        │nestcom0.c                                                     │

        ├────────────────────────────────┤

       1│#include     /* prototype :printf() */               │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│/* 这个程序必须把巢状批注的设定打开,才不会有错误 */            │

       5│/*                                                             │

       6│   printf("%d + %d = %d\n", 8 , 2 ,8+2 );                      │

       7│/*printf("%d - %d = %d\n", 8 , 2 , 8-2 );        */            │

       8│   printf("%d * %d = %d\n", 8 , 2 ,8*2 );                      │

       9│   printf("%d / %d = %d\n", 8 , 2 ,8/2 );                      │

      10│*/                                                             │

      11│}                                                               │

        ├────────────────────────────────┤

        └────────────────────────────────┘

 

      上面的例子,有四组批注 /* */ ,其中第三组及第四组的批注之间有部分重迭。

      想要Compile 没有错误,必须第 5 列的 /* 与第 10 列的 */ 配,也就是

      第 5 列到第 10 列都是批注;另外第 7 列的/* 与 第 7 列的 */ 配,也就是

      第 7 列是批注。这种批注方式,我们称之为巢状批注。

      Turbo C 内定是不可使用巢状批注的,上面的例子会是第 5 列的 /* 与 第 7 列

      的 */ 配,结果在第 10 列的 */ 会变成是多余的,造成Compile 错误。

      打开巢状批注的方法:

      按下F10 → Options → Compiler → Source → Nested comments     Off

      将 Off设为 On 就可以了。

 

   ◎ 巢状批注的使用时机

 

      在前面的例子只是为了说明巢状批注,也许你会觉得这样的用法是自找麻烦,

      但是以下的例子,你就会认为有巢状批注的功能还是比较好的。

      在nestcom1.c 中,每一列的 printf(); 后面都加上了批注。

      若要把这几列程序变成批注,不使用巢状批注,就会像 nestcom2.c 一样,

      必须在每一列的  printf(); 前后再加上 /* */,若是使用巢状批注,

      就像nestcom3.c 一样,只要在这几列的前后加 /* */ 就可以了。

 

        ┌────────────────────────────────┐

        │nestcom1.c                                                     │

        ├────────────────────────────────┤

       1│#include     /* prototype :printf() */               │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   /* 这个程序在每一个叙述后都加上了批注*/                     │

       5│                                                                │

       6│   printf("%d + %d = %d\n", 8 , 2 ,8+2 );     /* 8 + 2 = 10 */ │

       7│   printf("%d - %d = %d\n", 8 , 2 ,8-2 );     /* 8 - 2 = 6  */ │

       8│   printf("%d * %d = %d\n", 8 , 2 ,8*2 );     /* 8 * 2 = 16 */ │

       9│   printf("%d / %d = %d\n", 8 , 2 ,8/2 );     /* 8 / 2 = 4  */ │

      10│                                                               │

      11│}                                                              │

        ├────────────────────────────────┤

        └────────────────────────────────┘

 

 

        ┌────────────────────────────────┐

        │nestcom2.c                                                     │

        ├────────────────────────────────┤

       1│#include     /* prototype :printf() */               │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│/* 这个程序不用把巢状批注的设定打开,也不会有错误 */            │

       5│                                                               │

       6│/*printf("%d + %d = %d\n", 8 , 2 , 8+2 ); */  /* 8 + 2 = 10 */ │

       7│/*printf("%d - %d = %d\n", 8 , 2 , 8-2 ); */  /* 8 - 2 = 6 */ │

       8│/*printf("%d * %d = %d\n", 8 , 2 , 8*2 ); */  /* 8 * 2 = 16 */ │

       9│/*printf("%d / %d = %d\n", 8 , 2 , 8/2 ); */  /* 8 / 2 = 4 */ │

      10│                                                               │

      11│}                                                               │

        ├────────────────────────────────┤

        └────────────────────────────────┘

        ┌────────────────────────────────┐

        │nestcom3.c                                                     │

        ├────────────────────────────────┤

       1│#include     /* prototype :printf() */               │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│/* 这个程序也必须把巢状批注的设定打开,才不会有错误 */          │

       5│/*                                                             │

       6│   printf("%d + %d = %d\n", 8 , 2 ,8+2 );     /* 8 + 2 = 10 */ │

       7│   printf("%d - %d = %d\n", 8 , 2 ,8-2 );     /* 8 - 2 = 6  */ │

       8│   printf("%d * %d = %d\n", 8 , 2 ,8*2 );     /* 8 * 2 = 16 */ │

       9│   printf("%d / %d = %d\n", 8 , 2 ,8/2 );     /* 8 / 2 = 4  */ │

      10│*/                                                             │

      11│}                                                               │

        ├────────────────────────────────┤

        └────────────────────────────────┘

 

 

 

■ 第三章 常数与变数

 

      C 语言的数据可分为常数(constant)及变量(variable),常数指的是固定不变的数,

   例如:0,1,2等数值,或是用双引号定义的字符串,我们也称之为字符串常数。

   变量指的是数值可以改变的数,例如:一个整数变量,我们可以把它设成 1,然后再

   改为 10,或是其它的整数数值。

   一个程序若没有变量,那纯粹只是将常数秀出来而已,好比是用文字编辑器编辑一个

   档案,再用type 把它秀出来一样。有了变量,就可以做不同的变化。

 

   ◎ 变量的型态──Char,  int, long, float,double  etc.。

 

   ◎ 变数的命名

 

      如同档案的命名,变量的名字要取得有意义,在 C 中,名字可以取得很长,但是

      要用英文的,所以你可以把变量用中翻英来命名。

 

   ◎ 变量的命名规则

 

      ○ 变量名称的第一个字符必须是英文字母(A 到 Z 或 a 到 z)或是底线( _ )。

      ○ 第二个字符以后可以使用前述字符,再加上数字 0 到 9 。

      ○ 变量名称的大小写是不同的。

      ○ 变量名称的最前面 32 个字符有效。

      ○ 不可以使用「保留字」当变量的名称,保留字是给编译器使用,不可以当成

         变量名称。TC有以下的保留字:

 

         流程:   if      else     for      do      while

                 switch   default  case    break    continue

                 goto     return

         型别:   char    int      long     float   double

                 void     register signed   unsigned

                  short    near    far      huge

                 typedef  struct   union   enum

                 auto     const    static  volatile extern

                 interrupt         cdecl    pascal  asm

         运算:   sizeof

         缓存器:_AX      _AH      _AL     _cs      _CS

                 _BX      _BH      _BL     _ds      _DS

                 _CX      _CH      _CL     _es      _ES

                 _DX      _DH      _DL     _ss      _SS

                 _SI      _DI      _BP     _SP

 

   ◎ 变量的设定

 

      使用变量时,应该先考虑这个数可能的数值范围,用以选定变量的型别,例如:

      用一个数来存班上的人数,一个合班的大班级可能超过百人,但最大不太可能

      超过千人,所以选一种变量型别可存 1000 以下的数值,在此可选整数。

      若是要用一个数来存你的存款,则整数的上限 32767 可能某些同学一个月的薪资

      就是它的数倍,所以要选长整数,它的上限是 2147483647 。在数学运算时,

      想要有小数的就要用浮点数(float)。

      在 C 语言中,变量宣告的语法如下:

 

       ┌────────────────────┐

       │ 型别  变量名称1 [,变量名称2 [,...]] ;  │

       └────────────────────┘

 

      例如:int   NumberOfStudent;                /* 学生人数*/

            long  MoneyInBank, interest;          /* 银行存款 */

             float RateOfInterest;                 /* 利息利率*/

            char  EndOfString;                    /* 字符串结束*/

            char  OneStudentName[9];              /* 学生姓名 */

 

      在宣告变量时,我们可以设定变量的初始值(initial value),语法如下:

 

       ┌────────────────────────────┐

       │ 型别  变量名称1=初始值1 [,变量名称2=初始值2[,...]] ;  │

       └────────────────────────────┘

 

      例如:int   NumberOfStudent=60;             /* 学生人数 */

            long  MoneyInBank=1000000L;           /* 银行存款 */

            float RateOfInterest=5.0;            /* 利息利率 in % */

            char  EndOfString='\0';               /* 字符串结束*/

            char  OneStudentName[9]="王大明";   /* 学生姓名 */

 

      注意: 在银行存款的设定数值 1000000 后加上一个 L ,表示这个常数数值

            1000000 是一个长整数。因为 C 语言内定的型别是整数,为了防止

             不可预期的状况发生,最好是自己把它设定成你想要的型别,不要

             假设 TC会帮你做好好的,要假设 TC 很笨不会帮你做,这样在发展

             大程序要除错时,就可以把问题简化,不必再考虑是不是数据型别错误,

             只要把程序流程或算法搞定就可以了。

             在 TC 中,不加 L,结果还是正确的,但是在其它的环境下可能会不同。

             多加一个L 并不会使程序变大或变慢,又能保障正确使用,何乐不为。

 

      ○ 复习一下字符与字符串:

 

         char 字符只占一个 byte,以一组单引号 ' 引含字符数据,其表示法如下:

         ⊙ 单一字符:     'A'   、'a'   、'0'   。

         ⊙ 八进制数值:   '\101'、'\141'、'\60' 、'\0'

         ⊙ 十六进制数值: '\x41'、'\x61'、'\x30'、'\x0'

 

         字符串则是由一个以上的字符所组成的,而且以 '\0' 这个字符做为结尾。

         表示法: "123"、"ABC"、"abc"。以上的三个例子都是占 4 bytes。

         用strlen() 可以取得字符串的长度(不含 '\0' 字符)。

         如:  int StringLen=strlen("123");

         这样,StringLen就等于 3 。

         ☆ 在使用strlen() 时,必须加入  #include

 

   ◎ 设定叙述

 

前面已经说明了变量在宣告时给定初值的方法,接下来是在程序执行的过程中设定变量数值的方法。 即然是变量,表示它的数值可能在程序执行的过程中会改变多次,如果一个变量在整个程序执行中都不会改变,或许你该把它设成常数。

      在设定变量时,可以用等号 = 来设定变量新值,语法如下:

 

       ┌────────────────────────────┐

       │ 变量名称 = 表达式(表达式、函式传回数值或两者混合);     │

       └────────────────────────────┘

 

      这个意思是等号左边「变量名称」的数值会等于等号右边「表达式」的运算结果。

      在 C 中,等号 = 是用来设定变量数值的,所以在等号的左边必须是变量,不可以

      是常数。在逻辑上的相等,在 C 中是用两个等号 == 来表示,有关逻辑的表示,

      在第五章中会作介绍。以下我们来看一些设定的例子,计算圆的面积:

 

         PI =3.1415926;

         r = 4;

         area =PI * r * r ;

 

      以下是用变量的方式表示的范例:

        ┌────────────────────────────────┐

        │var.c                                                          │

        ├────────────────────────────────┤

        │#include                                              │

        │voidmain(void)                                                 │

        │{                                                              │

        │   int i,j;                                                    │

        │                                                               │

        │   i = 10;                                                     │

        │   j = 2;                                                      │

        │   printf("%d + %d = %d\n", i , j ,i+j );                      │

        │   printf("%d - %d = %d\n", i , j ,i-j );                      │

        │   printf("%d * %d = %d\n", i , j ,i*j );                      │

        │   printf("%d / %d = %d\n", i , j ,i/j );                      │

        │                                                                │

        │   i = 20;                                                     │

        │   j = 2;                                                      │

        │   printf("%d + %d = %d\n", i , j ,i+j );                      │

        │  printf("%d - %d = %d\n", i , j , i-j );                      │

        │   printf("%d * %d = %d\n", i , j ,i*j );                      │

        │   printf("%d / %d = %d\n", i , j ,i/j );                      │

        │}                                                               │

        ├────────────────────────────────┤

        │10 +2 = 12                                                    │

        │10 -2 = 8                                                     │

        │10 *2 = 20                                                     │

        │10 /2 = 5                                                     │

        │20 +2 = 22                                                    │

        │20 -2 = 18                                                     │

        │20 *2 = 40                                                    │

        │20 /2 = 10                                                    │

        └────────────────────────────────┘

 

 

变量使用的有效范围:

 

    整体变量(GlobalVariable): 整体程序内

    区域变量(LocalVariable):函式内

    静态变量(StaticVariable): 单一程序内

   

 

 

■ 第四章  基本输出入函式

 

   这一章将介绍一些基本输出入的函式,使用者经由这些函式可以与计算机沟通,让程序读取使用者的输入部分。程序依使用者不同的要求,做不同的事,再将结果输出给使用者。

 

   ◎ 输出指令:printf()

 

      在第二章中,曾经谈过 printf 指令,现在来详细的探讨它。

printf是一种格式化的输出指令,换句话说,你可以用它来编排你所要的输出格式。printf的一般型式如下:

 

       ┌────────────────────────────┐

       │printf("控制字符串" , 表达式1, 表达式2 , ... );        │

       └────────────────────────────┘

 

      控制字符串是你打算要秀出的讯息,其中利用 % 与 \ 这两个字符,来控制数值的

      输出格式。

      控制字符串中每一个 % 符号,表示在后面有一个表达式与它对应,表达式的值会代

      入这个 %的位置。在 % 后的字符表示代入数的型别,常用的句柄如下表:

 

       ┌────────┬──────────┐

       │printf的句柄 │ 代表代入的数值型别 │

       ├────────┼──────────┤

       │%c              │ 字符               │

       │%d              │ 十进制之整数       │

       │%ld             │ 十进制之长整数     │

       │%f              │ 浮点数             │

       │%lf             │ 倍精浮点数         │

       │%Lf             │ 长倍精浮点数       │

       │%s              │ 字符串               │

       └────────┴──────────┘

 

      表达式的型别必须跟句柄所代表的型别相符,否则会秀出不可预期的资料。

      另一个控制符号 \ ,其实只是定义字符而已。在上一章已经介绍了字符的各种表

      示法,如:'\x41'表示 A 字符。

      在 C 语言中,将一些特殊的控制字符另外定义,这些控制字符大部份跟光标的

      控制有关,如下表:

 

        ┌────┬──┬──┬───────────────┐

        │控制字符│Dec │Hex │功能                          │

        ├────┼──┼──┼───────────────┤

        │\n      │ 10 │0x0A│换列,也就是将光标移到下一列  │

        │\t      │  9 │0x09│将光标移到下一个定位(1+8n)    │

        │\b      │  8 │0x08│退一格,类似按下左键          │

        │\a      │  7 │0x07│喇叭叫一声                    │

        │\r      │ 13 │0x0D│回到列首                      │

        │\f      │ 12 │0x0C│跳页,在列表时控制列表机跳页  │

        │\\      │ 92 │0x5C│印出 \ 字符                   │

        │\'      │ 39 │0x27│印出 ' 字符                   │

        │\"      │ 34 │0x22│印出 " 字符                   │

        │\xHH    │    │0xHH│印出 0xHH 所表示的字符        │

       *│%%      │ 37 │0x25│印出 % 字符                   │

        └────┴──┴──┴───────────────┘

 

      其中,% 字符的定义与在控制字符串中的表示法不同,其余的字符在定义上与在控制

      字符串中的表示法都相同。

      printf 在做输出时,你也可以指定保留多少位置给对应的表达式放结果。指定的

      方式是在% 之后加一个数值,如 %5d:表示保留 5 个字符空间给一个十进制整数;

      %12ld:表示保留12 个字符空间给一个十进制长整数。

      如果要输出数据的长度比你指定的保留空间还要大时,printf 就不理会你的设定,

      把要输出的数据完整的输出,所以,你在设定保留空间时,应该注意输出数据的范围

      及长度,保留够大的空间,确保输出格式的整齐。

      在浮点数方面,你除了可以指定保留的空间外,还可以指定小数点后要取几位。

      如%8.3f :表示保留 8 个字符空间给一个浮点数,小数部分则是占 3 个字符空间

      ,由于小数点本身占一个字符,所以整数部分占 8 - ( 3 + 1) = 4 个字符空间。

      printf 在输出数据时,如果你指定保留的空间比要秀的数据长度还要大时,那

      printf 先秀一些空白,再秀出数据,使总长度等于你所指定的宽度,这样等于是

      让输出的数据向右对齐。如果你想要让数据是向左对齐的话,可以在指定宽度时

      使用负数,如 %-5d:表示保留 5 个字符空间给一个十进制整数,若数据长度不足

      5 ,则在秀出资料后补空白。

 

      ○ 整数(int)及长整数(long)

         ┌─────────────────────────────┐

         │ %-  + w d   →int                                     │

         │ %-  + w ld  →long                                    │

         │  ↑ ↑ ↑                                                │

         │  │ │ └───  若有指定,则保留 w 个字符               │

         │  │ │           若无指定,秀出长度将由数据决定          │

         │  │ └───── 若有指定,则一定会秀出正负号            │

         │  │              若无指定,则只有负数会秀出负号          │

         │  └──────  若有指定,则向左对齐                    │

         │                  若无指定,则向右对齐                    │

         └─────────────────────────────┘

 

      ○ 浮点数(float)、倍精浮点数(double)及长倍精浮点数(long double)

         ┌─────────────────────────────┐

         │ %-  + w . p f   → float                               │

         │ %-  + w . p lf  → double                             │

         │ %-  + w . p Lf  → long double                        │

         │  ↑ ↑ ↑  ↑                                            │

         │  │ │ │  └─  若有指定,则保留 p 个字符给小数         │

         │  │ │ │        若无指定,内定是保留 6 个字符给小数     │

         │  │ │ └───  若有指定,则保留 w 个字符含小数及小数点│

         │  │ │           若无指定,秀出长度将由数据决定          │

         │  │ └───── 若有指定,则一定会秀出正负号            │

         │  │              若无指定,则只有负数会秀出负号          │

         │  └──────  若有指定,则向左对齐                    │

         │                  若无指定,则向右对齐                    │

         └─────────────────────────────┘

 

      说了这么多,只有自己试试看才知道!以下是个简单的例子:

        ┌─────────────────────────────────┐

        │print.c                                                          │

        ├─────────────────────────────────┤

       1│#include                                               │

       2│voidmain(void)                                                  │

       3│{                                                                │

       4│   printf("|%ld|\n",   123456 );                                  │

       5│   printf("|%5ld|\n",  123456 );                                  │

       6│  printf("|%d|\n",   123    );                                  │

       7│   printf("|%5d|\n",   123   );                                 │

       8│   printf("|%-5d|\n",  123   );                                 │

       9│   printf("|%f|\n",    12.345 );                                  │

      10│   printf("|%9f|\n",   12.345 );                                  │

      11│   printf("|%9.2f|\n", 12.345 );                                  │

      12│   printf("|%-9.2f|\n",12.345 );                                  │

      13│}                                                                │

        ├─────────────────────────────────┤

        │|123456|          ← 123456 大于 32767 要长整数才能表示,所以用%ld│

        │|123456|          ← 所保留的 5 个字符不够使用,所以 TC 视同你没设│

        │|123|                                                            │

        │|  123|          ← 保留 5 个字符,只使用 3 个字,向右靠齐       │

        │|123  |          ← 保留 5 个字符,只使用 3 个字,向左靠齐       │

        │|12.345000|       ← 小数没有指定,所以 TC 使用内定的 6 个小数。 │

        │|12.345000|       ← 保留 9 个字符,小数部分仍使用内定值          │

        │|    12.35|      ← 保留 9 个字符,小数 2 个字符,向右靠齐       │

        │|12.35    |      ← 保留 9 个字符,小数 2 个字符,向左靠齐       │

        └─────────────────────────────────┘

 

   ◎ 输入指令:scanf()

 

      C 语言使用scanf 指令来读取keyboard输入的数据。scanf 的一般型式如下:

 

       ┌────────────────────────────┐

       │scanf("控制字符串" , &变量1 , &变量2 , ... );           │

       └────────────────────────────┘

 

      scanf 与printf 可以说是相对的,一个用来做输入,一个用来做输出。

      scanf 的控制字符串与 printf 几乎是一样。

 

       ┌────────┬──────────┐

       │scanf的句柄  │ 代表输入的数值型别│

       ├────────┼──────────┤

       │%c              │ 字符               │

       │%d              │ 十进制之整数       │

       │%ld             │ 十进制之长整数     │

      *│%D              │ 十进制之长整数     │

       │%f              │ 浮点数             │

       │%lf             │ 倍精浮点数         │

       │%Lf             │ 长倍精浮点数       │

       │%s              │ 字符串               │

       └────────┴──────────┘

       ★ 注意:没有 %F 这种句柄,课本有误!

                %D的句柄只能用在 scanf() ,在 printf() 中无法使用。

 

      在用scanf 时还有一点要注意,在控制字符串后的变量,使用的是指针(pointer)。

      什么是指标?指针就是指向内存的一个地址,在那个地址存放着数据。

      例如:一个整数变量 i ,它是存在内存的某一个地址,那个地址在 C 语言中,

      是以&i 来表示,我们通常称 &i 是 i 的地址,也称为 i 的指标。

      以下是常用的变量及其指针(因为它的地址固定不会改变,也称为指标常数):

 

       ┌──────────────────────────────────┐

       │charc;           /* 字符         */    /* c 的指针是 &c */       │

       │inti;            /* 整数         */    /* i 的指标是 &i */       │

       │longl;           /* 长整数       */    /* l 的指标是 &l */       │

       │floatf;          /* 浮点数       */    /* f 的指标是 &f */       │

       │doubled;         /* 倍精浮点数   */    /* d 的指标是 &d */       │

       │longdouble ld;   /* 长倍精浮点数 */    /* ld 的指标是 &ld */       │

       │                                                                    │

       │charstr[80];     /* 字符数组(字符串) */ /* str[80] 的指标是 str */  │

       │inta[100];       /* 整数数组       */  /* a[100] 的指标是 a  */  │

       │longb[100];      /* 长整数数组     */  /* b[100] 的指标是 b  */  │

       │floatc[100];     /* 浮点数数组     */  /* c[100] 的指标是 c  */  │

       └──────────────────────────────────┘

 

      以下的范例,将第三章的范例var.c 变量改由 scanf 输入:

        ┌────────────────────────────────┐

        │io.c                                                           │

        ├────────────────────────────────┤

       1│#include                                              │

       2│voidmain(void)                                                │

       3│{                                                               │

       4│   int i,j;                                                    │

       5│                                                               │

       6│   printf("Enter 2 integers:");                                 │

       7│   scanf("%d %d", &i, &j);        /* 用 i 与 j 的指标 &i,&j */ │

       8│   printf("Now, I find that...\n");                            │

       9│   printf("%d + %d = %d\n", i , j ,i+j );                      │

      10│   printf("%d - %d = %d\n", i , j ,i-j );                      │

      11│   printf("%d * %d = %d\n", i , j ,i*j );                      │

      12│   printf("%d / %d = %d\n", i , j ,i/j );                      │

      13│}                                                              │

        ├────────────────────────────────┤

        │Enter2 integers:20 4            ← 相当于 i = 20 , j = 4       │

        │Now,I find that ...                                            │

        │20 +4 = 24                                                    │

        │20 -4 = 16                                                    │

        │20 *4 = 80                                                    │

        │20 /4 = 5                                                     │

        └────────────────────────────────┘

 

      scanf 在读取多笔数据时,是把调位字符(如:空白、定位(tab)及换列字符)当

      作数据的分隔的记号,也就是利用 scanf 输入多笔数据时,必须用调位字符

      分隔输入的数据。如本例中,输入的 i 值与 j 值,可以用空白区隔,也可以用

      换列或定位来区隔。在结束输入时则要用换列字符,也就是说要按 Enter 。

 

      进阶的程序设计师通常不会用 scanf 作为数据的输入,因为只要输入的格式有

      一点点错误, scanf 所得到的结果就无法预期。大部分的程序设计师会用

      gets() 这个函式读入字符串,gets 是以换列字符作为结束记号,所以,利用

      gets 可以读入包含空白及定位的字符串。读入的字符串可以用 atoi() 转成整数、

      用atol() 转成长整数、用 atof() 转成浮点数。

      更进阶的程序设计师则是自行设计输入函式,并加入一些设定防止输入错误发生,

      例如,在输入整数的函式中,使用者只有按下数字键才有反应,若按下非数字键,

      则不会反应。也可以限定使用者输入的数据长度,防止输入的数值过大,等等。

 

 

      以下的范例,将华氏温度(℉)换算成摄氏温度(℃)

        ┌────────────────────────────────┐

        │f2c.c                                                          │

        ├────────────────────────────────┤

       1│#include                                              │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   int f,c;                                                     │

       5│                                                               │

       6│   printf("Enter the temperature in F :");                     │

       7│   scanf("%d", &f );                       /* 用 f 的指标 &f */ │

       8│   c = ( f - 32 ) * 5 / 9 ;                /* 换算公式*/       │

       9│   printf("%d degrees in F is %d degreesin C.", f, c);         │

      10│}                                                              │

        ├────────────────────────────────┤

        │Enterthe temperature in F : 100                                │

        │100degrees in F is 37 degrees in C.                            │

        └────────────────────────────────┘

 

      华氏温度(℉)与摄氏温度(℃)的互换公式如下:

 

         C = ( F- 32 ) * 5 / 9

         F = C * 9 / 5 + 32

 

      以下是课本的范例,将年纪改用"日"来估算:

        ┌────────────────────────────────┐

        │age.c                                                          │

        ├────────────────────────────────┤

       1│#include                                              │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   float years, days;                                           │

       5│                                                               │

       6│   printf("Enter the age of you :");                           │

       7│   scanf("%f", &years );            /* 用 years 的指标 &years */│

       8│   days = years * 365.25 ;                        /* 换算公式 */│

       9│   printf("You are %f days old.",days );                       │

      10│}                                                              │

        ├────────────────────────────────┤

        │Enterthe age of you : 28.5                                     │

        │Youare 10409.625000 days old.                                  │

        └────────────────────────────────┘

 

   ◎ 输入指令:getche()

 

      使用scanf() 读取字符时,必须再按下 Enter 键,该字符才会被读取。

      在某些场合中,我们希望每按一个键,程序就会读取,不用再按下 Enter 键,

      例如:在计算机游戏中,每按下方向键,所控制的人物就依所按的方向移动。

      利用getche() 可以达到这个目的。 getche 的一般型式如下:

 

       ┌────────────────────────────┐

       │ ch= getche();                                         │

       └────────────────────────────┘

 

      将 getche() 所读到的字符传给 ch 。此外,getche() 会将读到的字符先秀在

      屏幕上。

 

      以下的范例,将所按的键秀出其 ASCII 码:

        ┌────────────────────────────────┐

        │code.c                                                         │

        ├────────────────────────────────┤

       1│#include      /* 宣告 printf() 的原型 */               │

       2│#include     /* 宣告 getche() 的原型 */               │

       3│voidmain(void)                                                │

       4│{                                                               │

       5│   char ch;                                                    │

       6│                                                               │

       7│   ch = getche();      /* getche() 会传回你所按下键的字符 */    │

       8│   printf(" -- You typed %c.\n", ch);                          │

       9│   printf("Character %c has ASCII code%d.\n", ch, ch );        │

      10│}                                                              │

        ├────────────────────────────────┤

        │A -- You typed A.                                               │

        │CharacterA has ASCII code 65.                                  │

        └────────────────────────────────┘

      这个范例主要是用来查询一般字符的 ASCII 码,请勿输入方向键,否则它的结果

      可能会让你失望。因为方向键及功能键等特殊按键会产生两个码,必须读两次

      才能得到正确的结果。

      第 9 行,用 %c 的控制字符,表示要秀出 ch 所代表的字符;

               用 %d 的控制字符,表示要秀出 ch 所代表的数值。

 

 

      ☆★☆★☆★☆★ 练习 ☆★☆★☆★☆★

 

      实作课本习题第 10 题,看看你所得到的结果与课本所列的的结果一不一样?

 

      ☆★☆★☆★☆★ 作业 ☆★☆★☆★☆★

 

 

■ Help参考数据:

 

┌────────── Help ───────────┐

│ printf: formatted output to stdout             │格式化输出至stdout

│                                               │

│ int printf(const char *format, ...);           │printf 的语法

│                                               │

│ Prototype in stdio.h                           │必须 #include

│                                               │

│ Print formats a variable number of arguments   │不同的格式须要不同的

│ according to the format, and sends the output  │参数,这些数据会送到

│ to stdout. Returns the number of bytes output. │stdout。传回值是输出

│ In the event of error, it returns EOF.         │的byte数,若发生错误

│                                               │则传回 EOF

│ See also    ecvt    fprintf   putc            │

│             puts    scanf     vprintf        │相关指令

└────────────────────────┘

┌────────── Help ───────────┐

│              Format Specifiers                │format 的格式

│                                               │

│   % [flags][width] [.prec] [F|N|h|l] type     │[ ] 表示不一定要用

│                                                │

│ type         Format of Output                 │格式化输出的型别:

│  d      signed decimal int                     │d带正负号十进制整数

│  i      signed decimal int                     │i带正负号十进制整数

│  o      unsigned octal int                     │o 不带正负号八进位整数

│  u      unsigned decimal int                   │u 不带正负号十进制整数

│  x      in printf = unsigned hexdecimal int    │x 不带正负号16进位整数(小写)

│         lowercase;in scanf = hexadecimal int  │  在scanf 为16进位整数

│  X      in printf = unsigned hexdecimal int    │X 不带正负号16进位整数(小写)

│         uppercase;in scanf = hexadecimal long │  在 scanf 为16进位长整数

│  f      floating point [-]dddd.ddd             │f 浮点数例: 314.159

│  e      floating point [-]d.ddd e [+/-]ddd     │e 浮点数 例: 3.14159e2

│  g      format e or f based on precision       │g 由精度决定用 f 或 e 的格式

│  E      same as e except E for exponent        │E 同 e 只是以 E 表示指数符号

│  G      same as g except E for exponent        │G 同 g 只是以 E 表示指数符号

│  c      single character                       │c 单一字符

│  s      print characters till '\0' or[.prec]  │s 打印字符直到 '\0' 或指定长度

│  %      the % character                        │% 打印 % 这个字符

│  p      pointer: near - YYYY; far -XXXX:YYYY  │p 指标:近 YYYY 远 XXXX:YYYY

│  n      stores count of characters writtenso  │n 将目前已经打印出的字符数值

│         far in thelocation pointed to by      │  传给输入的参数。在此,

│         inputargument                         │  输入的参数必须是整数指标。

│                                               │

│ [flag]        Whatit Specifies                │[旗标]

│                                                │

│ none   right-justify, pad 0 or blank to left │无 靠右对齐不足在左边补空白或0

│  -      left-justify, pad spaces to right      │-  靠左对齐不足在右边补空白

│  +      always begin with + or -               │+    正负号一定会秀出

│ blank   print signfor negative values only    │空白 只有负数才会秀出负号

│  #      convert using alternate form:          │#    转换格式

│        c,s,d,i,u  no effect                   │对c,s,d,i,u 没有影响

│         o          0 prepended to nonzero arg  │o 如果数据不是 0 就会先秀 0

│         x orX     0x or 0X prepended to arg   │x,X 在资料前加秀 0x 或 0X

│         e, E,f    always use decimal point    │e,E,f 会秀出小数点

│         g orG     same as above but no        │g,G   会秀出小数点,但是不补 0

│                   trailing zeros              │

│                                                │

│ [width]      Effect on Output                 │[宽度]

│                                               │

│   n     at least n characters, blank-padded    │n  指定输出的宽度,不足补空白

│   0n    at least n characters, 0 left fill     │0n 指定输出的宽度,不足在左补0

│   *     next argument from list is width       │*  由下一个参数决定宽度

│                                               │

│ [.prec]      Effect on Output                 │[.精度]

│                                               │

│  none   default precision                      │无  内定的精度

│  .0     d,i,o,u,x    default precision         │.0 d,i,o,u,x 是内定的格式

│         e, E,f      no decimal point          │   e,E,f 表示没有小数

│  .n     at most n characters                   │.n 指定 n 个字符长度

│  *      next argument from list is precision   │.* 由下一个参数决定精度

│                                               │

│ Modifier      Howarg is Interpreted           │输入大小修饰词

│                                               │

│  F         arg is far pointer                  │F 远指标

│  N         arg is near pointer                 │N 近指标

│  h         d,i,o,u,x,X arg is short int        │h 短整数

│  l         d,i,o,u,x,X arg is long int         │l 长整数

│  l         e, E, f, g, G  arg is double        │l 倍精数(只有在 scanf)

│            (scanfonly)                        │

│  L         e,E,f,g,G   arg is long double      │L 长倍精数

└────────────────────────┘

 

 

 

┌────────── Help ───────────┐

│ scanf: performs formatted input from stdin     │由 stdin 读入格式化的数据

│                                               │

│ int scanf(const char *format, ...);            │scanf 的语法

│                                               │

│ Prototype in stdio.h                           │必须 #include

│                                                │

│ Returns the number of input fields processed   │传回成功读取的字段数。

│ successfully.  Itprocesses input according to │输入的数据会依照指定格式

│ the format and places the results in the       │储存,并将它们放到参数所

│ memory locations pointed to by the arguments.  │指到的内存。

│                                                │

│ See also    atof    cscanf   fscanf           │相关指令

│             getc    printf   sscanf           │

│             vfscanf vscanf   vsscanf          │

└────────────────────────┘

┌────────── Help ───────────┐

│ getch: gets character from console, no echoing │由控制台读入字符,不响应

│ getche: gets character from the console and    │由控制台读入字符,并

│         echoes toscreen                       │响应到屏幕

│                                               │

│ int getch(void);                               │getch 的语法

│ int getche(void);                              │getche 的语法

│                                               │

│ Prototype in conio.h                           │必须 #include

│                                                │

│ Both functions return the character read.      │getch 及 getche 两者都会

│ Characters are available immediately - no      │回传所读到的字符。由于没

│ buffering of whole lines.                      │有缓冲区,所传回的字符

│                                                │立即可用。

│ Special keys such as function keys and arrow   │功能键或方向键等这些特殊

│ keys are represented by a two character        │按键,会产生连续的两个字

│ sequence: a zero character followed by the     │元:第一个字符是 0,第二

│ scan code for the key pressed.                 │个是所按下键的扫描码。

│                                               │

│ See also    getpass   cgets     cscanf        │相关指令

│             kbhit     ungetch   putch        │

│             getchar   getc                    │

└────────────────────────┘

 

 

 

■ 第五章 流程图与抉择指令

 

   前一章的scanf好不好用呢?在多笔数据输入时,不小心输错一笔数据,可能会导致后面的读到的数据都是错的。而错误的输入数据,自然会使输出的数据也是错误的,这就是所谓的「垃圾进、垃圾出」( Garbage In, Garbage Out )。如果程序能分辨输入的数据是否正确,如果是错误的或是超过范围的,就可以要求使用者再输入一次。要达到这个目的,我们必须使用「判断」以及「循环」。本章将介绍如何用 C 来做判断,有关循环的部分参考下一章。

 

   ◎ 预习课程:流程图 ( Flow Chart )

 

     在进入本章之前,先介绍流程图。流程图不只可以用在程序的流程设计,也可以用在任何事件,例如:计划执行的流程图、休闲计划流程图等等。在程序设计上,小程序也许不用流程图。 但是中、大型程序最好要有流程图,有了流程图,可以进一步检视你的设计,看看是否在逻辑上有问题,看看那个部分可以再加强,那个部分可以简化,因为看流程图比看原始码要容易除错。另外流程图设计得好,程序的部分就显得相当简单了,只要把流程图的符号用对应的程序流程控制叙述取代,程序的架构就完成了。隔了一段时间,若须要增加或修改程序部分功能,有留下流程图,就可以快速进入状况,找到要修改的部分,并新增程序。以下介绍 6 种常用的流程图符号:

 

      ○ 开始/结束符号

 

           符        号          意                义          例        子

       ──────────  ──────────────  ──────────

         ╭──────╮    表示流程图的开始或结束,每个    ╭──────╮

         │            │    流程图必须以开始符号开始,以    │  开    始  │

         │            │    结束符号作结束。                ╰──────╯

         ╰──────╯                                     流程图的开始

                                                            ╭──────╮

                                                            │  结    束  │

                                                            ╰──────╯

                                                              流程图的结束

 

      ○ 输入/输出符号

 

           符        号          意                义          例        子

       ──────────  ──────────────  ──────────

             ___________    表示计算机与外界的连系,通常程          __________

           /          /    式需从外界输入数据,程序再将       / 输入     /

         /          /      执行后的结果输出给使用者。       / a, b, c  /

       /__________/        在符号中,输入数据时要注明是   /__________/

                             「输入」,在输出数据时要注明  读入三笔数据,并存在

                             是「输出」。                  a, b, c 三个变数内。

                                                                  ──────

                                                                / 输出     /

                                                             / a, b, c  /

                                                           /─────/

                                                      将变数 a,b,c 的数值输出。

 

      ○ 处理符号

 

           符        号          意                义          例        子

       ──────────  ──────────────  ──────────

        ┌───────┐   表示各种算术、函数的运算。     ┌───────┐

        │              │   在一个处理符号内可表示多个     │F = 100;      │

        │              │   运算。                         │C =(F-32)*5/9;│

        └───────┘                                 └───────┘

 

      ○ 抉择符号

 

           符        号          意                义          例        子

       ──────────  ──────────────  ──────────

             /\            表示各种算术、逻辑或关系运算       /\

           /    \          ,程序执行到这个地方必须做一     /a>=b\  No

         /  条件  \ No     决定,此项决定由符号内的条件     \    /──┐

         \        /→      来判断。当条件成立时,则程序       \/      │

           \    /          流程依 Yes 的方向指示继续执     Yes │       ↓

             \/            行;当条件不成立时,则程序          ↓   ┌─┴┐

          Yes ↓             流程依 No 的方向指示继续执行    ┌─┴┐ │c=0;│

                             。在这个菱形符号的四个角,上    │c=5;│└──┘

                             面的角通常是被上面的流程符号    └──┘

                             所指,其它的三个角,你可以任  当 a 大于或等于 b 时

                             选两个角做Yes 及 No 的流程   执行 c=5; 的运算,否

                             指向,只要标明清楚就可以了。  则执行c=0; 的运算。

 

      ○ 流向线

 

           符        号          意                义          例        子

       ──────────  ──────────────  ──────────

          ──────→     箭头方向表示程序执行的先后次   ┌───────┐

               │            序。                           │scanf("%d %d",│

               │                                           │       &a,&b);│

               ↓                                           └──┬────┘

          ──────┐                                          ↓

                     │                                         /\

            │    │  ↓                                       /a>=b\  No

            │    │                                           \    /──┐

            ├←─┘                                             \/      │

            ↓                                               Yes ↓      ↓

 

      ○ 连接符号

 

           符        号          意                义          例        子

       ──────────  ──────────────  ──────────

               ○            绘制流程图时,常会受到空间的        /\

                             限制,有时流向线需流到比较远      /a>=b\ No

                            的地方。这种长的流向线往往会      \    /─→?

                             使流程图看起来比较乱,有时流        \/

                             程图很大,可能须要许多页来绘     Yes ↓

                             制,这时,利用连接符号就可以        /\

                             简化流向线。                      /b>=c\  No

                             在连接符号内会有一个数字或字      \    /─→?

                             母做为识别。在不同的地方看到        \/

                             连接符号,只要它们的识别数字     Yes ↓

                             或字母是相同的,就表示它们是     ┌─┴─┐

                            相连接的。                       │ c=1; │

                                                             └─┬─┘

                                                                 ├←──?

                                                                 ↓

                                                                 /\

                                                              /a>=b\ No

                                                              \    /──┐

                                                                 \/      │

                                                             Yes ↓       ↓

 

      ○ 范例:星期天流程图

          ╭─────╮

          │ 开    始 │

          ╰──┬──╯

                ↓

          ┌──┴──┐

          │ 起    床 │

          └──┬──┘

                ↓

          ┌──┴──┐

          │刷牙、洗脸│

          └──┬──┘

                ↓

               /\

             /天气\     晴天

             \ ? / ─────┐

               \/             ↓

            下雨↓        ┌──┴──┐

          ┌──┴──┐  │ 看 电 影 │

          │ 看 报 纸 │  └──┬──┘

          └──┬──┘        ↓

                ↓        ┌──┴──┐

          ┌──┴──┐  │ 逛    街 │

          │ 读    书 │  └──┬──┘

          └──┬──┘        │

                ↓←──────┘

          ┌──┴──┐

          │ 吃 中 饭 │

          └──┬──┘

                ↓

          ╭──┴──╮

          │ 结    束 │

          ╰─────╯

 

   ◎ 关系与条件

 

      在数学上任两个数值一定会符合「三一律」,什么是「三一律」?假设两个数:

      一个是 a,一个是 b ,那 a 跟 b 的关系一定是下列三种的某一种:

      a 大于 b ; a 等于 b 或 a 小于 b 。

      在计算机程序中,常常要判断数值间的关系,例如:一些游戏软件会在一开始要求

      你输入密码,输入的密码与正确的密码相等,游戏才会继续执行;或是机密性的

      数据库管理系统会要求输入密码,系统再依输入的密码给予相对的权限,如:

      总经理可以看到全部的数据,一般员工只能看自己的数据。

      在 C 语言中提供了下列的关系运算子,让程序设计师判断两个数值的关系:

 

       ┌──────┬──────╥───┬───────────┐

       │ 关系运算子 │  意    义  ║ 例子 │      状      况      │

       ├──────┼──────╫───┼───────────┤

       │     >     │ 大于      ║ a>b  │ a 是否大于 b ?      │

       │     <     │ 小于      ║ a

       │     >=    │ 大于或等于 ║ a>=b │ a 是否大于等于 b ?  │

       │     <=    │ 小于或等于 ║ a<=b │ a 是否小于等于 b ?  │

       │     ==    │ 等于      ║ a==b │ a 是否等于 b ?     │

       │     !=    │ 不等于     ║ a!=b │ a 是否不等于 b ?    │

       └──────┴──────╨───┴───────────┘

 

      两个数的关系符合三一律,所以,当你使用上述任一个关系运算子时,所指定的

      状况不是成立,就是不成立。指定的状况成立,我们称之为「真(True)」,状况

      不成立,我们称之为「假(False)」。

      在 C 语言中,假(False)是用 0 来表示。不是假的,就是真的,所以,所有非 0

      的值,在C 语言中都表示真(True)。

      ★注意:在C 语言中 = 是设定运算子; == 是关系运算子,两者不可混用。

 

      ┌────┬────────────────────────────┐

      │a =3 ; │a 的数值设为 3                                          │

      ├────┼────────────────────────────┤

      │a ==3  │a 是否等于 3 ? a 等于 3 则此关系式为真,否则为假。     │

      └────┴────────────────────────────┘

 

   ◎ if 叙述

 

      if 叙述可用来作基本的抉择:                                ↓

                                                                /\

      如果某个条件成立,则要做某事。                           /    \

                                                        No  /条件测试\

      例:如果天下雨的话,我就要带伞。                  ┌─ \   ?   /

              ﹌﹌﹌          ﹌﹌﹌                    │不   \    /

              条件           要做的事                   │成     \/

                                                       │立      │成立

      if 叙述的型式如下:                              │        ↓Yes

      ┌────────────┐                      │  ┌──┴──┐

      │if(条件测试) { 指令群 } │                      │  │  指令群  │

      └────────────┘                      │  └──┬──┘

      意义:                                            └───→┤

      ┌─────────────────────────┐      ↓

      │如果 (条件测试) 是成立的,则执行 { 指令群 } 。    │

      └─────────────────────────┘

 

      ○ 指令群?

         在 C 语言中,所谓的指令群,乃是由数个(包括 0 个,1 个,...)指令

         括在一对大括号中而形成。例如:

 

            {}                      ────→    ;              空指令

           {                        ─┐

               i= i + 1;              ├──→    i = i + 1;     单一指令

           }                        ─┘

            {

               i= i - 1;

               j= i * 2;

            }

 

         这三者都可以称为指令群。指令群的后面不必以分号做为结束,因为右大括号

         便足以代表结尾在何处了。当指令群内只有一个指令时,我们也可以将这一对

         大括号省略,此时,该指令群便与一般指令无异。当指令群内没有任何指令时

         ,如果你要省略这对大括号,就要补上一个分号 ( ; ),表示是空指令。

 

      以下的范例,将使用者输入的数值取绝对值后再秀出:

        ┌────────────────────────────────┐

        │abs.c                                                          │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   int i;                                                       │

       5│                                                               │

       6│   printf("Enter an integer:");                                 │

       7│   scanf("%d",&i);                                              │

       8│  if( i < 0 ) i = -i;                                          │

       9│   printf("The absolute value of it is%d.\n", i );             │

      10│}                                                              │

        ├────────────────────────────────┤

        │Enteran integer:-100                                           │

        │Theabsolute value of it is 100.                                │

        └────────────────────────────────┘

                                ┌i  , 若 i >= 0

      绝对值的定义: │ i │ = ┤

                                └ -i, 若 i < 0

 

      所以在程序第 8 行,当 i < 0 成立时,就令 i = -i ,这样 i 就成为正数了。

 

   ◎ 二重选择题: if - else 叙述

 

      只有 if只能设定条件成立要做的事,                  ↓

      再加上else 则可设定条件不成立时                   /\

      要做的事。                                       /    \

      例如:如果好天气,就出去逛街,             No  /条件测试\ Yes

            否则,就在家看电视。              不┌─ \   ?   / ─┐成

                                              成│     \    /     │立

                                              立↓       \/       ↓

                                          ┌──┴──┐      ┌──┴──┐

      if-else 叙述的型式如下:            │  指令群2│      │  指令群1│

      ┌────────────┐        └──┬──┘      └──┬──┘

      │if(条件测试) { 指令群1 }│              └───→┬←───┘

      │else   { 指令群2 }      │                        ↓

      └────────────┘

      意义:

      ┌───────────────────────┐

      │如果 (条件测试) 是成立的,则执行 { 指令群1 };│

      │否则,执行 { 指令群2 }                        │

      └───────────────────────┘

 

      以下是课本的范例,将使用者输入的两个数值找出最大值后再秀出:

        ┌────────────────────────────────┐

        │max1.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   int i, j, max;                                               │

       5│                                                                │

       6│   printf("Enter 2 integer :");                                 │

       7│   scanf("%d %d", &i,&j);                                     │

       8│   if( i > j ) max = i;                                         │

       9│   else       max = j;                                         │

      10│   printf("The maximum of %d and %d is%d.\n", i, j, max );     │

      11│}                                                              │

        ├────────────────────────────────┤

        │Enter2 integer : 7 5                                           │

        │Themaximum of 7 and 5 is 7.                                    │

        └────────────────────────────────┘

 

   ◎ 多重选择题: if-else if 架构

 

      当状况不只一种时,光是 if-else 就不够用了。在 C 中,可以在 else

      之后再加上if-else 的架构,如此,就可多一个判断。同理,可在所加的 else

      之后再加上if-else ,直到所有状况都包含为止。

      如:如果气温小于 20 度,就穿两件衣服;气温小于 15 度,就穿三件衣服;

          气温小于10 度,就穿四件衣服。

 

      if-else if架构:

      ┌───────────────┐

      │if(条件测试1) { 指令群1 }     │

      │elseif(条件测试2) { 指令群2 }│

      │elseif(条件测试3) { 指令群3 }│

      │elseif...                    │

      │else{ 指令群n }             │

      └───────────────┘

      意义:

      ┌───────────────────────────┐

      │如果 (条件测试1) 是成立的,则执行 { 指令群1};        │

      │否则,如果 (条件测试2) 是成立的,则执行 { 指令群2 };  │

      │否则,如果 (条件测试3) 是成立的,则执行 { 指令群3 };  │

      │否则,如果...                                         │

      │否则,只好执行 { 指令群n }                            │

      └───────────────────────────┘

 

              ↓

             /\

           /    \

         /条件测试\ 不成立

         \   1   / ───┐

           \ ? /   No    ↓

             \/          /\

              │成立     /    \

              ↓Yes    /条件测试\ 不成立

        ┌──┴──┐ \   2   / ───┐

        │  指令群1 │   \ ? /   No    ↓

        └──┬──┘     \/          /\

              │            │成立     /    \

              │            ↓Yes    /条件测试\ 不成立

              │      ┌──┴──┐ \   3   / ───┐

              │      │  指令群2 │   \ ? /   No    ↓

              │      └──┬──┘     \/          /\

              │            │            │成立     /    \

              │            │            ↓Yes    /条件测试\ 不成立

              │            │      ┌──┴──┐ \   ...  /───┐

              │            │      │  指令群3 │   \ ? /   No    │

              │            │      └──┬──┘     \/           │

              │            │            │            │成立        │

              │            │            │            ↓Yes         │

              │            │            │      ┌──┴──┐      │

              │            │            │      │ 指令群...│      │

              │            │            │      └──┬──┘┌──┴──┐

              │            │            │            │      │  指令群n │

              │            │            │            │      └──┬──┘

              │            ↓            ↓            ↓            ↓

              ├←─────┴──────┴──────┴──────┘

              ↓

 

      以下的范例,仿真高速公路交通警察执勤状况。

        ┌────────────────────────────────┐

        │police.c                                                       │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   int speed;                                                  │

       5│                                                               │

       6│   printf("Enter the speed = ");                                │

       7│   scanf("%d", &speed );                                        │

       8│   if( speed < 60 )                                             │

       9│      printf("Too slow, speedup!\n");                          │

      10│   else if( speed < 90 )                                        │

      11│      printf("Good day,boss.\n");                             │

      12│   else if( speed < 100 )                                       │

      13│      printf("Too fast, slowdown!\n");                         │

      14│   else                                                        │

      15│      printf("I will give you aticket!\n");                    │

      16│}                                                              │

        ├────────────────────────────────┤

        │Enterthe speed = 50                                            │

        │Tooslow, speed up!                                             │

        │Enterthe speed = 80                                            │

        │Goodday, boss.                                                 │

        │Enterthe speed = 95                                            │

        │Toofast, slow down!                                            │

        │Enterthe speed = 120                                          │

        │Iwill give you a ticket!                                       │

        └────────────────────────────────┘

 

      这类的程序,在设计时要注意到条件测试的顺序,否则,答案可能会有问题。

      如:将第12 行与第 8 行的判断式对调,秀出讯息的第 13 行与第 9 行也对调:

 

             if(speed < 100 )

               printf("Too fast, slow down!\n");

            else if( speed < 90 )

               printf("Good day, boss.\n");

            else if( speed < 60 )

               printf("Too slow, speed up!\n");

            else

               printf("I will give you a ticket!\n");

 

      这样的话,程序只会有两种输出:

         speed<  100 ,秀出 Too fast, slow down!

         speed>= 100  ,秀出 I will give you a ticket!

 

      如果你无法确定你写的程序对不对,那你最好画画流程图来帮助判断。

 

      此程序的判断流程如下:

 

 

              ╭───╮

              │开  始│

              ╰─┬─╯

                 ↓

                ─┴───

              /输入车速/

            /──┬─/

                 ↓

                /\   No  表示车速 ≧ 60

               /小于\────┐

               \ 60 /        ↓

                \/         /\   No  表示车速 ≧ 90

              Yes│        /小于\────┐

                 ↓        \ 90 /       ↓

          ┌───┴───┐  \/         /\    No  表示车速≧ 100

          │  车速 < 60  │Yes│        /小于\ ──────┐

          │太慢了,加速!│   │        \ 100/             │

          └───┬───┘   ↓          \/               │

                  │  ┌───┴───┐Yes│                │

                 │   │60≦车速 <90│   │                │

                 │   │ 祝一路顺风。 │   │                │

                 │   └───┬───┘   ↓                ↓

                 │           │   ┌───┴───┐┌───┴───┐

                 │           │   │90≦车速<100││ 100 ≦ 车速  │

                 │           │   │太快了,减速!││赏你一张罚单!│

                 │           │   └───┬───┘└───┬───┘

                 │           ↓           ↓                ↓

                 ├←───────────┴────────┘

                 ↓

              ╭─┴─╮

              │结  束│

              ╰───╯

 

 

 

      以下是课本的范例:一个简单的四则运算计算器。

        ┌────────────────────────────────┐

        │calc.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   float num1,num2;                                             │

       5│   char op;                                                    │

       6│                                                               │

       7│   for(;;)                                                     │

       8│   {                                                           │

       9│      printf("Enter number, operator,number\n");               │

      10│      scanf("%f %c %f", &num1,&op, &num2);                     │

      11│      if( op == '+' )                                           │

      12│         printf("%f + %f = %f\n",num1, num2, num1+num2);       │

      13│      else if( op == '-' )                                      │

      14│         printf("%f + %f = %f\n", num1,num2, num1-num2);       │

      15│      else if( op == '*' )                                      │

      16│         printf("%f + %f = %f\n",num1, num2, num1*num2);       │

      17│      else if( op == '/' )                                      │

      18│         printf("%f + %f = %f\n",num1, num2, num1/num2);       │

      19│   }                                                           │

      20│}                                                              │

        ├────────────────────────────────┤

        │Enternumber, operator, number                                  │

        │4 +8                                                          │

        │4.000000+ 8.000000 = 12.000000                                │

        │Enternumber, operator, number                                  │

        │5 *7                                                          │

        │5.000000+ 7.000000 = 35.000000                                 │

        │^C                                                              │

        └────────────────────────────────┘

 

      在这个程序中,会读取二个数值以及一个运算符号,接着判断运算符号是否是

      + - * / 之中的一个,如果是的话就作相对应的运算,并将结果输出。

      另外,第7 行使用了下一章才会提到的「循环」。这个程序会不断的执行,

      你必须按下Ctrl + Break 或 Ctrl + C 才能中断它。

 

 

   ☆ 有关「抉择」☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

 

      前面所讲的抉择判断式子都相当简单,以下介绍三种 C 语言提供的方式,

      可以让你的「抉择」多样化。

 

   ◎ 逻辑运算子

 

      ○&&  AND

 

         假设有一个表达式 :  exp1 && exp2 

         则此表达式的结果为真的条件是 exp1 为真 且 exp2 也为真;

         否则此表达式的结果为假。

 

      ○ ||  OR

 

         假设有一个表达式 :  exp1 || exp2

         则此表达式的结果为真的条件是 exp1 为真 或 exp2 为真;

         只有在exp1 为假 且 exp2 也为假时,此表达式的结果才为假。

 

      ○!   NOT

 

         假设有一个表达式 :  !exp1

         则此表达式结果为真的条件是 exp1 为假;

         而当exp1 为真时,此表达式的结果就是假。

 

   ◎ 小括号指定叙述

 

      任何用小括号括住的指定叙述是一个表达式,它的值与指定值一样。

      例如:表达式 ( sum = 5 + 3 ) 的值为 8 ,所以表达式

      ( ( sum =5 + 3 ) <= 10 ) 结果为真,因为 ( 8 <= 10 ) 。

 

   ◎ 逗号运算子

 

      你可以利用逗号运算子 (,) 把多个表达式放入括号内。表达式由左至右执行或

      计算,整个表达式的值以最后一个的值为准。

      例如:表达式 ( i = 1, j = 2, k = i + j ) 的值为 k 也就是 3 ,因为,

      先设了 i= 1 ,再指定 j = 2 ,最后令 k = i + j ,所以 k 的值为 3 ,

      因为 k= i + j 是最后一个叙述,所以这个表达式的值就等于 k 也就是 3 。

 

      应用:

        ┌────────────────────────────────┐

        │yes.c                                                          │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│#include     /* 宣告 getche() 的原型 */               │

       3│voidmain(void)                                                │

       4│{                                                              │

       5│   char ch;                                                    │

       6│                                                               │

       7│   printf("Press Y or y to continuexxxxxx...");                │

       8│   if ( ( ch = getche() ) == 'Y' || ch == 'y')                 │

       9│     printf("\nYou press %c.\n",ch),                           │

      10│     printf("continuexxxxxx...\n");                            │

      11│   else                                                        │

      12│     printf("\nYou press %c.\n",ch),                           │

      13│     printf("stop xxxxxx!\n");                                  │

      14│}                                                               │

        ├────────────────────────────────┤

        │PressY or y to continue xxxxxx...y                             │

        │Youpress y.                                                   │

        │continue xxxxxx...                                              │

        │PressY or y to continue xxxxxx...q                             │

        │Youpress q.                                                   │

        │stopxxxxxx!                                                    │

        └────────────────────────────────┘

      在这个程序中,会要求使用者输入一个字符,如果输入的字符是 Y 或 y 则

      秀出You press y. continue xxxxxx... 类似程序继续执行某种程序;

      如果输入的不是 Y 也不是 y ,则秀出 Youpress X. stop xxxxxx! 类似程序

      停止执行某种程序。

 

      在第 8 行中,

      if ( ( ch= getche() ) == 'Y'  || ch == 'y' )

           ═╪═══╪═══

           │↑      │

           │└───┘由键盘读入一个键值,并存到 ch

           └→ 整个小括号的值就等于 ch

 

      所以上式可以简化为   if ( ch == 'Y' || ch == 'y')

      也就是,如果 ch 等于 'Y ' 或 'y' 则此判断式为真,否则为假。

 

      在第 9 行中,  printf("\nYou press%c.\n", ch),

      是用逗号( , ) 作为结尾的,而不是一般以分号 ( ; ) 来结尾,接着的第 10 行,

      则是以分号( ; ) 作结尾,表示第 9 行与第 10 行的指令是一体的。

      如果第 9行要用分号 ( ; ) 作结尾,那就要在第 9 行指令前及第 10 行

      指令后用大括号 { } 括起来,如:

 

         {  printf("\nYou press %c.\n", ch);

           printf("continue xxxxxx...\n");   }

 

      同理,在第12 行与第 13 行也用了相同的方法。

 

      各位也许会发现:第 9 行与第 12 行的程序代码完全相同的!没错,你可以想办法

      将第 9 行程序代码移到 if 之前,并删去第 12 行程序代码,这样程序就可以减小。

      本例只是用一下逗号运算子,所以才将程序写成这样。

 

      在这里,并不是要各位把程序写得很难看懂,而是希望各位能看得懂别人所写的

      程序,了解一些程序设计师可能的用的「技巧」或「手段」。

 

      以上的程序,我们也可以改写如下:

        ┌────────────────────────────────┐

        │yes2.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│#include     /* 宣告 getche() 的原型 */               │

       3│voidmain(void)                                                │

       4│{                                                              │

       5│   char ch;                                                    │

       6│                                                               │

       7│   printf("Press Y or y to continuexxxxxx...");                │

       8│   ch = getche();                                               │

       9│   if ( ch == 'Y' || ch == 'y' )                                │

      10│   {                                                           │

      11│     printf("\nYou press %c.\n",ch);                           │

      12│     printf("continuexxxxxx...\n");                           │

      13│   }                                                           │

      14│   else                                                        │

      15│  {                                                           │

      16│     printf("\nYou press %c.\n",ch);                           │

      17│     printf("stop xxxxxx!\n");                                  │

      18│   }                                                            │

      19│}                                                              │

        ├────────────────────────────────┤

        └────────────────────────────────┘

      这样子写,虽然程序比较长,但是在 Compile 之后产生的 .exe 档是一模一样

      大的【注】,只是程序代码比较长,却是比较「好看」。

 

   【注】用TC 整合环境所 Compile 出来的 .exe 文件中包含了除错信息(Debug

        Information),这些除错信息包含了原始码的行号数据,因为 yes.c 与yes2.c

         的行数不同,它们的行号数据就不会相同,所以用 TC 整合环境编译出来的执行

         檔大小是不同的。

         我们可以用命令列编译程序 tcc 来编译 yes.c 及yes2.c :

 

            DOS_Prompt> tcc yes.c

 

           DOS_Prompt> tcc yes2.c

 

         如此,产生的 yes.exe 及 yes2.exe 的档案大小就是一样的了。

 

 

 

 

■ 第六章 循环与自动重复

 

   前一章,已经用了 for 循环来写程序,让程序依需求重复执行某些程序代码。

   在 C 中有许多种循环,本章就以 for 循环为主,介绍循环设计的技巧。

 

   ◎ for叙述

 

      ○ for叙述的格式如下:

      ┌───────────────────────────┐

      │for( 指令一 ; 条件测试 ; 指令二 )                    │

      │{  指令群  }                                          │

      └───────────────────────────┘

      注意:在for 后面有对小括号,小括号里面的东西用分号隔成三个部份,

            这二个分隔用的分号绝对不可以省略掉。

 

      ○ for 叙述的意义:

      ┌───────────────────────────┐

      │1.先执行「指令一」。                                 │

      │2.测试看看「条件测试」是否成立?如果成立,则执行      │

      │  { 指令群 };否则,结束自动重复的动作。              │

      │3.在执行完 { 指令群 } 后,执行「指令二」,然后回到 2。│

      └───────────────────────────┘

 

      ○ for叙述流程图:

                              ↓

                       ┌──┴──┐

                       │指  令  一│

                       └──┬──┘

                              ↓

                             /\

                           /    \

                 不成立/  条件  \ ←────┐

                ┌─── \  测试  /           │

                │         \    /       ┌──┴──┐

                │           \/         │指  令  二│

                │        成立↓          └──┬──┘

                │      ┌──┴──┐          ↑

                │      │指  令  群├─────┘

                │      └─────┘

                │

                └──────┐

                              ↓

 

      在这里,指令一及指令二都是单一指令,如果你要作多项设定,那就要用到

      上一章最后所讲的「逗号运算子」,可以将多个指令写在一起。

 

      ○ 常见for 叙述的用法:

      ┌───────────────────────────┐

      │for( i = 0  ; i < 100 ; i = i+1 )                    │

      │   printf("i = %3d\n", i );                           │

      └───────────────────────────┘

      ┌───────────────────────────┐

      │for( i = 0 , j = 0 ; i < 100 ; i = i+1 , j = j+2 )   │

      │   printf("i = %3d j = %3d\n", i, j);                │

      └───────────────────────────┘

 

 

      ○ for叙述常用的格式:

      ┌───────────────────────────┐

      │for( 初值设定 ; 条件测试 ; 变化 )                    │

      │{ 指令群 }                                          │

      └───────────────────────────┘

 

      ○ for叙述常用格式的意义:

      ┌───────────────────────────┐

      │1.首先依「初值设定」的要求,设定你所指定变量的值。    │

      │2.测试看看「条件测试」是否成立?如果成立,则执行      │

      │  { 指令群 };否则,结束自动重复的动作。              │

      │3.在执行完 { 指令群 } 后,按照「变化」的要求,改变指定│

      │  变量的值,然后回到 2 。                             │

      └───────────────────────────┘

 

      ○ for叙述常用格式的流程图:

                              ↓

                       ┌──┴──┐

                       │ 初值设定 │

                       └──┬──┘

                              ↓

                             /\

                           /    \

                 不成立/  条件  \ ←────┐

                ┌─── \  测试  /           │

                │         \    /       ┌──┴──┐

                │           \/         │ 变    化 │

                │        成立↓          └──┬──┘

                │      ┌──┴──┐          ↑

                │      │指  令  群├─────┘

                │      └─────┘

                │

                └──────┐

                              ↓

 

 

      ○ 省略的for 叙述:

 

      ┌───────────────────────────┐

      │for( 指令一 ; 条件测试 ; 指令二 )                    │

      │{  指令群  }                                          │

      └───────────────────────────┘

         在 for 叙述中,指令一、条件测试、指令二以及指令群这四者都可以省略,

         △ 省略指令一:以常用格式来说,就是不做初值设定。

                       初值可在 for 之前设定,或者不用做初值设定。

         △ 省略条件测试:表示不做条件测试,重复执行 { 指令群 }。

                       我们通常称这种循环为「无穷循环」。

         △ 省略指令二:以常用格式来说,就是不做变量数值的改变。

                        变量数值可以在指令群中改变。

         △ 省略指令群:表示空循环,可用于时间延迟(delay)。

                       若连大括号都要省略,则要补上分号 ( ; ) 表示空指令。

         for 叙述括号内的指令是可以省的,但是分隔用的分号 ( ; ) 则不能省。

 

      以下是课本的范例:秀出 ASCII 码及相对应的字符。

        ┌────────────────────────────────┐

        │ascii.c                                                        │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   int i;                                                      │

       5│                                                               │

       6│   for( i = 32 ; i < 256 ; i = i+1 )                            │

       7│      printf("%3d=%c\t", i, i );                                │

       8│}                                                              │

        ├────────────────────────────────┤

        │32=     33=!    34="   35=#    36=$    37=%   38=&    39='  ...

        │42=*    43=+    44=,   45=-    46=.    47=/   48=0    49=1  ...

        │52=4    53=5    54=6   55=7    56=8    57=9   58=:    59=;  ...

        │...                                                            ...

        │252=*   253=*  254=*   255=                                    │

        └────────────────────────────────┘

 

            for( i = 32 ;  i < 256 ; i =i+1 )

                ========  ========  =========

                 初值设定  条件测试    变化

 

         在 C 语言中,我们通常会把  i = i + 1 这种变量递增写成  i++  。

         同样的,变量递减写成  i--  表示  i = i - 1  。

         如果递增或递减的数值不是 1 ,则写成 i += 2  表示i = i + 2

         或  i -= 2 表示  i = i- 2   。

         注意,在这里的 += 或 -= 不可以分开。 同样的,乘除法也有相同的用法,

         如 i *= 2  表示  i = i * 2 、  i /=2  表示 i = i /2  。

 

      以下的范例用来计算 n! 的值。

        ┌────────────────────────────────┐

        │n!.c                                                           │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   long fact;                                                   │

       5│   int n;                                                      │

       6│                                                               │

       7│   printf("Enter the value of n to computen! : ");             │

       8│  scanf("%d", &n );                                            │

       9│   printf("%d! = %d", n, n );                                   │

      10│   fact = n;                                                   │

      11│   for( n = n-1 ; n >0 ; n-- )                                  │

      12│   {                                                           │

      13│      fact *= n;                                               │

      14│      printf("x%d", n);                                         │

      15│   }                                                           │

      16│   printf(" = %ld", fact);                                      │

      17│}                                                              │

        ├────────────────────────────────┤

        │Enterthe value of n to compute n! : 5                          │

        │n! =5x4x3x2x1 = 120                                            │

        └────────────────────────────────┘

 

        n 阶乘的定义是  n! = n * (n-1) * (n-2) * ...* 2 * 1

        在上面的程序中以 fact 这个变量来存 n 阶乘的数值。此例来说,输入的数值

        为 5 ,所以第 9 行的 n 为 5 。第 10 行 fact = n; 设定fact = 5 。

        再来进入循环,先设定 n 为 n-1 也就是 4 ,因为 n 阶乘下一个是要乘 n-1 ,

        第 13 行 fact *= n 也就是 fact = fact * n ,因为每次循环 n 的值都会

        减 1 ,如此,就达到阶乘的效果。以下是 fact 及 n 在每次循环的数值:

 

        ┌─────┬──────┬──┬───────────┐

        │  时  刻  │  fact 值   │n 值│  印   出   效   果   │

        ├─────┼──────┼──┼───────────┤

        │进入循环前│ 5          │ 5  │5!= 5                │

        ├─────┼──────┼──┼───────────┤

        │n 初值设定│5          │4  │5! = 5                │

        ├─────┼──────┼──┼───────────┤

        │第 1 圈后│ 5*4        │ 4  │5!= 5x4              │

        ├─────┼──────┼──┼───────────┤

        │第 2 圈后│ 5*4*3      │ 3  │5!= 5x4x3            │

        ├─────┼──────┼──┼───────────┤

        │第 3 圈后│ 5*4*3*2    │ 2  │5!= 5x4x3x2          │

        ├─────┼──────┼──┼───────────┤

        │第 4 圈后│ 5*4*3*2*1  │ 1  │5!= 5x4x3x2x1        │

        ├─────┼──────┼──┼───────────┤

        │离开循环后│ 5*4*3*2*1  │ 1  │5!= 5x4x3x2x1 = 120  │

        └─────┴──────┴──┴───────────┘

 

   ◎ 重复中有重复

 

      在 for叙述所要重复执行的指令群中,也可以再含有另一个 for 叙述,于是便形

      成了重复中有重复的情形。一般我们称 for 叙述及所重复的指令群为 for 循环,

      而这种重复中有重复的情形,便称为巢状(nest)循环。(课本称为套迭式循环)

      在使用巢状循环时,最好使用缩排的编辑技巧,让你的程序更好看:

 

                     叙述;

                     ...

                     for( ... ; ... ; ... )

            ┌───→{

            │           叙述;

            │           ...

            │           for( ... ; ... ; ... )

            │ ┌───→{

            │ │           叙述;

            │ │           ...

            │ └────}

            │           叙述;

            │           ...

            └────}

 

      同样的技巧可以用在巢状 if 叙述,或其它类似的叙述。

 

      巢状循环可以多层,但是,各个循环之间绝对不可以交叉。

      以下是正确的巢状循环:

 

            ┌────→    for(...;...;...)

            │              {

            │┌───→       for(...;...;...)

            ││               {

            ││                  ...

            │└────       }

            │                 ...

            │┌───→       for(...;...;...)

            ││               {

            ││                  ...

            │└────       }

            └─────    }

 

            ┌────→    for(...;...;...)

            │              {

            │┌───→       for(...;...;...)

            ││               {

            ││                  ...

            ││┌──→          for(...;...;...)

            │││                {

            │││                   ...

            ││└───          }

            │└────       }

            │                 ...

            └─────    }

 

 

      以下是不正确的巢状循环:

 

            ┌─────→                    ┌──→

            │                                │

            │  ┌───→                ┌─┼──→

            │  │                        │  │

            └─┼────                │  └───

                │                        │

                └────                └─────

 

      以下的范例:印出九九表

        ┌────────────────────────────────┐

        │99.c                                                           │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   int i, j;                                                   │

       5│                                                               │

       6│   for( i=1 ; i<=9 ; i++ )                                      │

       7│   {                                                            │

       8│      for( j=1 ; j<=9 ; j++ )                                   │

       9│         printf("%dx%d=%2d ", j, i,j*i );                      │

      10│      printf("\n");                                             │

      11│   }                                                           │

      12│}                                                              │

        ├────────────────────────────────┤

        │1x1=1 2x1= 2 3x1= 3 4x1= 4 5x1= 5 6x1= 6 7x1= 7 8x1= 8 9x1= 9  │

        │1x2=2 2x2= 4 3x2= 6 4x2= 8 5x2=10 6x2=12 7x2=14 8x2=16 9x2=18  │

        │1x3=3 2x3= 6 3x3= 9 4x3=12 5x3=15 6x3=18 7x3=21 8x3=24 9x3=27  │

        │1x4=4 2x4= 8 3x4=12 4x4=16 5x4=20 6x4=24 7x4=28 8x4=32 9x4=36  │

        │1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 6x5=30 7x5=35 8x5=40 9x5=45  │

        │1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 7x6=42 8x6=48 9x6=54  │

        │1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 8x7=56 9x7=63  │

        │1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 9x8=72  │

        │1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81  │

        └────────────────────────────────┘

 

      九九表是1 到 9 的两个数相乘,一个是乘数,一个是被乘数,因此,

      我们需要两个循环, i=1  要乘以 1 到 9 、 i=2 也要乘以 1 到 9 、...,

      所以要用巢状循环的方式来处理这个问题。

      第 9 行的printf("%dx%d=%2d ", j, i, j*i ); 是属于内层循环,也就是在

      某一个 i值, j 要由 1 变化到 9 。

      第 10 行的 printf("\n"); 是属于外层循环,在内层循环执行完之后,就跳下一

      行。

 

 

   ◎ 跳出循环: break 叙述

 

      有时,我们可能在重复执行到一半时,检测到某个情况发生,而希望提早结束重复

      的动作,便可以利用 break 叙述。 break 叙述的功能相当单纯,它用来跳出一层

      的循环。注意!它只能跳出一层循环。如果你使用两层的巢状循环,那么在内层的

      break 只会跳出内层循环,在外层的 break 才会跳出外层循环。

 

            ┌────→    for(...;...;...)          /* 外层循环 开始 */

            │              {

            │┌───→       for(...;...;...)       /* 内层循环 开始 */

            ││               {

            ││                  ...

            ││                  if(...) break;      /* 跳出内层循环 */

            ││                  ...

            │└────       }                      /* 内层循环 结束 */

            │                 ...

            │                 if(...) break;         /* 跳出外层循环 */

            │                 ...

            └─────    }                         /* 外层循环 结束 */

 

 

   ○Ctrl + C  或  Ctrl + Break    中断按键

 

      当程序进入无穷循环时,在某些状况下,你可以按下 Ctrl + C 或 Ctrl + Break

      来中断程序的执行,但是在某些状况下,即使你按下 Ctrl + C 或 Ctrl + Break

      也没有用,只有重开机才能中断程序执行。所以,在程序中最好能正常结束,

      例如:使用者按下 q 键或 x 键表示结束程序等等,若你要使用无穷循环,最好能

      确定用Ctrl + C 或 Ctrl + Break 可以中断程序。

 

 

   ☆ 其它的循环 ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

 

      除了for 以外, C 语言还有 while 循环及 do ... while 循环。

 

   ◎while 循环

 

      ○while 叙述的格式如下:

      ┌───────────────────────────┐

      │while( 条件测试 )                                    │

      │{  指令群  }                                          │

      └───────────────────────────┘

 

      ○while 叙述的意义:

      ┌────────────────────────────────┐

      │1.测试看看「条件测试」是否成立?                               │

      │  如果成立,则执行{ 指令群 };否则,结束自动重复的动作。        │

      │2.执行完{ 指令群 }后,再回到 1 。                              │

      └────────────────────────────────┘

 

      ○while 叙述流程图:

                              ↓

                             /\

                           /    \

                 不成立/  条件  \ ←────┐

                ┌─── \  测试  /           │

                │         \    /             │

                │           \/               │

                │        成立↓                │

                │      ┌──┴──┐          │

                │      │指  令  群├─────┘

                │      └─────┘

                │

                └──────┐

                              ↓

 

      我们可以把while 循环用 for 循环来表示:

 

         while(i < 10 )            for(  ; i<10 ; )

         {                          {

           ...                        ...

         }                          }

 

 

   ◎ do... while 循环

 

      ○ do... while 叙述的格式如下:

      ┌───────────────────────────┐

      │do                                                   │

      │{                                                    │

      │   指令群                                             │

      │}while ( 条件测试 );                                 │

      └───────────────────────────┘

 

      ○ do... while 叙述的意义:

      ┌────────────────────────────────┐

      │1.先执行{ 指令群 }。                                            │

      │2.测试看看「条件测试」是否成立?                               │

      │  如果成立,则执行{ 指令群 };否则,结束自动重复的动作。        │

      │3.执行完{ 指令群 }后,再回到 2 。                              │

      └────────────────────────────────┘

 

      ○ do... while 叙述流程图:

                              │

                              ↓←──────┐

                       ┌──┴──┐        │

                       │指  令  群│        │

                       └──┬──┘        │

                              ↓              │

                             /\             │

                           /    \   成立    │

                         /  条件  \ ────┘

                         \  测试  /

                           \    /

                             \/

                              │不成立

                              ↓

 

      while 循环与do ... while 循环最大的不同点在于:

      while 循环要先条件测试,成立才执行指令群,而 do ... while 循环则是

      先执行指令群,才条件测试,成立再一次执行指令群。

 

 

 

 

■ 第七章 数组与指针

 

   数组是由一群相同型态的变量所组成,当你需要一堆相同型态的变量时,用数组是最适合的。例如,要储存全班 50 位同学的成绩,那就需要 50 个变量,如果要一个一个宣告,那就太没有效率了。这时就需要用数组了。指针则是指向变量或数组地址的运算子,任一变量或数组的数值均可经由指标的运算获得。

   ◎ 数组的宣告

 

      数组宣告的格式如下:

      ┌──────────────────┐

      │变量型别  变量名称[元素个数];       │

      └──────────────────┘

 

      以储存全班50 位同学的成绩为例,用整数来存成绩:

      ┌──────────────────┐

      │int  score[50];                     │

      └──────────────────┘

 

      C 语言中,数组的索引值是由 0 开始的,所以这 50 个变量为:

      score[0],score[1], score[2], ..., score[48], score[49] 。

 

      以下的范例:读取一周的气温,再求出其平均值。

        ┌────────────────────────────────┐

        │temper.c                                                       │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│   int t[7];                                                   │

       5│   int day, sum;                                               │

       6│                                                               │

       7│   for( day = 0 ; day < 7 ; day++ )                             │

       8│   {                                                           │

       9│      printf("Enter the temperature forday %d : ", day+1 );    │

      10│      scanf("%d", &t[day]);                                     │

      11│   }                                                           │

      12│                                                               │

      13│   sum = 0;                                                    │

      14│  for( day = 0 ; day < 7 ; day++ )                             │

      15│      sum = sum + t[day];                                       │

      16│                                                               │

      17│   printf("Average temperature =%f\n", sum/7. );               │

      18│}                                                              │

        ├────────────────────────────────┤

        │Enterthe temperature for day 1 : 22                            │

        │Enterthe temperature for day 2 : 20                            │

        │Enterthe temperature for day 3 : 18                            │

        │Enterthe temperature for day 4 : 19                            │

        │Enterthe temperature for day 5 : 23                            │

        │Enterthe temperature for day 6 : 24                            │

        │Enterthe temperature for day 7 : 26                            │

        │Averagetemperature = 21.714286                                 │

        └────────────────────────────────┘

 

      在第四章中,我们曾经列出各种变量型态的指针,以上面的例子:

 

         位 址         记   忆   体         指 标        假设地址

                  ├────────┤

        &t[0] ─→│      t[0]      │←─ t + 0        ( 1000 )h

                  ├────────┤

         &t[1]─→│      t[1]      │←─ t + 1        ( 1002 )h

                  ├────────┤

        &t[2] ─→│      t[2]      │←─ t + 2        ( 1004 )h

                  ├────────┤

        &t[3] ─→│      t[3]      │←─ t + 3        ( 1006 )h

                  ├────────┤

        &t[4] ─→│      t[4]      │←─ t + 4        ( 1008 )h

                  ├────────┤

        &t[5] ─→│      t[5]      │←─ t + 5        ( 100A )h

                  ├────────┤

        &t[6] ─→│      t[6]      │←─ t + 6        ( 100C )h

                   ├────────┤

 

      我们利用第10 行 scanf("%d", &t[day]); 读取数据,这里用的是每一个数组

      元素的地址  &t[day] ,day 由 0 到 6 。

      我们也可以改用指标的方式: scanf("%d", t+day );

      这里的 t是数组变量的名称,也就一个指标常数(constant pointer),在宣告

      数组变量的同时,就宣告了这个指标常数,只是这个指标常数的数值不是由你决定

      的,而是在程序执行时,它的数值才会确定,一但确定,就不会改变。

      t + day 的 day是 t 指标的增加值,假设 t 为 ( 1000 )h,

      那 t +1 是 ( 1001 )h 吗?

      C 语言在作指针运算时,会依指标的型别不同,而改变递增或递减的数值。

      以 t +1 来说, t 是一个整数的指标常数,在 TC2 中一个整数占有 2 bytes,

      所以 t+ 1 的数值是 ( 1002 )h ,而不是 ( 1001)h 。

      同理,t+ 2 的数值是 ( 1004 )h 。

 

      在上例第17 行, printf("Average temperature = %f\n",sum/7. );

      不知道你有没有注意到 sum/7. 在 7 后面的一点 (. ) ?

      这并不是Keyin错误,而是故意的。因为 sum 是一个整数变量,

      若写成  sum/7  表示整数除以整数,那结果还是整数。故意写成 sum/7.

      就变成整数除以实数,那结果会是实数。即然是平均值,通常会有小数,所以

      我们用%f 来秀出数据。

 

   ◎ 字符串

 

      字符串是一堆字符所组成的,它是一个字符数组,只是字符串它还要有个 '\0' 字符,

      作为字符串结束记号。例如,我们要用一个字符串存 DOS 的文件名:

      DOS 的檔名是主檔名最多 8 个字符,扩展名最多 3 个字符,中间以句号 ( . )

      分隔,所以需要 8 + 3 + 1 = 12 个字符来存盘名。但是别忘了字符串要有 '\0'

      的结束字符,所以,总共需要 12 + 1 = 13 个字符:

 

         charfilename[13];

 

      你可以用以下的程序片断来询问文件名:

 

        printf("Enter the file name : ");

        scanf("%12s", filename );

 

      scanf 是要变量的地址,而 filename[13] 这个变量的地址就是 filename ,

      你也可以用&filename[0] 。另外,用 %12s 这个句柄,表示读入的字符串只取

      前面 12个字符再加上 '\0' 字符,传给后面所指定的地址。如果你不加, 12

      而用 %s的话,当使用者输入超过 13 个字符时,将会发生不可预期的后果。

 

      以下的范例:读取一个字符串并作输出。

        ┌────────────────────────────────┐

        │string.c                                                       │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                │

       3│{                                                              │

       4│  char name[13];                                               │

       5│                                                               │

       6│  printf("Enter your name please :");                          │

       7│  scanf("%12s", name );                                         │

       8│  printf("Good day, Mr. %s.", name);                           │

       9│}                                                              │

        ├────────────────────────────────┤

        │Enteryour name please : Lee                                    │

        │Goodday, Mr. Lee.                                              │

        └────────────────────────────────┘

 

      你可以将第7 行的 %12s 改为 %s ,并且在执行程序时,输入一个较长的字符串,

      看看会发生什么事?

 

 

   ◎ 二维及多维数组

 

      C 语言除了一维数组外,也可以依需要宣告二维或以上的变量数组:

      ┌──────────────────────────────────┐

      │变数型别  变量名称[第一维元素个数][第二维元素个数];                 │

      └──────────────────────────────────┘

      ┌──────────────────────────────────┐

      │变数型别  变量名称[第一维元素个数][第二维元素个数][第三维元素个数];│

      └──────────────────────────────────┘

      不过会用到三维数组以上的机会蛮少的。

      以计算学生成绩为例,用一维数组  int score[50];  我们只能记录一科的成绩,

      用二维数组  int score[50][7];  就能记录七科的成绩,或者是记录单一科目的

      期中考、期末考、平常成绩、作业成绩等等。

 

      以下是课本的范例(加上范围判断):秀出使用者所指定坐标的位置。

        ┌────────────────────────────────┐

        │demo.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                 │

       3│{                                                              │

       4│   char matrix[5][10];                                          │

       5│   int x,y;                                                     │

       6│                                                               │

       7│   for( y=0 ; y<5 ; y++ )          /* 设定数组初值*/           │

       8│      for( x=0 ; x<10 ; x++)                                    │

       9│         matrix[y][x] = '.' ;                                   │

      10│                                                               │

      11│   printf("Enter the coordinate(0 0) - (94) : ");              │

      12│   scanf("%d %d", &x, &y);        /* 读取指定坐标 */           │

      13│                                                               │

      14│   for( ; !( ( x>=0 || x<=9 ) &&( y>=0 || y<=4 ) ) ; )         │

      15│   {                                                           │

      16│      printf("\aInvalidValue!!\n");                           │

      17│      printf("Please enter thecoordinate(0 0) - (9 4) : ");    │

      18│      scanf("%d %d", &x, &y);     /* 读取指定坐标*/           │

      19│   }                                                            │

      20│   matrix[y][x] = '*' ;            /* 设定指定坐标*/           │

      21│                                                               │

      22│   for( y=0 ; y<5 ; y++ )          /* 秀出数组值*/             │

      23│   {                                                           │

      24│      for( x=0 ; x<10 ; x++)                                    │

      25│         printf("%c", matrix[y][x]);                           │

      26│      printf("\n");                                             │

      27│   }                                                           │

      28│}                                                              │

        ├────────────────────────────────┤

        │Enterthe coordinate(0 0) - (9 4) :  5  3                       │

        │..........                                                     │

        │..........                                                     │

        │..........                                                     │

        │.....*....         ← 因为索引值是由 0 开始所以 * 在 6,4        │

        │..........                                                     │

        └────────────────────────────────┘

 

      第 14 行到第 19 行是利用一个 for 循环来判断输入的数值是否在要求的范围。

      在使用数组时,最好要注意一下使用索引值的范围,因为 C 不会做数组边界检查,

      当你使用大于你所宣告的索引值时,在语法上,并不会有任何的错误。但是当你使用

      索引值超过你所宣告的的数组时,你有可能改变到别的变量,甚至是程序代码。轻微的

      话,只是程序结果错误,严重的话,可能导致当机。

 

 

   ☆ 两维数组的指针 ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

 

      以  int s[50][3]; 为例

 

         位 址     记   忆   体       指 标                         假设状况

                ├──────┤

     &s[0][0] →│  s[0][0]   │← *(s + 0) + 0  ← s + 0  ← s   ( 1000 )h

                ├──────┤

     &s[0][1] →│  s[0][1]   │← *(s + 0) + 1                   ( 1002 )h

                ├──────┤

      &s[0][2]→│  s[0][2]   │← *(s + 0) + 2                   ( 1004 )h

                ├──────┤

     &s[1][0] →│  s[1][0]   │← *(s + 1) + 0  ← s + 1         ( 1006 )h

                ├──────┤

     &s[1][1] →│  s[1][1]   │← *(s + 1) + 1                   ( 1008 )h

                ├──────┤

     &s[1][2] →│  s[1][2]   │← *(s + 1) + 2                   ( 100A )h

                ├──────┤

     &s[2][0] →│  s[2][0]   │← *(s + 2) + 0  ← s + 2         ( 100C )h

                ├──────┤

                    ... ... ...

                ├──────┤

    &s[48][2] →│  s[48][2]  │← *(s +48) + 2     (30h*3+2)*2   ( 1124 )h

                ├──────┤

    &s[49][0] →│  s[49][0]  │← *(s +49) + 0  ← s +49         ( 1126 )h

                ├──────┤

    &s[49][1] →│  s[49][1]  │← *(s +49) + 1                   ( 1128 )h

                ├──────┤

    &s[49][2] →│  s[49][2]  │← *(s +49) + 2                   ( 112A )h

                ├──────┤

 

      s 是一个二维指标常数。

      一个一维指标,我们必须在指标前加一个星号 ( * ) 才能取得一维数组内的变量

      值,同样地,在二维指标,我们必须在指标前加二个星号,才能取得二维数组内的

      变数值。若是二维指标前的星号少于二个,例如,没有星号或只有一个,那它所表

      示的就还是一个指标。另外,二维指标前如果有一个星号,那就相当是降了一维,

      而成为一个一维指标。

 

   ○ 二维指标的递增值

 

      一个一维指标,如果它所指的变量型态占有 n 个 byte ,那这个一维指标的递增值

      就是 n 。

      一个二维数组: vartype Array[ X ][ Y ];  如果 vartype 占有 n 个 byte,那

      Array 这个二维指标的递增值就是 Y * n ,也就是 Y 个vartype 所占的空间。

      Array 是指向二维数组的开端 &Array[0][0],而 Array+1 正好指向 &Array[1][0]

      ,同理Array+i 是指向 &Array[i][0]。

 

      如前所述,在二维指标前加一个星号就会变成一维指标。所以, *Array 是个一维

      指标、*(Array+1)是一维指标、...、*(Array+i) 是一维指标、...、

     *(Array+(X-1)) 是一维指标,而这些一维指标都是指向第二维数组开始的地址。

      其中的 0≦ i ≦(X-1),如同决定Array[X][Y] 中第一维(X)的位移量(Offset)。

      这些一维指标的递增值 *(Array+i)、...、*(Array+i)+j、...、*(Array+i)+(Y-1)

      就指向第二维的每一个变量。

      其中的 0≦ j ≦(Y-1),如同决定Array[X][Y] 中第二维(Y)的位移量(Offset)。

      所以,*(Array + i) + j 就是指向 Array[i][j] 这个变量,同时

      *( *(Array+ i) + j ) 就是 Array[i][j] 这个变量。

 

      ★ C 语言不会作数组边界(Boundary)检查,例如,你宣告  int s[50][7];

         可是你可以使用 s[0][8]、s[51][2] 或s[100][200]。

 

   ○ 用指标的方式,修改上例,你看懂了吗?

        ┌────────────────────────────────┐

        │demoptr.c                                                      │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(),scanf() 的原型 */       │

       2│voidmain(void)                                                 │

       3│{                                                              │

       4│   char matrix[5][10];                                          │

       5│   int i,x,y;                                                  │

       6│                                                               │

       7│   for( i=0 ; i<5*10 ; i++ )       /* 设定数组初值*/           │

       8│       *((*matrix)+i) = '.' ;                                   │

       9│                                                                │

      10│   printf("Enter the coordinate(0 0) - (94) : ");              │

      11│   scanf("%d %d", &x, &y);        /* 读取指定坐标 */           │

      12│                                                               │

      13│   for( ; !( ( x>=0 || x<=9 ) &&( y>=0 || y<=4 ) ) ; )         │

      14│   {                                                           │

      15│      printf("\aInvalidValue!!\n");                           │

      16│      printf("Please enter thecoordinate(0 0) - (9 4) : ");    │

      17│      scanf("%d %d", &x, &y);     /* 读取指定坐标*/           │

      18│   }                                                           │

      19│   matrix[y][x] = '*' ;            /* 设定指定坐标*/           │

      20│                                                               │

      21│   for( y=0 ; y<5 ; y++ )          /* 秀出数组值*/             │

      22│   {                                                           │

      23│      for( x=0 ; x<10 ; x++)                                    │

      24│         printf("%c", *( *(matrix +y) + x) );                  │

      25│      printf("\n");                                             │

      26│   }                                                           │

      27│}                                                              │

        ├────────────────────────────────┤

        └────────────────────────────────┘

 

 

 

 

■ 第八章 函数与呼叫

 

   C 语言的程序,是由一堆函数(Function)或函式(Routine)所组成的。

 

   ○ 什么是函数?以 sin 这个函数为例:

    ┌────────────────────┐

    │sin: sine function                     │正弦函数。

    │                                        │

    │double sin(double x);                  │ sin 的语法

    │                                        │

    │Prototype in math.h                    │必须 #include

    │                                        │

    │ xis in radians.                       │ x 的单位是径度(rad)

    │Returns a value in the range -1 to 1.  │传回数值在 -1 到 1 之间。

    └────────────────────┘

 

     A = sin(M_PI/2 ); 我们都知道 sin(π/2)=1.0 ,所以这一行叙述相当于 A=1.0;

     我们可以把sin() 当成一个黑箱子,给它角度(rad),它会输出相对的sine 值。

 

        角度            ┌───────┐

         x    ────→┤    sin()    ├─────→  sin(x)

      (rad)            └───────┘

 

     在 C 语言中,函数的输入可以多个,例如: pow( X, Y) 可以计算 X 的Y 次方。

    ┌─────────────────┐

    │pow: power function, x to the y  │乘幂函数,X 的 Y 次方。

    │                                  │

    │double pow(double x, double y);  │ pow 的语法

    │                                  │

    │Prototype in math.h              │ 必须#include

    └─────────────────┘

     但是函数的输出值,也就是回传值,只能有一个。

 

   ○ 什么是函式?函式的英文为 routine ,原意是「例行公事」,也就是一些一成不变

      做完甲这个事后,就做乙,再做丙...。

      到现在为止,我们常用的 printf() 、 scanf() 都算是函式,而且在每一个程序也

      都用建立了main() 这个函式。通常称 main() 为主函式,而其它的都称做副函式

     (sub-routine)。

      在计算机语言中,函数、函式已经混在一起了,像教科书中的作者用函数,而笔者则

      是用惯了函式。

 

   ◎ 函式的宣告与定义

 

      函式宣告与定义的格式如下:

      ┌─────────────────────────────┐

      │传回值型别  函式名称(型别参数1, 型别 参数2, ...)        │

      │{                                                         │

      │    函式主体──包含宣告函式内部变量及指令群              │

      │}                                                        │

      └─────────────────────────────┘

 

      其中,第一列 传回值型别  函式名称(型别参数1, 型别 参数2, ...)

      我们称之为函式的宣告,而大括号 { } 内则是函式的定义。

 

      传回值型别,表示函式传回数值的数据型别,如果省略,则 C 语言会视为是内定

      的整数型别。在函式中,我们可以用  return(回传数值); 让函式传回数值,并且

      结束函式的执行。

      参数(或称自变量)可以使函式具有变化性,如果省略,则表示此函式不需要参数。

 

      ○ 养成好习惯

 

         如果省略传回值型别,等于定义了整数型别,那要是我们所设计的函式没有传回

         数值,应该怎么办呢?在 C 语言中,有一种资料型别叫 void ,void 表示

         「空」的数据型态,所以

 

            void函式名称(型别 参数1, 型别 参数2,...)

            {

              ...

            }

 

         就表示这个函式没有传回任何数值。

         同样地,如果设计的函式不用参数,也可以用 void 来表示:

 

            传回值型别  函式名称( void )

            {

              ...

            }

 

         就表示这个函式不需要任何参数。

 

   ◎ 呼叫函式

 

      C 语言在使用变量或是函式之前,都必须先宣告所使用的变量或是函式。

      这也是为什么,我们所写的程序都要 #include ,因为,

      在stdio.h 中,宣告了 printf() 、scanf() 等函式的原型(prototype)。

      同样地,在使用自己所定义的函式前,也要先宣告。

      前一节说过,在定义函式的同时,也做了宣告。如此,我们可以将自己写的函式

      放在main() 的前面,再由 main() 来呼叫。如同课本的范例:

 

      以下是课本的范例:让计算机发出「哔」!

        ┌────────────────────────────────┐

        │beep.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│#include     /* 宣告 getche() 的原型 */               │

       3│                                                               │

       4│voidbeep(void)        /* 副函式 beep() 的宣告 */               │

       5│{                      /* 开始定义 beep() */                    │

       6│   printf("\a");       /* 「哔」一声 */                         │

       7│}                      /* beep() 定义结束 */                    │

       8│                                                                │

       9│voidmain(void)        /* 主函式 main() 的宣告 */               │

      10│{                      /* 开始定义 main() */                    │

      11│   beep();                                 /* 呼叫 beep() */    │

      12│   printf("Press any key tocontinue..."); /* 呼叫 printf() */  │

      13│   getche();                               /* 呼叫 getche() */  │

      14│   beep();                                 /* 呼叫 beep() */    │

      15│}                      /* main() 定义结束 */                    │

        ├────────────────────────────────┤

        │「哔」!一声                                                   │

        │Pressany key to continue...                                    │

        │「哔」!一声                                                    │

        └────────────────────────────────┘

 

      void 既然是「空」的数据型态,所以在呼叫时就是「空的」。

      如果,你还记得的话,getche() 会传回使用者所按下的键值,但是在这里,我们

      只是要使用者按下任意键,所以读入的键值是多少我们并不在意,可以不管它,

      就如同呼叫不会传回数值的 beep() 函式一样。printf() 也是有传回值的,

      它传回输出的 byte 数,而这个数值,我们通常也不会在意。

 

      以下是课本的范例:以条形图来比较数值的大小。

        ┌────────────────────────────────┐

        │bar.c                                                          │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│                                                               │

       3│voidbar(int i)        /* 副函式 bar() 的宣告 */                │

       4│{                      /* 开始定义 bar() */                     │

       5│   int j;                                                      │

       6│                                                               │

       7│   for( j=0 ; j

       8│      printf("*");                                              │

       9│   printf("\n");                                               │

      10│}                      /* bar() 定义结束 */                     │

      11│                                                               │

      12│voidmain(void)                                                │

      13│{                                                              │

      14│   printf("Merry\t");                                           │

      15│   bar(30);              /* 输入参数为 30 */                   │

      16│   printf("John\t");                                            │

      17│   bar(40);               /* 输入参数为40 */                   │

      18│   printf("Johnson\t");                                         │

      19│   bar(20);               /* 输入参数为20 */                   │

      20│   printf("Sposh\t");                                           │

      21│   bar(50);               /* 输入参数为50 */                   │

      22│}                                                               │

        ├────────────────────────────────┤

        │Merry   ******************************                          │

        │John   ****************************************                │

        │Johnson********************                                    │

        │Sposh  **************************************************      │

        └────────────────────────────────┘

 

      各位可能会发现,课本在宣告 bar() 函式时,好像跟老师所用的不一样:

 

          bar(i)

          inti;        /* 宣告参数的数据型别 */

          {

             ...

          }

 

      这种宣告参数的方式,是旧的 C 语言格式,目前的 TC 仍然可以使用。

 

      在呼叫有参数的函式时,给定的参数会代入函式中相对的变数。

      如:bar(30); 就是给定 30 作为输入参数,此例来说,就是先设定 i = 30 ,

      再开始执行第 5 行以后的指令。

 

 

 

 

      以下是课本的范例:计算圆面积的副函式。

        ┌────────────────────────────────┐

        │area.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(), scanf() 的原型 */      │

       2│                                                                │

       3│floatareas(float r)                                            │

       4│{                                /*              2   */        │

       5│   return( 3.14159 * r * r );    /* 圆面积 = πr    */         │

       6│}                                                              │

       7│                                                               │

       8│voidmain(void)                                                │

       9│{                                                               │

      10│   float radius;                                               │

      11│                                                               │

      12│   printf("Enter the radius =");                               │

      13│   scanf("%f", &radius);                                        │

      14│   printf("Area of this circle =%f\n", areas( radius ) );      │

      15│}                                                              │

        ├────────────────────────────────┤

        │Enterthe radius = 6                                            │

        │Areaof this circle = 113.097240                                │

        └────────────────────────────────┘

 

      在第 14行中, areas( radius ) 表示以 radius 为参数呼叫 areas()函式,

      areas() 会传回一个float 型别的数值。我们可以把 areas( radius ) 整个当

      成一个变数来看,而它的数值与 radius 有关。

      例如,我们要计算两个半径分别为 r1 及 r2 的圆面积总和:

 

        totalarea = areas( r1 ) + areas( r2 ) ;

 

 

      以下是课本的范例:求任意二个整数的最大值。

        ┌────────────────────────────────┐

        │max.c                                                          │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(), scanf() 的原型 */      │

       2│                                                                │

       3│intmax(int i, int j)                                           │

       4│{                                                              │

       5│   if( i > j ) return i;     /* 若 i > j 则传回较大的 i 值 */  │

       6│   else       return j;     /* 否则是 j 比较大,就传回 j 值 */ │

       7│}                                                              │

       8│                                                               │

       9│voidmain(void)                                                 │

      10│{                                                              │

      11│   int i, j;                                                   │

      12│                                                               │

      13│   printf("Enter 2 integers :");                               │

      14│   scanf("%d %d", &i,&j);                                     │

      15│   printf("Maximum of %d and %d is%d.\n", i, j, max(i,j) );    │

      16│}                                                               │

        ├────────────────────────────────┤

        │Enter2 integers : 6 8                                          │

        │Maximumof 6 and 8 is 8.                                        │

        └────────────────────────────────┘

 

      事实上,TC2 有提供 max() 这个函式,你可以在 TC 的整合环境下的编辑窗口内

      输入max ,将光标移到 max 字上,按下 Ctrl+F1 就可以看到说明

 

        ┌────────── Help ──────────┐

        │Macros: max, min                            │宏指令:max, min

        │   These macros generate inline code tofind  │以宏的方式求得两数

        │   the maximum or minimum value of two        │的最大值或最小值。

        │   integers.                                  │

        │                                              │

        │   max(a,b) maximum of two integers a and b  │求 a,b 的最大值

        │   min(a,b) minimum of two integers a and b  │求 a,b 的最小值

        │                                              │

        │Defined in stdlib.h                         │必须 #include

        └───────────────────────┘

 

      由上可知,max() 是定义在 stdlib.h 内,所以我们可以改写上例如下:

        ┌────────────────────────────────┐

        │max1.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf(), scanf() 的原型 */      │

       2│#include    /* 定义 max() 的原型 */                  │

       3│                                                               │

       4│voidmain(void)                                                │

       5│{                                                              │

       6│   int i, j;                                                   │

       7│                                                               │

       8│   printf("Enter 2 integers : ");                               │

       9│   scanf("%d %d", &i,&j);                                     │

      10│   printf("Maximum of %d and %d is%d.\n", i, j, max(i,j) );    │

      11│}                                                              │

        ├────────────────────────────────┤

        │Enter2 integers : 6 8                                          │

        │Maximumof 6 and 8 is 8.                                        │

        └────────────────────────────────┘

 

      在这里只是要告诉各位,TC 它有提供非常多的内建函式,有一些功能我们并不

      须要再去设计一次。如果要作练习,那就另当别论。建议有意要写程序的人,多

      多参考「参考手册」,大略知道所使用的语言提供了那些内建函式,要用时,只

      要依照它的格式来呼叫它就可以了。要不然,你可以花了许多时间在写一个内建

      函式,而知道的人,只要 #include ??.h 就可以直接使用它。

 

 

   ◎ 整体变量(Global Variable)

 

      在前面的例子中,所有的变量都只能在所宣告的函式内使用,函式与函式之间只

      能用传回值或是参数来传递数值。除了用传回值与参数外,在 C 语言中还可以使用

      「整体变量」。

      什么是「整体变量」?由字面上来看,就是适用于程序整体,都可以使用的变量。

      相对于整体变量的就是「区域变量(Local Variable)」。同样地,由字面上来看,

      就是只适用于程序的某一个区域所能使用的变量。

 

      我们要如何判断变量可以使用的范围呢?以下就来谈谈变数的生命周期。

      以一个副函式来说:

 

         void sub1(void)

         {

            inti;     ←── 变数 i 的「生」

            ...

         }     ←────── 变数 i 的「灭」

 

      我们所写的函式,都会用一对大括号 { ... } 括起来,而在大括号内宣告的变量,

      就适用于大括号内。当这个函式执行结束时,在大括号内所宣告的变量,也就消逝

      不见。这种变量,就是区域变量,它能活动的区域,就是在所宣告的大括号内。

 

      事实上,在 TC2.0 中,你可以在函式中任意加入一对大括号 { } ,在大括号中,

      前面可宣告变量,之后可以是程序代码。改写前面 bar.c 为例:

 

        ┌────────────────────────────────┐

        │bar2.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│                                                               │

       3│voidbar(int i)                                                │

       4│{                                          /* 开始定义 bar() */ │

       5│   int j;                                                      │

       6│   for( j=0 ; j

       7│   printf("\n");                                               │

       8│}                                          /*bar() 定义结束 */ │

       9│                                                               │

      10│voidmain(void)                                                │

      11│{                                                               │

      12│   int i = 50;  ←────────────────────┐外 │

      13│   bar(i);             /* 使用外层的 i为 50 */           │层 │

      14│   {                                                      │i  │

      15│      int i = 20;  ←───────────────┐内层  │的 │

      16│      bar(i);             /* 使用内层的 i为 20 */ │i 的  │生 │

      17│      i = i + 10;                                  │生命  │命 │

      18│      bar(i);             /* 使用内层的 i为 30 */ │周期  │周 │

      19│   }               ←───────────────┘      │期 │

      20│   i = i + 10;                                             │   │

      21│   bar(i);             /* 使用外层的 i为 60 */           │   │

      22│}                ←────────────────────┘   │

        ├────────────────────────────────┤

        │**************************************************              │

        │********************                                            │

        │******************************                                  │

        │************************************************************    │

        └────────────────────────────────┘

 

      在主程序中,故意宣告两个同名的整数变量 i,它们分别在两组巢状的大括号内。

      在外层的i 适用于整个 main() 函式。不巧的是,内层大括号也宣告了一个 i,

      使得在内层的程序代码只能使用内层宣告的 i。内层大括号执行结束后,内层的 i

      就寿终正寝,外层的 i 就又开始有作用了。

      如果,你在内层不另外宣告 i 这个变量,那在内层的程序也可以使用外层的 i,

      你可以把第15 行的程序代码 remark 掉,看看结果有什么不一样。

 

      总归一句话,在大括号内所宣告的变量,就是区域变量。随着大括号的结束,这一

      区的变数也就失去作用。等到下一次再执行到这段程序代码时,这一区的变数才会

      重生。

 

      那整体变量要如何宣告呢?由上可知,只要是在大括号内宣告的就是区域变量,那

      整体变量,就是要宣告在大括号以外的地方。例:

 

 

        ┌─#include<...>

        │

        │   int i;   ←─────────────────┐整体变量

      程│                                                │

        │   void sub1(int a)  ←─────┐参数          │

        │   {                             │同样只适用于  │

        │      int j;  ←───┐区域变量│函式之内      │

        │      ...              │        │              │

        │   }          ←───┴────┘              │

        │                                                │

      式│   void sub2( b )                                │

        │   int b;     ←────────┐是参数,      │

        │   {                             │不是整体变量,│

        │      int k;  ←───┐区域变量│同样只适用于 │

        │      ...              │        │函式之内      │

        │   }          ←───┴────┘              │

        │                                                │

        │   void main(void)                               │

      码│   {                                             │

        │      int c;  ←────┐区域变量              │

        │      ...                │                      │

        └─}           ←────┴───────────┘

 

 

   ☆Call by Value  v.s.  Call by Address ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

 

   ○Call by Value 传值呼叫

 

      前面我们所设计的副函式都是使用「传值呼叫」,也就是副函式的参数变量不是

      指针型变量。以副函式 bar() 为例,我们把它改写如下,它的功能同上例:

 

        ┌────────────────────────────────┐

        │bar3.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│                                                                │

       3│voidbar(int i)                                                │

       4│{                                                              │

       5│   for( ; i>0 ; i-- ) /* i 值,由传进来的数值递减至 0 */       │

       6│      printf("*");                                              │

       7│   printf("\n");                                               │

       8│}                                                              │

       9│                                                                │

      10│voidmain(void)                                                │

      11│{                                                              │

      12│   int i = 50;                                                  │

      13│   int j = 30;                                                 │

      14│                                                               │

      15│   bar( 20 );          /* 以数值 20 呼叫 bar() 函式 */          │

      16│                                                                │

      17│   bar( j );           /* 以 j 变量的数值 30 呼叫 bar() 函式 */ │

      18│   printf("j = %d\n", j );   /* 秀出 j 的数值 */                │

      19│                                                               │

      20│   bar( i );           /* 以 i 变量的数值 50 呼叫 bar() 函式 */ │

      21│   printf("i = %d\n", i );   /* 秀出 i 的数值 */                │

      22│}                                                              │

        ├────────────────────────────────┤

        │********************                                            │

        │******************************                                  │

        │j =30                                                         │

        │**************************************************              │

        │i =50                                                         │

        └────────────────────────────────┘

 

      bar(20); 表示:把20 这个数值传给 bar() 函式做为参数 i 的数值,

               我们可以这样看被呼叫的 bar() 函式:

 

            void bar(...)

            {

              int i = 20;   ───────┐ 传进来的数值是 20

              for(  ; i>0 ; i-- )         │

                 printf("*");            │

              printf("\n");              │

           }                ←──────┘ 变数 i 的灭亡

 

     bar(j);  表示:把j 这个变数的数值 30 传给 bar() 函式做为参数 i 的数值,

               我们可以这样看被呼叫的 bar() 函式:

 

            voidbar(...)

            {

              int i = 30;   ───────┐ 传进来的数值是变数 j 的数值 30

              for(  ; i>0 ; i-- )         │

                  printf("*");             │

              printf("\n");              │

           }                ←──────┘ 变数 i 的灭亡

 

      由于是传入变量的数值,所以不论函式如何改变传入的数值,都不会影响原来传入

      的变数。请各位不要被变量名称所蒙骗,即使你使用与函式参数同名的变量,它一

      样只是传值,如范例中的

 

     bar(i);  表示:把i 这个变数的数值 50 传给 bar() 函式做为参数 i 的数值,

               我们可以这样看被呼叫的 bar() 函式:

 

            voidbar(...)

            {

              int i = 50;   ───────┐ 传进来的数值是变数 i 的数值 50

              for(  ; i>0 ; i-- )         │

                 printf("*");            │

              printf("\n");              │

           }                ←──────┘ 变数 i 的灭亡

 

 

      总结来说,传值呼叫,就是不会改变所传入变量的数值。

      如果想要改变传入的变量,就要使用「传址呼叫」。

 

   ○Call by Address 传址呼叫

 

      我们一直在使用的 scanf() 函式,就是一个传址呼叫的函式,其一般型式如下:

 

       ┌────────────────────────────┐

       │scanf("控制字符串" , &变量1 , &变量2 , ... );            │

       └────────────────────────────┘

 

      我们传给scanf() 的参数是变量的地址,所以 scanf() 可以把读入的字符串依

      控制字符串的格式转换成数值,存到变量的地址,这样在结束 scanf() 之后,

      我们传给scanf() 的变量数值,就会是 scanf() 所读入的数值。

      前面说过,函式的传回数值只有一个,而利用传址呼叫的设计,就可以传回更多的

      数值。以下就来看一个传址呼叫的范例:将两个整数值互换。

 

        ┌────────────────────────────────┐

        │swap.c                                                         │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf() 的原型 */               │

       2│                                                               │

       3│voidswap(int *a, int *b)                                       │

       4│{                                                               │

       5│   int backup = *a ;    /* 把 *a 的数值存到 backup 变数内  */  │

       6│   *a = *b ;            /* 把 *b 的数值存到 *a              */  │

       7│   *b = backup ;        /* 把 backup 的数值存到 *b 完成互换 */ │

       8│}                                                               │

       9│                                                               │

      10│voidmain(void)                                                │

      11│{                                                              │

      12│   int i = 50 , j = 30 ;                                        │

      13│                                                               │

      14│   printf("Before swap(): i = %d   j = %d\n", i, j);            │

      15│                                                                │

      16│   printf("Swapping i & j...\n");                             │

      17│   swap( &i , &j );                 /* 传入i 与 j 的地址 */    │

      18│                                                               │

      19│   printf("After  swap(): i = %d   j = %d\n", i, j);            │

      20│}                                                              │

        ├────────────────────────────────┤

        │Beforeswap(): i = 50   j = 30                                  │

        │Swappingi & j ...                                              │

        │After  swap(): i = 30   j = 50                                  │

        └────────────────────────────────┘

 

 

 

 

■ 第九章 档案存取

 

   前面几章所写的程序,都是将结果直接显示在屏幕上,若有输入的数值,则利用键盘输入,想要知道程序执行的结果,就要再执行一次。  本章则是将程序执行的结果存成档案,存好的档案,你可以直接显示出来看,或者是由程序直接读取数据文件,当作是输入的数值,经运算后,再另存档案。

 

   ◎ 档案的观念

 

      我们的数据及程序,都是以档案的型式存在磁盘驱动器中。每一种应用程序通常会使用

      自定的格式来存取它的数据文件,应用程序之间如果没有互相支持,就不能读取对方

      所产生的数据文件,例如:PEII 这个 DOS 下的文书处理器,就不能读取 WORD 所编

      辑出来的档案。

      在磁盘驱动器中的档案可能很多,在使用时,我们必须先指定要处理档案的名称,作

      「开启档案」的动作,再依程序设计的格式存取档案内的数据,处理完后,则要

      「关闭档案」。

      为什么要这么麻烦呢?因为,我们在作档案存取时,是去呼叫系统程序所提供的

      程序模块,在「开启档案」后,系统会预置一些内存空间来存这个开启的档案。

      你在做档案数据的处理时,系统并不一定会马上将结果存回磁盘,它等预置的记

      忆体内的数据都处理完时,才会将数据存入磁盘。所以在使用完档案之后,就要

      作「关闭档案」的动作,这样,系统才知道你已经不再使用这个档案了,而将预

      置内存内尚未储存的数据,存入磁盘,再收回预置的内存,供其它程序使用。

      如果,你不作「关闭档案」的动作,那程序就会一直占用住那一块内存,形成

      内存的浪费,此外,处理档案不正常的结束,可能使新增的数据流失,还可能

      使磁盘驱动器的档案位置配置表(FAT,File Allocation Table)发生失去链接(Lost

      Chain) 或是交错连结(Cross Link)的状况。

      所以,要养成好习惯:在程序中,只要有「开启档案」,就要有对应的

      「关闭档案」。

 

   ◎ 档案的开启和关闭

 

      要开启档案时,可以用 fopen 。其格式如下:

      ┌────────────────────────────┐

      │FILE *fp;                                              │

      │ fp=fopen("文件名称","存取模式");                       │

      └────────────────────────────┘

 

      在这里,fp是一个指针,它指向 FILE 结构变量。FILE 结构内存着一些关于档案

      的信息,如:档案位置指示,数据传输缓冲区的长度及其在内存中的地址等等,

      通常我们可以不用去理会这些数值,只要会使用 C 所提供的档案处理函式,就可以

      了。FILE*fp; 中的 fp 通常称为档案指标。在使用 C 所提供的档案处理函式时,

      只要指定好档案指标,就是对那个已开启的档案做相对应的处理。

      fopen() 函式的两个参数,第一个是"文件名称",也就是要处理的文件名称,如果不

      在所执行的目录,就要指定档案的全名,也就是要包含路径。

      第二个参数是"存取模式",有下列字符串可供选择:

 

        存取模式       意                                        义

        ==== ============================

          r     开启一存在档案仅供读取使用。当档案不存在时,将传回错误。

          w     开启档案仅供写入。当档案不存在时,会产生新档。

          a     开启一个档案仅供增添数据。当档案不存在时,会产生新档。

          r+    开启一存在档案供读/写使用。当档案不存在时,将传回错误。

          w+    开启一档案供读/写使用。当档案不存在时,会产生新档。

          a+    开启一档案供读取及增添数据。当档案不存在时,会产生新档。

        ==== ============================

 

      另外,还可以再加上 b ,表示以二进制的模式来存取数据文件。例如:

 

         fp1 =fopen("letter.txt","w");

         fp2 =fopen("c:\\work\\test.txt","a+");

         fp3 =fopen("score.dat","r");

         fp4 =fopen("database.dat","w+b");

 

 

      要开启档案在使用完毕后,可以用 fclose 来关闭 。其格式如下:

      ┌────────────────────────────┐

      │fclose( 已开启的档案指标 );                            │

      └────────────────────────────┘

      如:fclose( fp );

 

      对于已经关闭的档案,如果还要再使用,则必须重新开启。

 

   ◎ 档案的写入

 

      要将数据输出到档案时,可以用 fprintf 。其格式如下:

      ┌────────────────────────────┐

      │fprintf( fp, "控制字符串", 表达式1, 表达式2, ...);       │

      └────────────────────────────┘

      fprintf() 的格式如同printf() ,只是输入参数中的第一个参数必须是档案指标

      ,也就是指定出 fprintf() 要输出到那一个档案。有关 printf() 请参考第四章。

 

      以下是课本的范例:将使用者输入的交易数据,编排成一份报表档案。

        ┌────────────────────────────────┐

        │record.c                                                       │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf, scanf, fopen,fprintf...*/│

       2│#include    /* 宣告 strcmp() 的原型 */               │

       3│                                                               │

       4│voidline(FILE *fp)    /* 列出分隔线 */                         │

       5│{                                                              │

       6│   int i;                                                      │

       7│   for( i=0 ; i<60 ; i++ )                                      │

       8│      fprintf( fp, "-" );                                       │

       9│   fprintf( fp, "\n" );                                         │

      10│}                                                              │

      11│                                                                │

      12│voidmain(void)                                                │

      13│{                                                              │

      14│   FILE *fp;           /* 档案指标       */                     │

      15│   char filename[20];  /* 输出报表的文件名 */                     │

      16│   char client[40];    /* 交易对象名称   */                     │

      17│   float amount;       /* 交易金额       */                     │

      18│   float total=0;      /* 总金额         */                     │

      19│                                                               │

      20│   printf("File to record the amount :");                      │

      21│   scanf("%19s", filename );        /* 读取输出报表文件名*/      │

      22│   fp = fopen( filename, "w");      /* 开启报表文件 */            │

      23│   if( fp == NULL )                 /* 判断是否开启成功 */      │

      24│      printf("\aCannot open %s foroutput!\n",filename);        │

      25│   else                                                        │

      26│   {                                /* 开启成功 */              │

      27│      line( fp );                                               │

      28│      printf("Client : ");                                      │

      29│      scanf("%39s", client);                                    │

      30│      for( ; strcmp(client, "end") !=0 ; ) /* clinet=="end" */ │

      31│      {                                     /* 时,循环结束  */ │

      32│         printf("Amount = ");                                   │

      33│         scanf("%f",&amount);                                 │

      34│         total = total + amount ;                               │

      35│         fprintf( fp, "%-40s $%f\n",client, amount);           │

      36│                                                                │

      37│         printf("Client : ");                                   │

      38│         scanf("%39s", client);                                 │

      39│      }                                                         │

      40│      line( fp );                                               │

      41│      fprintf( fp, "%-40s $%f\n","*****  Total", total );      │

      42│      line( fp );                                               │

      43│   }                                                           │

      44│   fclose(fp);                      /* 关闭报表文件 */            │

      45│}                                                              │

        ├────────────────────────────────┤

        │Fileto record the amount : test                                │

        │Client: Nanya_College                                          │

        │Amount= 9876.54                                               │

        │Client: ABC_Company                                            │

        │Amount= 1234.56                                               │

        │Client: IJK_lmn_...                                            │

        │Amount= 2323.23                                               │

        │Client: Xyz....                                               │

        │Amount= 2222.20                                               │

        │Client: zzzzzz                                                │

        │Amount= 1111.10                                               │

        │Client: end                                                   │

        └────────────────────────────────┘

         test 档案内容

        ┌────────────────────────────────┐

        │------------------------------------------------------------    │

        │Nanya_College                            $9876.540039           │

        │ABC_Company                              $1234.560059           │

        │IJK_lmn_...                              $2323.229980           │

        │Xyz....                                 $2222.199951           │

        │zzzzzz                                  $1111.099976           │

        │------------------------------------------------------------    │

        │*****  Total                             $16767.630859          │

        └────────────────────────────────┘

 

      因为我们是用 scanf() 来读取字符串的,而 scanf() 会把空格符当成是数据的分

      隔,所以在输入客户名称时以底线代替空白。(这也是 scanf() 的缺点之一)

      第 30 行,循环以 strcmp(client, "end") != 0 作为结束的条件判断。其中,

      strcmp 是 TC 的内建函式,可用来比较两个字符串是否相等,若相等,则传回 0 ,

      否则传回非0 值。所以当使用者输入客户名称为 "end" 时, strcmp 会传回 0,

      而结束for 循环。

 

   ◎ 档案的读取

 

      要读取档案内的资料时,可以用 fscanf 。其格式如下:

      ┌────────────────────────────┐

      │fscanf( fp, "控制字符串", &变量1, &变量2, ...);          │

      └────────────────────────────┘

      fscanf() 的格式如同scanf() ,只是输入参数中的第一个参数必须是档案指标

      ,也就是指定出 fscanf() 要读取那一个数据文件。有关 scanf() 请参考第四章。

 

      以下的范例:读取档案内容,将各个字符与ASCII值一起显示。

        ┌────────────────────────────────┐

        │ftoascii.c                                                     │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf, scanf, fopen,fprintf...*/│

       2│                                                               │

       3│voidmain(void)                                                │

       4│{                                                              │

       5│   FILE *fp;           /* 档案指标       */                     │

       6│   char filename[20];  /* 读取的文件名称 */                     │

       7│   char ch=0;                                                  │

       8│                                                               │

       9│   printf("File name : ");                                      │

      10│   scanf("%19s", filename );        /* 读取输入档名*/          │

      11│   fp = fopen( filename, "r");                                  │

      12│   if( fp == NULL )                 /* 判断是否开启成功 */      │

      13│      printf("\aCannot open %s !\n",filename);                  │

      14│   else                                                        │

      15│      for( ; ch != EOF ; )          /* ch == EOF 时,循环结束 */│

      16│      {                                                        │

      17│         fscanf( fp, "%c",&ch);    /* 读取一个字符 */          │

      18│         printf("%c = %d\n", ch,ch);                           │

      19│      }                                                        │

      20│   fclose(fp);                                                  │

      21│}                                                              │

        ├────────────────────────────────┤

        │Filename : ftoascii.c                                          │

        │# =35                                                          │

        │i =105                                                        │

        │n =110                                                        │

        │......  (省略)                                                 │

        │} =125                                                        │

        │=-1                                                           │

        └────────────────────────────────┘

      第 15 行,循环以 ch != EOF 作为结束的条件判断。其中,EOF 是一个字符,

      表示档案结束(End Of File)的字符。所以当 fscanf 读取到档案结束字符时,

      循环就会结束执行。

 

      以下是课本的范例:读取档案内容,并将档案行距加倍后,另存新档。

        ┌────────────────────────────────┐

        │2space.c                                                       │

        ├────────────────────────────────┤

       1│#include     /* 宣告 printf, scanf, fopen,fprintf...*/│

       2│                                                               │

       3│voidmain(void)                                                │

       4│{                                                               │

       5│   FILE *fpi, *fpo;    /* 档案指标       */                     │

       6│   char filename[20];  /* 读取的文件名称 */                     │

       7│   char ch=0;                                                   │

       8│                                                               │

       9│   printf("File to be read : ");                                │

      10│   scanf("%19s", filename );        /* 读取输入档名*/          │

      11│   fpi = fopen( filename, "r");     /* 开启输入档案 */          │

      12│   if( fpi == NULL )                /* 判断是否开启成功 */      │

      13│   {                                                           │

      14│      printf("\aCannot open %s!\n",filename);                  │

      15│      return ;                      /* 结束 main() 函式 */      │

      16│   }                                                           │

      17│                                                               │

      18│   printf("File to be written :");                             │

      19│   scanf("%19s", filename );        /* 读取输出档名*/          │

      20│   fpo = fopen( filename, "w");     /* 开启输出档案 */          │

      21│   if( fpo == NULL )                /* 判断是否开启成功 */      │

      22│  {                                                           │

      23│      printf("\aCannot open %s foroutput!\n",filename);        │

      24│      fclose(fpi);       /* 已开的 fpi 在结束前要先关闭 */      │

      25│      return ;                      /* 结束 main() 函式 */      │

      26│   }                                                           │

      27│                                                               │

      28│   for( ; ch != EOF ; )                                         │

      29│   {                                                           │

      30│      fscanf( fpi, "%c",&ch);      /* 读取一个字符 */          │

      31│      fprintf( fpo, "%c", ch);      /* 写入一个字符 */          │

      32│      if( ch == '\n')               /* 如果读到跳行字符 */      │

      33│        fprinf( fpo, "\n");         /* 就再跳一行*/            │

      34│   }                                                           │

      35│                                                               │

      36│   fclose(fpo);                     /* 关闭输出档(后开先关) */ │

      37│   fclose(fpi);                     /* 关闭输入档(先开后关) */ │

      38│}                                                              │

        ├────────────────────────────────┤

        │Fileto be read : test                                          │

        │Fileto be written : doubled                                    │

        └────────────────────────────────┘

         double 档案内容

        ┌────────────────────────────────┐

        │------------------------------------------------------------    │

        │                                                               │

        │Nanya_College                            $9876.540039           │

        │                                                                │

        │ABC_Company                              $1234.560059           │

        │                                                               │

        │IJK_lmn_...                              $2323.229980           │

        │                                                                │

        │Xyz....                                 $2222.199951           │

        │                                                               │

        │zzzzzz                                   $1111.099976           │

        │                                                               │

        │------------------------------------------------------------    │

        │                                                               │

        │*****  Total                             $16767.630859          │

        │                                                               │

        └────────────────────────────────┘

 

 

 

 

 

 

 

┌────────── Help ───────────┐

│ fopen: opens a stream                          │开启一个「档案流」。

│                                               │

│ FILE *fopen(const char *filename,              │fopen 的语法

│              const char *mode);               │

│                                               │

│ Prototype in stdio.h                           │必须 #include

│                                               │

│ Returns a pointer to the newly open stream if  │若成功则传回开启档案的指标

│ successful; else it returns NULL.              │否则传回NULL (空指标)

│                                                │

│ See also    fclose   creat    open            │相关指令

│             dup      ferror   _fmode          │

│             rewind   setbuf   setmode         │

└────────────────────────┘

┌────────── Help ───────────┐

│ fclose: closes a stream                        │关闭一个「档案流」。

│                                               │

│ int fclose(FILE *fp);                          │fclose 的语法

│                                               │

│ Prototype in stdio.h                           │必须#include

│                                               │

│ Returns 0 on success; it returns EOF if any    │若关闭成功则传回 0 ,

│ errors are detected.                           │发生错误则传回 EOF 字符。

│                                                │

│ See also    fflush  flushall                  │相关指令

│             fopen   close   fcloseall         │

└────────────────────────┘

┌────────── Help ───────────┐

│ fprintf: sends formatted output to a stream    │将格式化的数据输出到档案流

│                                                │

│ int fprintf(FILE *fp, const char *format, ...);│fprintf 的语法

│                                               │

│ Prototype in stdio.h                           │必须 #include

│                                                │

│ Uses the same format specifiers as printf,     │使用方法同 printf(),

│ but fprintf sends output to the specified      │只是将结果输出到指定的

│ stream fp. fprintf returns the number of bytes │档案指标 fp 。

│ output.  In eventof error, it returns EOF.    │

│                                                │

│ See also    putc    fscanf                    │相关指令

└────────────────────────┘

┌────────── Help ───────────┐

│ fscanf: performs formatted input from a stream │由档案流读取格式化的数据

│                                                │

│ int fscanf(FILE *fp, const char *format, ...); │fscanf 的语法

│                                               │

│ Prototype in stdio.h                           │必须 #include

│                                               │

│ Returns the number of input fields success-    │使用方法同 printf(),

│ fully scanned, converted, and stored; the      │只是由指定的档案流 fp 读入

│ return value does not include unstored         │格式化的数据。

│ scanned fields.                                │

│                                                │

│ See also    getc    fprintf   scanf           │相关指令

└────────────────────────┘

┌────────── Help ───────────┐

│ strcmp: compares s2 to s1                      │比较两个字符串

│                                               │

│ int  strcmp(constchar *s1, const char *s2);   │strcmp 的语法

│                                               │

│ Prototype in string.h                          │必须 #include

│                                               │

│ Returns a value that is < 0 if s1 is less than │如果 s1 < s2 则传回值 < 0

│ s2; == 0 if s1 is the same as s2; > 0 if s1 is │如果 s1 = s2 则传回值 = 0

│ greater than s2. Performs a signed comparison. │如果 s1 > s2 则传回值 > 0

└────────────────────────┘

┌────────── Help ───────────┐

│ gets: gets a string from stdin                 │由stdin 读取字符串

│                                               │

│ char *gets(char *string);                      │gets的语法

│                                               │

│ Prototype in stdio.h                           │必须#include

│                                               │

│ Collects input from stdin until a newline      │由 stdin 读取字符串,直到遇到

│ character (\n) is found. The \n is not        │跳行字符 \n 。

│ placed in the string.                          │\n 不包含在所读入的字符串中。

│                                               │

│ Returns a pointer to the argument string.      │所传回的指针指向参数字符串。

│                                               │

│ See also    ferror     getc                   │相关指令

│              fopen      puts                   │

│             fread      scanf                  │

└────────────────────────┘

 

 

你可能感兴趣的:(程序设计-C++丨C丨C#,DOS)