计算器程序讲义
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
第二列: 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
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
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
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
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
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
2│#include
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
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
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
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
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
2│#include
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
2│#include
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
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
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
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
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
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
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
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
2│#include
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
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
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
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
2│#include
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
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
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
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
2│#include
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
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
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 │
└────────────────────────┘