C语言知识点难点总结

总体上必须清楚的:

1)程序结构是三种: 顺序结构 , 循环结构(三个循环结构), 选择结构(if 和 switch)

2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择)。

3)计算机的数据在电脑中保存是以 二进制的形式. 数据存放的位置就是 他的地址.

4)bit是位 是指为0 或者1。 byte 是指字节, 一个字节 = 八个位.

5)一定要记住 二进制 如何划成 十进制。

概念常考到的:

1、编译预处理不是C语言的一部分,不再运行时间。C语言编译的程序称为源程序,它以ASCII数值存放在文本文件中。

2、每个C语言程序中main函数是有且只有一个。

3、在函数中不可以再定义函数。

4、算法的是一定要有输出的,他可以没有输入。

5、break可用于循环结构和switch语句。

6、逗号运算符的级别最低。

第一章

(1)合法的用户标识符考查:

合法的要求是由字母,数字,下划线组成。有其它元素就错了。

并且第一个必须为字母或则是下划线。第一个为数字就错了。

关键字不可以作为用户标识符号。main define scanf printf 都不是关键字。迷惑你的地方If是可以做为用户标识符。因为If中的第一个字母大写了,所以不是关键字。

(2)实型数据的合法形式:

2.333e-1 就是合法的,且数据是2.333×10-1。

考试口诀:e前e后必有数,e后必为整数。.

(3)字符数据的合法形式:

'1' 是字符占一个字节,"1"是字符串占两个字节(含有一个结束符号)。

'0' 的ASCII数值表示为48,'a' 的ASCII数值是97,'A'的ASCII数值是65。

(4) 整型一般是两个字节, 字符型是一个字节,双精度一般是4个字节:

考试时候一般会说,在16位编译系统,或者是32位系统。碰到这种情况,不要去管,一样做题。掌握整型一般是两个字节, 字符型是一个字节,双精度一般是4个字节就可以了。

(5)转义字符的考查:

在程序中 int a = 0x6d,是把一个十六进制的数给变量a 注意这里的0x必须存在。

在程序中 int a = 06d, 是一个八进制的形式。

在转义字符中,’\x6d’ 才是合法的,0不能写,并且x是小写。

‘\141’ 是合法的, 0是不能写的。

‘\108’是非法的,因为不可以出现8。

(6)算术运算符号的优先级别:

同级别的有的是从左到右,有的是从右到左。

(7)强制类型转换:

一定是 (int)a 不是 int(a),注意类型上一定有括号的。

注意(int)(a+b)和(int)a+b 的区别。 前是把a+b转型,后是把a转型再加b。

(8)表达式的考查:

是表达式就一定有数值。

赋值表达式:表达式数值是最左边的数值,a=b=5;该表达式为5,常量不可以赋值。

自加、自减表达式:假设a=5,++a(是为6), a++(为5);

运行的机理:++a 是先把变量的数值加上1,然后把得到的数值放到变量a中,然后再用这

个++a表达式的数值为6,而a++是先用该表达式的数值为5,然后再把a的数值加上1为6,

再放到变量a中。 进行了++a和a++后在下面的程序中再用到a的话都是变量a中的6了。

考试口诀:++在前先加后用,++在后先用后加。

逗号表达式:优先级别最低 ;表达式的数值逗号最右边的那个表达式的数值。

(2,3,4)的表达式的数值就是4。

(9)位运算的考查:

会有一到二题考试题目。

总的处理方法:几乎所有的位运算的题目都要按这个流程来处理(先把十进制变成二进制再变成十进制)。

例1: char a = 6, b;

b = a<<2; 这种题目的计算是先要把a的十进制6化成二进制,再做位运算。

例2: 一定要记住,

例3: 在没有舍去数据的时候,<<左移一位表示乘以2;>>右移一位表示除以2。

(10)018的数值是非法的,八进制是没有8的,逢8进1。

(11)%符号两边要求是整数。不是整数就错了。

第二章

(1)printf函数的格式考查:

%d对应整型;%c对应字符;%f对应单精度等等。宽度的,左对齐等修饰。

%ld对应 long int;%lf 对应double。

(2)scanf函数的格式考察:

注意该函数的第二个部分是&a 这样的地址,不是a;

Scanf(“%d%d%*d%d”,&a,&b,&c); 跳过输入的第三个数据。

(3)putchar ,getchar 函数的考查:

char a = getchar() 是没有参数的,从键盘得到你输入的一个字符给变量a。

putchar(‘y’)把字符y输出到屏幕中。

(4)如何实现两个变量x ,y中数值的互换(要求背下来)

不可以把 x=y ,y=x; 要用中间变量 t=x;x=y;y=t。

(5)如何实现保留三位小数,第四位四舍五入的程序,(要求背下来)

这个有推广的意义,注意 x = (int)x 这样是把小数部分去掉。

第三章

特别要注意:c语言中是用非0表示逻辑真的,用0表示逻辑假的。

(1)关系表达式:

表达式的数值只能为1(表示为真),或0(表示假)

当关系的表达是为真的时候得到1。如 9>8这个是真的,所以表达式的数值就是1;

(2)逻辑表达式:

只能为1(表示为真),或0(表示假)

a) 共有&& || ! 三种逻辑运算符号。

b) !>&&>|| 优先的级别。

c) 注意短路现象。考试比较喜欢考到。

d) 要表示 x 是比0大,比10小的方法。0<x<10是不可以的(一定记住)。< span="span">是先计算0要用 (0<x)&&(x<10)表示比0大比10小。< span="span">

(3)if 语句

else 是与最接近的if且没有else的相组合的。

(4)条件表达式:

表达式1 ?表达式2 :表达式3

注意是当非0时候是表达式2的数值,当为0是就是表达式2的数值。

考试口诀:真前假后。

(5)switch语句:

a)一定要注意 有break 和没有break的差别,书上(34页)的两个例子,没有break时候,只要有一个case匹配了,剩下的都要执行,有break则是直接跳出了swiche语句。

b)switch只可以和break一起用,不可以和continue用。

第四章

(1)三种循环结构:

a)for() ; while(); do- while()三种。

b)for循环当中必须是两个分号,千万不要忘记。

c)写程序的时候一定要注意,循环一定要有结束的条件,否则成了死循环。

d) do-while()循环的最后一个while();的分号一定不能够丢。(当心上机改错)

(2) break 和 continue的差别

记忆方法:

break:是打破的意思,(破了整个循环)所以看见break就退出真个一层循环。

continue:是继续的意思,(继续循环运算),但是要结束本次循环,就是循环体内剩下的语句不再执行,跳到循环开始,然后判断循环条件,进行新一轮的循环。

(3)嵌套循环

就是有循环里面还有循环,这种比较复杂,要一层一层一步一步耐心的计算,一般记住两层是处理二维数组的。

(4) while((c=getchar())!=’\n’) 和 while(c=getchar() !=’\n’)的差别

先看a = 3 != 2 和(a=3)!=2 的区别:

(!=号的级别高于=号 所以第一个先计算 3!=2) 第一个a的数值是得到的1;第二个a的数值是3。

考试注意点: 括号在这里的重要性。

第五章

函数:是具有一定功能的一个程序块;

(1) 函数的参数,返回数值(示意图):

main()

{

int a = 5,b=6,c;

c = add(a,b);

printf(“%d”,c);

}

调用函数

a,b是实参

整个函数得到一个数值就是

Add函数的返回数值。

int add ( int x, int y)

{

int z;

z=x+y;

return z;

}

被调用函数

x,y是形式参数

函数返回数值是整型

z就是这个add函数计算后得到的结果,就是函数返回给主程序的返回数值。

程序是在从上往下顺序执行,当碰到了函数add后,把a,b的数值穿给调用函数,程序暂时中断等待返回数值。当得到了返回数值后,再顺序的往下执行

(2)一定要注意参数之间的传递

实参和形参之间 传数值,和传地址的差别。(考试的重点)

传数值的话,形参的变化不会改变实参的变化。

传地址的话,形参的变化就会有可能改变实参的变化。

(3)函数声明的考查

一定要有:函数名,函数的返回类型,函数的参数类型。

不一定要有:形参的名称。

第六章

指针变量的本质是用来放地址,而一般的变量是放数值的。

int *p 中 *p和p的差别:

*p可以当做变量来用;*的作用是取后面地址p里面的数值

p是当作地址来使用。

*p++ 和 (*p)++的之间的差别:改错题目中很重要

*p++是 地址会变化。

(*p)++ 是数值会要变化。

三名主义:(考试的重点)

数组名:表示第一个元素的地址。数组名不可以自加,他是地址常量名。(考了很多次)

函数名:表示该函数的入口地址。

字符串常量名:表示第一个字符的地址。

第七章

一维数组的重要概念:

对a[10]这个数组的讨论。

1、a表示数组名,是第一个元素的地址,也就是元素a[10]的地址。

2、a是地址常量,所以只要出现a++,或者是a=a+2赋值的都是错误的。

3、a是一维数组名,所以它是列指针,也就是说a+1是跳一列。

对a[3][3]的讨论。

1、a表示数组名,是第一个元素的地址,也就是元素a[10]的地址。

2、a是地址常量,所以只要出现a++,或者是a=a+2赋值的都是错误的。

3、a是二维数组名,所以它是行指针,也就是说a+1是跳一行。

4、a[0]、a[1]、a[2]也都是地址常量,不可以对它进行赋值操作,同时它们都是列指针,a[0]+1,a[1]+1,a[2]+1都是跳一列。

5、注意a和a[0] 、a[1]、a[2]是不同的,它们的基类型是不同的。前者是一行元素,后三者是一列元素。

二维数组做题目的技巧:

如果有a[3][3]={1,2,3,4,5,6,7,8,9}这样的题目。

步骤一:把他们写成: 第一列 第二列 第三列

a[0]à 1 2 3 ->第一行

a[1]à 4 5 6 —>第二行

a[2]à 7 8 9 ->第三行

步骤二:这样作题目间很简单:

*(a[0]+1)我们就知道是第一行的第一个元素往后面跳一列,那么这里就是a[0][1]元素,所以是1。

*(a[1]+2)我们就知道是第二行的第一个元素往后面跳二列。那么这里就是a[1][2]元素,所以是6。

一定记住:只要是二维数组的题目,一定是写成如上的格式,再去做题目,这样会比较简单。

数组的初始化,一维和二维的,一维可以不写,二维第二个一定要写

int a[]={1,2} 合法。 int a[][4]={2,3,4}合法。 但int a[4][]={2,3,4}非法。

二维数组中的行指针

int a[1][2];

其中a现在就是一个行指针,a+1跳一行数组元素。 搭配(*)p[2]指针

a[0],a[1]现在就是一个列指针。a[0]+1 跳一个数组元素。搭配*p[2]指针数组使用

还有记住脱衣服法则:

a[2] 变成 *(a+2) a[2][3]变成 *(a+2)[3]再可以变成 *(*(a+2)+3)

这个思想很重要!

</x)&&(x<10)表示比0大比10小。<></x<10是不可以的(一定记住)。

 

第二篇:c语言知识点总结与习题

C程序设计

20xx年3月1日

1知识点总结与习题

第一篇 知识点总结篇

第1章 C语言程序的基本形式 .........................................................................................................4

1.1C语言程序的基本结构................................................................................................................4

1.2字符集与标识符 .......................................................................................................................5

1.3保留字(即关键字).......................................................................................................................5

1.4 基本数据类型..............................................................................................................................6

1.4.1C语言的数据类型.................................................................................................................6

1.4.2常量与变量...........................................................................................................................6

1.5算术表达式...................................................................................................................................9

1.6 C语言的基本语句.....................................................................................................................10

1.7运行C语言的步骤和开发环境.................................................................................................13

第2章 流程控制 ............................................................................................................................14

2.1 控制表达式................................................................................................................................14

2.2 FOR循环语句...............................................................................................................................15

2.3 WHILE语句 .............................................................................................................................17

2.4 DO-WHILE语句 .......................................................................................................................18

2.5 IF语句 ......................................................................................................................................20

2.6 条件表达式运算符 ................................................................................................................23

2.7 BREAK语句.................................................................................................................................23

2.8 CONTINUE语句 .......................................................................................................................24

2.9 SWITCH语句...............................................................................................................................25

第3章 数组与字符串 ....................................................................................................................30

3.1 一维数组 ................................................................................................................................30

3.2 多维数组 .................................................................................................................................31

3.3 数组元素初始化 ....................................................................................................................31

3.4 字符串 .....................................................................................................................................32

3.5 字符串函数 ............................................................................................................................34

第4章 函数与变量 ........................................................................................................................36

4.1 C程序设计的一般形式 ...........................................................................................................36

4.2 函数.............................................................................................................................................37

4.3函数返回值 ..............................................................................................................................39

4.4 函数的调用 .............................................................................................................................40

4.5 递归函数与递归调用.................................................................................................................42

4.6 外部函数和内部函数.................................................................................................................43

4.7 变量的存储类型.........................................................................................................................44

4.8 函数的数据传递.........................................................................................................................49

第5章 指针 ....................................................................................................................................53

5.1指针和地址..................................................................................................................................53

5.2指针变量和指针运算符..............................................................................................................53 2

5.3 指针与函数参数 .....................................................................................................................57

5.4 指针、数组和字符串指针 .....................................................................................................58

5.5 指针数组 .................................................................................................................................61

5.6 多级指针.....................................................................................................................................63

5.7 返回指针的函数.........................................................................................................................64

5.8 函数指针 .................................................................................................................................65

5.9 命令行参数.................................................................................................................................65

第6章 结构与联合 ..........................................................................................................................66

6.1 结构的定义.................................................................................................................................66

6.2结构数组 ..................................................................................................................................68

6.3 结构与函数 .............................................................................................................................69

6.4 结构的初始化 .........................................................................................................................71  6.5 联合(UNION) .......................................................................................................................71

第7章 预处理程序 ..........................................................................................................................73

7.1什么是预处理程序......................................................................................................................73

7.2宏定义和宏替换 ......................................................................................................................73

7.3文件包含 .................................................................................................................................74

7.4条件编译 .................................................................................................................................75

7.4格式化输入/输出.......................................................................................................................76

第8章 枚举、位操作 ......................................................................................................................77

8.1枚举 .........................................................................................................................................77

第9章文件............................................................................................................................................80

9.1 ASCII码文件的存取...................................................................................................................80

9.2 二进制文件的存取.....................................................................................................................81

第二篇 习题篇......................................................................................................................................82

3

第1章 C语言程序的基本形式 

1.1C语言程序的基本结构

C语言程序都是由一个或多个函数(Function)构成。一个C程序至少必须存在一个函数“main()”。它是程序运行开始时调用的一个函数。它表明该程序完成动作轮廓。C语言程序的基本形式如下: 

main() 

{ 

变量说明语句; 

执行语句; 

} 

main()为主函数,只能有一个。执行语句中,可有其他函数,但不能用main为函数名。许多常用的函数做成标准函数与C编译器一起提供给用户,这就是标准库函数。  例1.1:在屏幕上显示信息“This is the first C program.”。 

#include <stdio.h>

void main() 

{ 

printf(“This is the first C program.\n”); 

} 

运行结果:

This is the first C program.

其中:

#include <stdio.h>是编译预处理命令,因为后面调用的printf()函数是C语言提供的标准输出函数,在系统文件stdio.h中声明。

void main()定义了一个名称为main()的函数,关键字void表示函数无返回值。 一对大括号把构成函数的语句括起来,称为函数体。

语句printf(“This is the first C program.\n”)是一个函数调用,它的作用是将双引号中的内容原样输出;“\n”是换行符,即在输出“This is the first C program.”后换行。

分号表示该语句的结束。

例1.2:求两个数之和

/*This program is to get the sum of two integer */ 

main() 

{ 

int a,b,sum; /*说明a,b和sum为整型变量*/

a=123; 

b=456; 

sum=a+b; 

printf(“The sum of %d and %d is %d\n”,a,b,sum); 

} 

此例中,只有一个函数——主函数main()。第一个语句是说明a,b和sum这些变量的语句,说明它们都是整数(int)型的变量。所有语句都放在左右花括号之内,各语句之间以分号“;”隔开。

/* …… */是程序的注释,用来说明程序的功能。

总结:从上述两个例子,可以看出C语言程序的基本结构如下:

4

(1)C语言由函数组成,函数是程序的基本单位。main是一个特殊的函数名,一个程序总是从main()函数开始执行。

(2)函数由函数首部和函数体两部分组成。

函数首部用于定义函数的名称、函数的返回值类型和各种参数名称以及参数类型(也可以没有参数及数据类型)。例如:void main()即是函数的首部。

函数体一般包括数据的定义部分和执行部分,它们都是C语句。

(3)每条语句都用分号“;”作为结束符,分号是C语言必不可少的组成部分。

(4)在C语言中,一行可以写多条语句,一条语句也可以写成多行。其结果和输出格式不变。

(5)可以对C语言中的任何部分做注释。因为一个好的、有使用价值的程序应当加上必要的注释,以改善程序的可读性和可维护性。注释可以占一行的一部分,也可以单独占一行,还可以占若干行。

1.2字符集与标识符 

1.字符集

字符是组成语言的最基本的元素。C语言的字符集由字母、数字、空白符、标点和特殊字符组成。在字符常量,字符串常量和注释中还可以使用汉字或其他可显示的图形符号。

(1)英文字母:大写字母:A~Z、小写字母:a~z 。

(2)阿拉伯数字:0~9 。

(3)空白符:空格符、制表符、换行符等统称空白符。

空白符只在字符常量和字符串常量中起作用。其他地方出现只起到间隔作用,编译程序对它们忽略,主要用于提高程序的清晰性和可读性。

(4)标点和特殊字符:

! # % ^ & * + = - ~ < >

/ \ ‘ “ ; . , () [] {}

2.标识符

C语言中所使用的每个函数和变量名都应有唯一的名称,这样才能被识别和使用。通常,这种函数和变量名称用一串字符表达,称为标识符。

C语言使用的标识符有严格限制: 

(1)必须以字母或下划线开头, ; 

(2)必须由字母,数字或下划线组成; 

(3)大小写字母是有区别的; 

(4)不允许用一些保留字(或叫关键字)。

(5)命名应尽量有相应的意义,做到“见名之义”。

例: 正确的函数或变量名: _abc, veb7, lev_5

错误的函数或变量名称的实例:3H, sUM$,char

3.分隔符

在C语言中采用的分隔符有逗号和空格两种。逗号主要用在类型说明符和函数参数表中分隔各个变量。空格多用于语句中分隔各个单词。

1.3保留字(即关键字)

 C语言规定的具有特殊意义的字符串,通常称为保留字。

在C语言中保留字或关键字并不太多,原先规定有28个,新标准规定改为32个,如下所示 

auto default extern long static void break do for

register struct volatile case double float return

5

switch while char enum goto sizeof typedef continue

else if signed union int short unsigned 

1.4 基本数据类型

1.4.1C语言的数据类型

数据类型是C语言中允许使用的数据的种类。不同数据类型决定了该类型数据的取值范围及精度等属性。

C语言可以使用多种数据类型 

1) 基本类型 

·整数类型 int

·实数类型(浮点类型):

单精度浮点型 float

双精度浮点型 double

·字符类型 char

·枚举类型 enum

2) 构造类型 

·数组类型  []

·结构类型 struct

·联合类型 union

3) 指针类型 &, *

4) 空类型 void

1.4.2常量与变量

对于基本类型数据,按其取值是否可改变分为常量和变量两种。在程序执行过程中,其值不发生改变的量称为为常量,取值可以变化的量称为变量。

常量可以不经说明而直接引用,而变量必须先说明后使用。

1.变量

变量(Variable)是命名的内存空间,用于存储程序中的数据。它是在程序运行过程中其值可以发生变化的量。一旦变量存入了数据,该数据就可以被反复读取而不变,直至存入了新的数据为止。因此它通常用于保存程序运行过程中的输入数据、计算获得的中间结果和最终结果。

在内存中,变量是一个值的存放处,所以它有地址。程序为了区分不同的变量,通常用标识符为变量命名,变量的命名规则和前面章节中谈到的标识符的命名规则相同,即以字母或下划线开头,后跟字母、数字和下划线,不能用C语言中的关键字作为变量名,其中的英文字母一般为小写。例如:num,total2,_t等。最好能够“见名知意”,主要是为了使读程序者能够易于使用和理解。从变量中取值,实际上是通过变量名找到相应的内存地址,从该存储单元中读取数据。

当程序运行时,每个变量都要占用连续的若干字节,所占用的字节数由不同版本的编译系统分配给变量的数据类型确定。其中第1个字节的地址称为变量的地址。C语言规定,程序中变量的地址是用“&变量名”来表示的。例如:&num。

在C语言中,变量有三个重要属性:数据类型、名称和值。使用变量一般要有3个步 6

骤:声明、赋值(初始化)和使用。

声明变量(Declare Variable):通知编译器有这个变量,并要求编译器按给定标识符类型分配和命名内存空间。

变量定义的一般形式为:

类型说明符 变量名标识符,变量名标识符,...;

例如:

int a,b,c; /* a,b,c为整型变量 */

long x,y; /* x,y为长整型变量 */

unsigned p,q; /* p,q为无符号整型变量 */

在书写变量定义时,应注意以下几点:

(1)允许在一个类型说明符后,定义多个相同类型的变量。各变量名之间用英文的逗号间隔,最后一个变量名后需要使用英文的分号(“;”)结尾。

(2)最后一个变量名之后必须以“;”号结尾。

(3)变量定义必须放在变量使用之前,一般放在函数体(后面会有关章节对此内容展开)的开头部分。

(4)在同一段程序中,变量不允许被重复定义。

(5)类型说明符与变量名之间至少用一个空格间隔。

(6)字符串只能是常量,C语言中没有字符串变量。

2.常量与符号常量

在C语言中,对某些有特定含义的、经常使用的常量可以使用符号常量来代替。符号常量在使用之前必须先定义,定义的一般形式为:

#define 符号常量名 字符串

其中:

(1)符号常量名遵循变量命名的规则,也称为宏名;字符串是一串字符,简称为宏体;以#define定义的行也叫宏行,通常情况下程序中所有的宏行都放在程序文件的开头部分。

(2)功能是把该标识符定义为其后的常量值。一经定义,以后在程序中所有出现该标识符的地方均代之以该常量值。

(3)习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。 例:符号常量的使用。

#define PRICE 30

main()

{

int num,total; /*定义变量num,total表示某一农产品的数量和总额 */

num=100;

total=num* PRICE;

printf(“total=%d”,total);

}

程序剖析:

程序功能:用宏定义实现求某一商品的总价格。

符号常量与变量不同,它的值在其作用域内不能改变,也不能再被赋值。

使用符号常量的优点:

(1)书写简单不易出错。减少程序代码的输入错误和输入量,特别当这个变量在程序中多次重复出现,且数字又很长时,使用符号常量的好处就显得尤为突出。

(2)增加了程序的可读性和移植性。如看上面的程序时,见到PRICE就可知道它代表价格,定义符号常量名时应该尽量使用“见名知意”的常量名。

7

(3)增加了程序的易修改性。如程序多次用到圆周率,那么要改变精度时,就需要一一修改用到3.1415926的语句,如用符号常量PI表示进行宏代换,则只需改动#define PI 3.1415926一处,程序中的所有PI都会自动全部代换。此时用符号常量就比较方便,可以做到“一改全改”。

1.4.3整型数据

整型数据包括整型常量、整型变量。

1. 整数常量 

整型常量就是整数。用来表示正整数、负整数和0。C语言中,整数由3种形式。

(1)十进制整数(一般表示方法):主要由0~9的一个或者多个十进制数位组成,首位不能为0。如:123,-210,32767等。

(2)八进制整数:必须以0(注:不是字母o)开头,由0~7的一个或者多个八进制数位组成。如:012,029等,分别代表十进制整数的10和25。

(3)十六进制数:以0X(或0x)(注:不是字母o)作为起始位,由0~9,a~f(A~F)组成的一个或多个十六进制数位。如:0x13,0xaf等,分别表示十进制数的19和30。

例:16进制转为10进制:0x2C5即(2×16+12)×16+5=709

数据的八进制和十六进制表示,其实是数据在存储器中二进制存放形式的短写形式,由于书写起来太长,C语言中不用二进制形式表示数据。

整型常量也分short、int和long类型以及它们的无符号unsigned形式。它们是通过一些字符标识的,可以用字符U(u),L(l)作为整型常量的后缀,来表示该变量属于何种类型。如:123U(123u)表示无符号(unsigned)类型;123L(123l)表示具有长整型(long)类型。 

整数类型又可细分成不同长短的类型,应加上类型修饰符构成,即: 

short int 可简化为short 

long int 可简化为long 

unsigned int 可简化为unsigned 

2.浮点类型(即float型)

 

实数类型也叫浮点类型。可包含有整数部分和小数部分,例如: 

0.012等同于.012 

5.0等同于5. 

和日常习惯一样,小数点(.)的左边为整数部分,右边为小数部分。 

科学计数法中,则用“尾数+e+指数”表示浮点数值,e即exponent(指数)例如: 

6.3e5等同于630000.0 

其中,6.3就是尾数,含有整数部分(integer part)为6和小数部分(fractional part)为3,而5就是指数 (exponent) 部分。尾数和指数也都有可能为负值,例如: 

-1.23e4 

12.34567e-8 

-78e-12

 例:求摄氏温度100C对应的华氏温度,计算公式:f=09c+32,式中:c表示摄氏温度,5

f表示华氏温度。

#include<stdio.h>

void main()

{

float c,f;

c=100;

scanf(“%f”,&f);

f=9.0/5.0*c+32;

printf(“摄氏温度为:%f 华氏温度:% f\n”,c,f);

8

}

3.字符类型(即character—char型) 

字符类型的数据代表一个字符,由一对单引号将字符括起来,表示的是该字符在ASCII码表中的代码值,例如: 

′a′ 即97 

′A′ 即65 

它们占1 byte。 

ASCII码表中的某些控制字符不可显示,则通过加反斜线“\”的转义字符表示,例如:  ′\0′表示NULL(空) 即0 

′\t′表示tab(制表) 即9 

′\n′表示new line(新行或换行) 即10 

′\r′表示return(回车) 即13 

′\\′表示反斜线\ 即92

 

例1.2:ASCII码值。

main ( )

{ char c1, c2; /*定义字符类型*/

c1=97; c2=98; /*赋整型值*/

printf("%c # %c\n", c1,c2);/*以字符类型输出*/

}

例1.3: 小写转大写

main ( )

{

char c1, c2; /*定义字符类型*/

c1='a'; c2='b';

c1=c1-32; c2=c2-32; /*整型数值运算*/

printf("%c %c", c1, c2);

}

 

1.5算术表达式

1. C语言中算术运算符 

算术表示达式由变量、常量及算术运算符构成。在C语言中算术运算符有:  +、 - 、 *、 /、 % 、-- 、++ 

+,-,*,和/为四则运算符,和日常概念没有区别,其中*和/优先于+和-。  %为取模(Modulus)运算符,是针对整数运算,即取整数除法之后,所得到的余数,例如: 

10%3=1 即10对3取模,结果为1。 

13%8=5 即13对8取模,结果为5。 

--为自减1,++为自增1。 

n++或++n都变量n自增1,最终结果与n=n+1等效。但处理过程却有所区别。++n,表示n先自增1,然后进到具体的式子中运算;n++,则n本身先进入式中运算,最后n再增1。 

例如:已知n=6,则 

m=++n;结果为:m=7,n=7 

m=n++;结果为:m=6,n=7 

n--与--n同样类似。 

9

2.数据类型与运算结果的关系。 

(1)同类型数据运算结果仍保持原数据类型 

整型数的除法得到的结果仍是整型数,小数部分将被去掉,例如: 

5/2=2 

而不是2.5。浮点数的除法得到的仍是浮点数,例如: 

5.0/2.0=2.5 

(2)不同数据类型混合运算,精度低的类型往精度高的类型转换后,再做运算。这样,可保证运算结果不损失精度。例如: 

5.0/2=2.5 

1.6 C语言的基本语句

1. C语言的特点 

(1) 所有C语句都以“;”分号结尾。一条语句可以不止一行,不必加续行符,只根据“;”来确定语句结束。两条或多条语句也可写在同一行,用“;”分开。 

(2) 语句可从任一列位置开始,每行开头,有多少空格都可以,但为了可读性好,通常习惯还是按一定规律缩进。 

2. 变量说明语句 

变量说明语句的主要作用就是定义变量类型,其格式是: 

类型说明符 变量1[,变量2,…]; 

例如: 

int number; 

char a,b,c; 

float t1; 

3. 赋值语句 

赋值语句是将常量或算术表达式的运算结果赋给变量,其格式是: 

变量名=常量或算术表达式; 

例如: 

int number; 

number=10; 

例1.4: 赋值操作

main() 

{ 

int n1,n2,n3; 

int total; 

n1=1; 

n2=2; 

n3=3; 

total=n1+n2+n3; /* 赋值操作*/

}

4.基本输入输出语句 

printf()和scanf()是C语言的基本输入输出函数,都放在标准函数库中,为了使用它们,应在程序开头加上: 

10

#include <stdio.h> 

基本输入输出语句就是直接调用这两个基本输入输出函数。 

(1)输出语句 

一般格式是: 

printf (“控制串”[,表达式1,…,表达式n]); 

控制串(或格式串)是用双引号括起来的输出格式控制说明。控制串中每一个变量都应当与后面相应的某个表达式对应。

控制串分两部分,即:要显示的字符和格式串。格式串以“%”开头,后跟格式码。格式串与参数一一对应。含有不同格式码的格式串表示显示不同的内容,如下所示:  %c 显示字符 

%s 显示字符串 

%d 以十进制格式显示整数 

%o 以八进制格式显示整数 

%x 以十六进制格式显示整数 

%u 显示无符号整数 

%f 显示浮点数 

%e 以科学计数法显示数字 

格式码前可加修改量以便更好地控制显示格式,主要有: 

·字符宽度控制,例如: 

%4d 显示十进制整数,至少给4个数字位置 

%10s 显示字符串,至少给10个字符位置 

·精度控制,例如: 

%10.4f 显示浮点数,共占10位,小数点后取4位 

%5.7s 显示字符串,最少占5位,最多占7位 

·L或h 

%Ld 显示十进制长整数 

%hd 显示十进制短整数 

%Lf 显示双精度浮点数 

·显示位置默认为右对齐,若加负号(即“-”),则为左对齐,例如: 

%d 右对齐显示十进制整数 

%—d 左对齐显示十进制整数  

例如: 

prinft(″Welcome!″); 

屏幕上显示结果: 

Welcome! 

例如: 

prinft(″Welcome\n″); 

与前例不同是后面加了一个换行符。 

例如: 

int number=10; 

printf(″The number is %d\n″,number); 

应显示: 

The number is 10 

11

例如: 

float valuel,value2,value3; 

value1=2.3; 

value2=4.5; 

value3=6.7; 

printf(″The average of %of and %f and %f is %f\n″,value1,value2,value3,(value1+value2+value3)/3.0); 

应显示: 

The average of 2.3 and 4.5 and 6.7 is 4.5 

%d和%f表示在相应的位置显示的数据类型,且一一对应。%d表示要显示整型数,%f表示要显示浮点型数。 

(2)输入语句 

一般格式是: 

scanf(控制串,地址表达式1[,地址表达式2,……,地址表达式n]);  控制串(或叫格式串)与前述printf()中的控制串类似,也包含有以“%”开头加格式码组成的格式串。控制串是用双引号括起来的输入格式控制说明。地址表达式所列出的应当是变量的地址,而不是变量名。每个地址表达式的值,对应于前面控制串中某一格式变量的地址。如: int number; 

scanf(″%d″,&number); 

其中,%d表示应以整型格式输入,&number表示指向number的地址。

注意:

控制串中的非空白符,例如:

  scanf(″%d,%d″,&i,&j) 

上式中“%d”之间有逗号,输入时也应加逗号。还可用修改控制域宽,例如:  %20s 就只取前20个字符 

%s 取全串  

例如 :

float average; 

scanf(″%f″,&average); 

其中,%f表示应以浮点型格式输入,&average表示指向average的地址。 

例:输入一个华氏温度,要求输出摄氏温度,公式为:C=(5/9)*(F-32),输出要有文字说明,取2位小数。

#include<stdio.h>

main()

{

double tem_c, tem_f;

puts("=====华氏温度至摄氏温度转换程序=====");

printf("请输入华氏温度:");

scanf("%lf", &tem_f);

tem_c = (tem_f - 32) * 5.0 / 9.0;

printf("%f对应的摄氏温度为%.2f", tem_f, tem_c);

return 0;

}

12

1.7运行C语言的步骤和开发环境

1.7.1运行C语言的步骤

对于C语言代码的编写和调试,一般包括一下几个步骤:

1.编辑

编辑的过程指用程序设计语言写出源代码的过程。一些常用的编辑软件,比如:记事本等都可以完成这一功能。

2.编译

对于程序进行编译是将源程序翻译成机器能够识别的目标程序的过程。这一个过程必须借助一些专门的编译程序(编译器)完成。

3.连接

连接过程是将不同的模块连接成一个完整模块的过程。比如一个程序包含多个文件,在分别对每个源程序文件进行编译并得到多个目标程序后,连接就是要把这些目标程序以及系统提供的资源(通常是一些库函数)组合起来形成的一个整体。这一过程必须通过连接程序来完成,从而形成一个完整的可执行程序。

4.执行

一个程序经过了编辑、编译、连接过程,就可得到可执行程序,于是可以执行了。

1.7.2集成开发环境

将上述步骤集成起来的方法,就形成了集成开发环境(Integrated Development Environment,IDE)。所谓的集成开发环境是指集源程序编辑、编译、连接、运行和调试于一体、用菜单驱动的集成化的软件开发工具。

常用的C语言开发环境有Turbo C2.0、Turbo C3.0以及Visual C++6.0等。

13

第2章 流程控制 

2.1 控制表达式

程序的流程走向是由条件表达式的值控制决定的。表达式总是有值的。

1.逻辑表达式 

逻辑表达式由变量、常量和逻辑关系运算符构成,用以表示变量的逻辑关系。

(1) 逻辑与 (AND) &&

(2) 逻辑或 (OR)  ||

(3) 逻辑非 (NOT) !

逻辑表达式只有两种可能的取值,真假必取其一。假者取值为0;真者取值为1。

2.关系运算符 

变量与变量或常量之间比较数值上大小的关系用关系运算符来表示。关系运算符共有六种,即: 

== 等于 

  != 不等于 

< 小于 

<= 小于等于 

> 大于 

>=   大于等于

关系表达式也只有两种可能的取值,例如:

′a′==′b′

此式取值0,值为假(false)。又如,已知变量x=′c′,则表表达式

x>′b′

值为真(true),取值1。 

3. 运算操作符的结合性和优先级 

运算操作符的结合性和优先级规定了表达式运算操作的处理顺序。优先级高的操作应优先执行。例如,处理表达式

a+b*c时,*优先于+,相当于a+(b*c)。 

有部分运算操作符的结合性是从右向左。最常见的赋值表达式中,赋值运算操作符的结合性就是自右向左。例如,

a=b+c是将b+c结果赋给a

又如:

a*=b+c相当于a=a*(b+c)。

单操作数的运算操作符++(自增1),--(自减1),-(负号)的结合性也都从右向左。例如, -i++相当于-(i++),而不是(-i)++ 

关于运算符的优先级低于算术运算符,例如: 

a<b+c等同于a<(b+c) 

14

2.2 for循环语句

 

1. for循环语句的一般格式为: 

for(初始化表达式;循环条件;循环表达式) 

循环体语句块

例2.1:在屏幕上,显示1~100 

#include <stdio.h> 

main() 

{ 

int n; 

for (n=1;n<=100;n++) 

printf(″%d\n″,n);  /*循环体语句*/

} 

for语句中“;”隔开的各部分: 

(1)初始化表达式,用于循环开始前,为循环变量设置初始值。 

(2)循环条件是一个逻辑表达式,若该式取值为真即条件满足,则继续执行循环;否则,执行循环体后的语句。 

(3)循环表达式定义了每次循环时,循环变量的变化情况。 

(4)循环体可以是一条语句或一组语句,若是一组语句,需要用“{”和“}”括起来。 

2.for语句执行过程 

(1)计算初始表达式。 

(2)判断循环条件,若满足,则执行;否则,退出循环。 

(3)执行循环。 

(4)返回第(2)步。 

例2.2:计算并显示十的阶乘(10!)  

#include <stdio.h> 

main() 

{ 

int n,result; 

result=1; 

for(n=1;n<=10;n++) 

{ 

result*=n; 

}  /* 循环体语句 */

printf(″%d!=%d\n″,n-1,result);  } 

例2.3:打印可印刷字符的ASCII(从32到126)代码字符对照表。

#include <stdio.h>

main ()

{

int i;

15

for (i=32;i<127; i++) /* 从ASCII码为32的开始 */

{

printf("%4d %c",i,i); /* 打印ASCII码及其对应的字符 */

if ((i+4)%5 == 0) printf ("\n"); /* 每行打印5个字符后换行 */ }

}

3.嵌套的for语句 

在for的循环体中还包含for语句,这就是嵌套的for语句。 

例2.4:计算五个给定数的阶乘 

#include <stdio.h> 

main() 

{ 

int i,j,number,result; 

for(i=1;i<=5;i++) 

{ 

scanf(″%d″,&number); 

result=1; 

for(j=1;j<=number;j++) 

result *=j;  /*内循环体语句*/ printf(″%d!=%d\n″,number,result); 

}  /*外循环体语句*/

} 

例:编写一个程序,输出从1到n(n由用户输入)的所有素数。

#include "stdio.h"

#include <math.h>

void main()

{

int n,m,flag,i,j,num=1;

printf("n:");

scanf("%d",&n);

printf("从2到%d的所有素数如下:\n1",n);

for(i=2;i<=n;i++)

{

flag=1;

m=(int)sqrt(i);

for(j=2;j<=m;j++)

if(i%j == 0)

{

flag=0;

break;

}

if(flag ==1)

16

{

printf("%4d",i);

if(num++%10 ==0)

printf("\n");

}

}

}

例:分析以下程序的执行结果。

#include <stdio.h>

void main()

{

int i,j;

float s;

for(i=7;i>4;i--)

{

s=0.0;

for(j=i;j>3;j--)

s=s+i*j;

}

printf("%f\n",s);

}

45.000000

2.3 while语句 

这是另一种形式的循环语句,只是按某一条件循环,可以不知道循环的次数,直到不满足该条件时,才停止循环。 

1.while语句的一般格式为: 

while(循环条件) 

循环体语句块

例2.5:显示1~100 

#include <stdio.h> 

main() 

{ 

int n; 

n=1; 

while(n<=100){ 

printf(″n=%d″,n); 

n++; 

} 

} 

17

2.while语句的执行过程 

(1)判断循环条件是否满足,不满足就退出循环体。 

(2)执行循环体。 

(3)返回第(1)步。 

如果循环条件根本不能成立,则永不执行循环体;反之,若循环条件总是成立,则成为永久循环(死循环)。

 

例2.6:倒排输出数字 

#include <stdio.h> 

main() 

{ 

int number,rdigit; 

scanf(″%d″,&number); 

while(number!=0)

{ 

rdigit=number%10;  /*取出低位数*/

printf(″%d″,rdigit); 

number/=10;  /*把上次循环的number缩小10倍回到循环头*/ } 

printf(″\n″);  /*循环跳出后换行*/

} 

输入的数若为12345则输出54321。

注意:number为整数类型,其最大值应不超过32767,否则,会出现不正确的结果。  例如:利用while语句和空语句跳过所有的空白字符 

while(c=getchar())==′′) 

; /*空语句*/ 

例如:永久循环 

while(1)

{ 

 … 

} 

2.4 Do-while语句 

Do- while语句为当型循环结构,执行时先执行循环体语句再判断循环条件。

1.do- while语句的一般格式为: 

do 

循环体语句块 

while(循环条件);

 

例2.7:倒排数字输出 

#include<stdio.h> 

main() 

{ 

18

int number,rdigit;

printf(″Please input a number:″); 

scanf(″%d″,&number); 

do

{  /*先执行*/

rdigit=number%10;

printf(″%d″,rdigit); 

number/=10;  /* 等效于number= number/10

}

while(number!=0);  /*后判断*/

} 

2. 执行过程 

(1)执行一次循环体。 

(2)判断是否满足循环条件,若满足,则循环,转到(1)继续执行;否则,执行随后的语句。 例:分析以下程序的执行结果。

#include <stdio.h>

void main()

{

static char a[]="sTring",b[20];

int i=0;

do

{

b[i]=(a[i]>='a' && a[i]<='z'?a[i]-'a'+'A':a[i]);

}while (a[i++] !='\0');

printf("%s\n",b);

}

STRING

例:求两个正整数m和n的最大公约数。

求两个正整数的最大公约数的算法通常使用“辗转相除法”。设有两个正整数m、n,求它们的最大公约数的算法如下:

①输入两个正整数m和n,若m<n,则交换m和n(保证m大于n)。

②计算m/n的余数r(0≤r<n)。

③若r不等于0,则令m=n、n=r,转第②步继续执行; 否则,算法结束,n就是最大公约数。

main()

{

int m, n, r,b,a;

printf("请输入m和n的值:");

scanf("%d%d",&m,&n);

a=m;

b=n;

if(m<n )

{

19

r = m;

m = n;

n = r;

}

do{

r=m%n;

m=n;

n=r;

}while(r!=0);

printf("%d和%d的最大公约数是%d",a,b,m);

}

2.5 if语句 

 

1.简单的if语句 

简单的if语句格式为: 

if(条件表达式) 

语句块 

只要条件表达式满足逻辑关系,即表达式的值为真,就执行语句块。 

例2.8:比较两个数 

/* Compare two numbers */ 

#include <stdio.h> 

main() 

{ 

int m,n; 

printf(″Please input two numbers\n″); 

scanf(″%d %d″,&m,&n); 

if(m>n) 

printf(″The first number is bigger\n″);

} 

2.复合句的if语句 

复合的if语句格式为: 

if(逻辑表达式) 

语句块1 

else 

语句块2 

若满足逻辑关系,即逻辑表达式的值为真,则执行语句块1;否则,执行语句块2。 

例2.9:比较两个数的改进

#include <stdio.h> 

main() 

{ 

20

int m,n; 

printf(″Please input two numbers\n″); 

scanf(″%d %d″,&m,&n); 

if(m>n) 

printf(″The first number is bigger\n″); 

else  /* 相当于m<n */

printf(″The second number is bigger\n″);

}

 

例2.10:检查闰年 

/* This program checks if a year is a leap year */ 

#include <stdio.h> 

main() 

{ 

int x; 

printf(″Please input a number\n″); 

scanf(″%d″,&x); 

if(x%4==0&&x%100!=0||x%400==0) 

printf(″%d is a leap year\n″,x); 

else  /* 不满足上述条件 */

printf(″%d is not a leap year\n″,x); 

} 

可见,这种复合的if语句是等同于两个简单的if语句,即: 

if(逻辑表达式) 

语句块1 

if(逻辑表达式) 

语句块2 

3.嵌套的if语句 

在if语句的语句块中还可包含if语句,这就形成嵌套的if语句。其格式为:  if(逻辑表达式1) 

if(逻辑表达式2) 

语句块1 

else 

语句块2 

else 

语句块3 

-1 (x<0)

例2.11: 有一个函数 y= 0 (x=0)

1 (x>0)

main ( )

{

int x,y;

scanf ("%d",&x);

21

if (x<0) y=-1;

else if (x==0) y=0; /* else 包括(x>0)和(x=0) */

else y=1; /* else 只包括(x>0) */

printf ("x=%d,y=%d\n",x,y);

}

此例if与else是成对的,比较容易理解。实际上也可能在嵌套中只有if,而没有else,这样就容易造成错误。因为else总是与前面最相近的一个不包含else的if语句对应匹配,为避免发生这种错误,应将嵌套中的if语句用“{”和“}”括起来,即: 

if(逻辑表达式1){ 

if(逻辑表达式2) 

语句块1 

} 

else 

语句块2 

4. else-if语句 

实际上,这也是一种嵌套,在else下再嵌套if_else,形成如下格式: 

if(逻辑表达式1) 

语句块1 

else if(逻辑表达式2) 

语句块2 

else if(逻辑表达式3) 

语句块3 

… 

else 

语句块n 

例2.12:用else-if语句,写一个程序,输入一个数x,打印出符号函数sign(x)的值。

-1 x<0    

符号函数为 sign(x)= 0 x=0 

1 x>0

#include <stdio.h> 

main() 

{ 

int number,sign; 

printf(″Please type in a number x=″); 

scanf(″%d″,&number); 

if(number<0) 

sign=-1; 

else if(number==0)  /*else包括=0和>0两者*/

sign=0; 

else 

sign=1; 

22

printf(″sign(x)=%d\n″,sign); 

} 

2.6 条件表达式运算符 

条件表达式运算符的一般格式为:

条件 ? 表达式 1:表达式2

  这是一个三元运算符,它控制三个操作数,第一个操作数是条件,条件通常是一个逻辑关系表达式。该条件若为真,取表达式1的值,就是整个条件表达式的运算结果;否则,取表达式2的值作为整个条件表达式的运算结果。

 例2.13:求最大值.

#include <stdio.h> 

main() 

{ 

int a,b,c; 

scanf(″%d %d″,&a,&b); 

c=(a>b)?a:b;  /* 取a和b中大者赋给c */

printf(″The maxum is %d″,c); 

} 

2.7 break语句

 

break语句用在循环语句(for,while,do)中以及switch语句中。当执行一个循环体时,在某一特定状态出现break语句时,立即退出循环体,且只退出本级循环。 

例2.14: 九九乘法表

#include <stdio.h> 

main() 

{ 

int i,j; 

for(i=1;i<=9;i++){ 

for(j=1;j<=9;j++){ 

if(j>i)break;  /* 被乘数大于乘数时跳出内循环 */ printf(″%d ″,i*j); 

} 

printf(″\n″); 

} 

} 

其结果将打出三角形的乘法表,即 

1 

2 4 

3 6 9 

23

4 8 12 16 

 … 

9 18 27 36 45 54 63 72 81

例:分析以下程序的执行结果。

#include <stdio.h>

void main()

{

int i,j,k,s;

i=0,j=10,k=2,s=0;

for(;;)

{

i+=k;

if(i>j)

{

printf("s=%d",s);

break;

}

s=s+i;

}

}

结果:s=30

2.8 continue语句 

continue语句执行后,在本次循环中其他语句均不执行,转而继续执行循环体的下一次循环。在while﹑do循环中意味着立即转入条件测试,在for循环中意味着立即转到执行i++,而进入下一次重复。

 

例2.15:只做正数的累加而跳过负数 

#include <stdio.h> 

main() 

{ 

int a,i,n,sum; 

scanf(″%d″,&n); 

sum=0; 

for(i=0;i<n;i++){ 

scanf(″%d″,&a); 

if(a<0) 

continue; 

sum+=a; 

} 

printf(″The sum=%d\n″,sum); 

}

例:分析以下程序的执行结果。

24

#include <stdio.h>

void main()

{

int i;

for(i=1;i<6;i++)

{

if(i%2)

{

printf("#");

continue;

}

printf("*");

}

}

*#*#*

 注:continue语句与break语句的区别:continue语句只结束本轮循环,而不终止整个循环的执行;而break语句则结束整个循环。

2.9 switch语句

 

switch语句的一般格式为: 

switch(表达式) 

{ 

case常量1: 

语句块1 

break; 

case常量2: 

语句块2 

break; 

 … 

case常量n: 

语句块n 

break; 

default: 

语句块n+1 

} 

它是根据表达式为不同的值而执行不同的语句块,可以看成是特殊的else_if语句。即:  if(表达式==常量1) 

语句块1 

else if(表达式==常量2) 

语句块2 

 … 

if(表达式==常量n) 

25

语句块n 

else 

语句块n+1

但用switch语句则更方便,实际上也用得更多一些。 

例2.16 编写一个程序,输出给定的某年某月的天数。

int main(int argc, char* argv[])

{

int year,mon,day,leap=0;

printf("年.月");

scanf("%4d.%2d",&year,&mon);

switch(mon)

{

case 1:case 3:case 5:case 7:case 10:

case 12:

day=31;break;

case 4:case 6:case 8:case 9:

case 11:

day=30;break;

case 2:

if(year%400 == 0)

leap=1;

else if(year%4 == 0 && year%100 !=0)

leap=1;

if(leap)

day=29;

else

day=28;

}

printf("%d年%d月的天数为%d",year,mon,day);

return 0;

}

例2.17编写一个程序,将输入的十进制整数转换成十六进制整数后输出。in()

{

int n,b[20],i=0,j;

printf("输入一个正整数:");

scanf("%d",&n);

while(n>0)

{

b[i++]=n%16;

n=n/16;

}

printf("对应的十六进制数是:");

for(j=i-1;j>=0;j--)

26

{

if(b[j]<=9)

printf("%d",b[j]); else

{

switch(b[j])

{

case 10:printf("A"); break;

case 11:printf("B"); break;

case 12:printf("C"); break;

case 13:printf("D"); break;

case 14:printf("E"); break;

case 15:printf("F"); break;

}

}

}

printf("\n");

}

例:分析以下程序的执行结果 综合性较强

#include <stdio.h>

void main()

{

int k=0;

char c='A';

do {

switch(c++)

{

case 'A':

k++;

break;

case 'B':

k--;

break;

case 'C':

k+=2;

break;

case 'D':

k=k%2;

27

continue;

case 'E':

k=k*2;

break;

default:

k=k/3;

}

k++;

}while(c<'F');

printf("k=%d\n",k);

}

运行结果:k=3

给出一个不多于4位的整数。要求:

(1)求出它是几位数;

(2)分别打印出每一位数字;

(3)按逆序打印出各位数字(例如原数为:486,则应输出684)。#include <stdio.h>

void main()

{

int i=0,n,len,b[4];

printf("输入整数:");

scanf("%d",&n);

if(n<0 || n>=10000)

{

printf("输入负数或大于4位,无法求解");

return;

}

if(n<10)

len=1;

else if(n<100)

len=2;

else if(n<1000)

len=3;

else

len=4;

while(n>0)

{

b[i++]=n%10;

n=n/10;

}

printf("数字位数:%d",len);

printf("各位数字:");

for(i=len-1;i>=0;i--)

{

28

printf("%d ",b[i]);

}

printf("逆序数字:");

for(i=0;i<len;i++)

printf("%d",b[i]);

}

综合例:完全数的定义可以描述如下:如果正数m等于它全部因子(不包含m自己)之和,则m叫做完全数。例如:6=1+2+3,28=1+2+4+7+14;本例中,6和28均是完全数。试求出1000以内的全部完全数并输出。

#include <stdio.h>

void func(int n)

{

int j,s=0;

for(j=1;j<n;j++)

if(n%j == 0)

s=s+j;

if(s == n)

printf("%d\n",n);

}

void main()

{

int i;

printf("1000以内的完全数:\n");

for(i=1;i<=1000;i++)

func(i);

}

29

第3章 数组与字符串 

3.1 一维数组 

1.数组及其特点 

实际处理的数据,往往不仅是一个,而是量大但性质相同的数据。例如,一个班的学生成绩等等。它们是相同性质的数据。如果仍一个个加以说明,麻烦易错。改用数组,这就方便多了。例如: 

int grade[n]; 

数组是相关变量的有序集合,其中所有的变量具有相同的数据类型。数组的特点: 

(1)各元素是同一种类型的变量。 

(2)各元素按顺序排列,其位置由下标来确定。 

(3)各元素可独立地作为一个基本变量被赋值和使用。 

2.一维数组说明和下标 

一维数组说明格式为: 

类型说明符 数组名[size]

 

其中,size为数组的大小,即组成该数组元素的个数。 

(2)数组的下标 

C语言中,数组的下标从0开始,到size-1为止。例如: 

int x[5] 

就有下标从0到4共5个元素,即:x[0],x[1],…,x[4]。  例3.1:产生斐波那契(Fibonacci)数列前16项 

/* program to generate the first 16 Fibonacci numbers */ 

main() 

{ 

int Fibonacci[16],i; 

Fibonacci[0]=0; 

Fibonacci[1]=1; 

for(i=2;i<16;i++) 

Fibonacci[i]=Fibonacci[i-2]+Fibonacci[i-1];  for(i=0;i<16;i++) 

printf(″%d,″,Fibonacci[i]); 

printf(″…\n″); 

} 

执行结果: 

0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,510,… 

30

3.2 多维数组 

多维数组说明格式为: 

类型说明符 数组名[size1][xize2]…[sizen]; 

其中,size1,size2,…,sizen为数组各维的大小。组成该组元素的总个数应为size1×size2×…×sizen。 

例如: 

int d[10][20]; 

说明这是一个10×20二维数组,即: 

d[0][0]d[0][1]…d[0][19] 

d[1][0]d[1][1]…d[1][19] 

… 

d[9][0]d[9][1]…d[9][19]

例3.2 将一个二维数组行和列元素互换,存入另一个二维数组里

/*L6-4*/

main ()

{

static int a[2][3]={{1,2,3},{4,5,6}};

static int b[3][2],i,j;

printf("array a : \n");

for (i=0;i<=1;i++) /*循环变量作为行坐标*/ {

for (j=0;j<=2;j++) /*循环变量作为列坐标*/

{

printf("%5d",a[i][j]);

b[j][i]=a[i][j]; /*行列互换*/

}

printf("\n");

}

printf("array b : \n");

for (i=0;i<=2;i++) /*循环变量作为行坐标*/

{

for (j=0;j<=1;j++) /*循环变量作为列坐标*/

printf("%5d",b[i][j]);

printf("\n");

}

}

3.3 数组元素初始化 

31

1.数组的初始化 

C语言允许在说明时对全局数组和静态数组初始化,即给各个元素赋以初始值,例如:

  main() 

{  

static int counters[5]={0,1,2,3,4}; 

static char letters[5]={′a′,′b′,′′c,′d′,′e′}; 

static int M[4][5]={{10,5,-3,-17,82},{9,0,0,8,-7}{32, 10,20,1,14},{0,0,8,7,6}}; 

static int x[5]={0,1,2}; /*后面两个默认为0*/ 

 … 

} 

当给出初始值的个数少于数组全部元素个数时,未列出的后若干个初始值默认为0。

2.变长数组的初始化 

有时,不直接给出数组大小,作为变长数组出现,也照样可以初始化。例如: 

static int a[]={1,2,3,4,5}; 

ststic float f[]={1.2,2.5,3.8,4.0}; 

static char word[]={′H′,′e′,′1′,′1′,′o′,′ ′,′\0′}; 

static char error[]={″read error\n″};

3.4 字符串 

字符串要用一维字符数组处理, 实质上就是以空字符结尾的字符数组。 

1.以空字符结尾的字符数组 

空字符就是NULL,即′\0′。一个字符串可看成结尾为′\0′的不定长的一维字符数

组。例如: 

″a″等同于′a′,′\0′ 

″″等同于′\0′ 

″ ″等同于′′,′\0′ 

″Hello!\n″等同于′H′,′e′,′1′,′1′,′o′,′!′,′\n′,′\0′2.

字符串常量 

字符串常量是由一对双引号包括的除双引号之外的任何字符集合,也可看成一维的字符

数组常量。若要包括双引号必须加反斜线“\”。 

3.字符串的初始化 

可采用类似于变长数组初始化处理。 

(1)变长数组初始化,例如: 

static int A[]={0,1,2,3,4}; 

(2)字符串初始化,例如: 

static char x[]={′H′,′e′,′l′,′l′,′0′,′!′,′\0′}; 

由于是字符数组,可以直接用字符数组常量即字符串常量给出初始值,编译器将自动加

入空字符′\0′。显然,下面这样将更简便些; 

static char x[]={″Hello ″}; 

4.字符串的显示 

可用%s的格式显示一个以′\0′结尾的字符串。系统显示字符串遇到空格符′\0′时,

32

就结束该字符串的显示。例如: 

static char x[]={″Hello!″}; 

printf(″%s\n″,x); 

此语句的执行结果,将显示: 

Hello  

5.字符串的输入 

可用%s的格式输入一个字符串。例如: 

char s1[20]; 

scanf(″%s″,s1); 

注意:·在s1前,不加“&”。 

·输入字符串时,遇到回车则结束。 

6.单字符的输入 

用scanf()输入字符或字符串时,遇到空格或回车结束。这不便于文字处理,一般用getchar()函数输入单字符。例如: 

#include <stdio.h> 

main 

{ 

char ch; 

ch=getchar(); 

} 

7.转义字符 

一些特殊字符,须用反斜线来转义,例如: 

\0 NUL \b 退格 

\n 回车换行 \r 回车不换行 

\t 横向制表 \v 纵向制表 

\′ 单引号 \″ 双引号 

\f 换页 \\ 反斜线  

例 3.3 输出一个钻石图形。

main ()

{static char diamond[ ][5]= /*第二维大小不能省略*/

{{' ',' ','*'},{' ','*',' ','*'},{'*',' ',' ',' ','*'},{' ','*','

','*'},{' ',' ','*'}};

int i,j;

for (i=0;i<5;i++) /* 逐行 */

{

for (j=0;j<5;j++) /*逐列*/

printf("%c",diamond[i][j]);

printf("\n");

}

}

运行结果:

*

* *

* *

33

* *

*

例 3.4 统计输入的一段文字有多少个单词。

/*“L6-8 */

#include "stdio.h"

main ()

{

char string[81];

int i,num=0,word=0;

char c;

gets(string); /* 输入一段文字到一维数组string */

for (i=0;(c=string[i])!='\0';i++)

if (c==' ') /* c为空格,没出现单词*/

word=0;

else if (word==0) /* c不为空格,但上次c为空格,出现单词 */ {

word=1;

num++; /*单词数累加*/

}

printf("There are %d words in the line\n", num);

}

例:分析以下程序的执行结果。

#include <stdio.h>

void main()

{

static char ss[]="12345";

int i=0,m,s=1;

if(ss[i] == '+' || ss[i] == '-')

s=(ss[i] == '+')?1:-1;

for(m=0;ss[i]!='\0'&& ss[i] >= '0' && ss[i]<='9';i++)

m=m*10+ss[i]-'0';

printf("%d\n",s*m);

}

把字符串ss表示的数转化为int型的数值和符号标识,并且输出。

3.5 字符串函数 

一般C语言编译器都提供了字符串函数,放在文件string.h中,要使用字符串函数必须加有包含文件string.h,即写成: 

#include <string.h>

3.6字符函数和字符转换及运算 

34

1. 字符函数 

一般C语言编译器的字符函数放在文件ctype.h中,要使用时必须加上包含文件ctype.h,即写成: 

#include <string.h> 

这类函数主要是用于字符类别判断及转换,例如: 

isalnum(ch)  若ch为字母或数字,则返回非零值;否则,返回零。 

islower(ch) 若是小写字母,则返回非零值;否则,返回零。 

tolower(ch)  将大写字母转换成小写字母 

例 3.5 利用二维数组,找出三个字符串的最大者(ASCII值)。

/*L6-9*/

main ()

{

char string[20];

char str[3][20]; /*每行一个字符串*/

int i;

for (i=0;i<3;i++)

gets (str[i]); /* 分别读入三个字符串*/

if (strcmp(str[0],str[1])>0) /* 字符串比较 */

strcpy(string,str[0]); /* 字符串str[0]复制到string */

else strcpy(string,str[1]);

if (strcmp(str[2],string)>0)

strcpy(string,str[2]);

printf("\nthe largest string is:\n%s\n",string);

}

35

第4章 函数与变量 

函数是C语言的基本构件。利用这些基本构件,可以组成结构良好的大型程序。一个C源文件中可包含一个或多个函数,每个函数完成一个特定的功能。模块化编程在C语言程序设计中就是函数。

4.1 C程序设计的一般形式 

1. C语言程序的一般格式 

全程变量说明 

main() 

{ 

局部变量说明 主程序 

执行语句 

} 

f1(形参表) 

{ 

局部变量说明 

执行语句 

} 

 … 函数 

fn(形参表) 

{ 

局部变量说明 

执行语句 

} 

例4.1:输出fa(n)函数值

#include <stdio.h>

int fa(int n) /* 函数定义fa,函数返回值的类型为int*/

{

if (n>0) return (1); /* 函数体,返回不同的值*/

else return (0);

}

main()

{ int a;

printf("请输入数据:\n");

scanf(“%d”,&a);

printf(“\n%d\n”, fa(a)); /* 函数调用,用实参a替代形参n */ }

36

例4.2:将输入的大写字母转换为小写 

/*converte a string in lowercaes */ 

#include <stdio.h> 

main() 

{ 

char uptolow(); /* 说明uptolow()函数已定义 */

char ch; 

do{ 

printf(″Please type in a letter\n″); 

scanf(″%c″,&ch); 

printf(″%c″,uptolow(ch)); /* 函数调用 */

}while(ch!=′′); /*遇到空格字符则结束*/  printf(″\n″); 

} 

char uptolow(k)  /* 函数定义 */

char k; 

{ 

if(k>=′A′&&k<=′Z′) 

k+=′a′-′A′; 

return(k); 

} 

4.2 函数

 

1.函数是C语言程序的基本构件 

函数在C语言程序中,是完成某个算法的一个程序段,可以看成是程序的基本构件。函数的作用主要有:

(1)分解大任务 

(2)利用现有成果 

(3)增加可读性 

 

例4.3:求任意两个整数的阶乘之和 

main() 

{ 

int number1,number2,result;

int factorial(); 

printf(″Please type number1 and number2″); 

scanf(″%d %d″,&number1,&number2); 

result=factorial(number1)+factorial(number2); 

printf(″number1!+ number2! =%d″,result); 

37

} 

int factorial(n)  /* 函数定义 */

int n; 

{ 

int result1=1; 

while(n!=0){ 

result1 * =n; 

n--; 

} 

return(result1); /*结果值result1返回 */  } 

2. 定义函数的一般格式 

类型说明符 函数名(形参表) 

形参说明 

{ 

局部变量说明 

函数体语句 

} 

注意: 

·函数也可能不需要传递参数,这时只保留括号,也无需对形参说明。 ·形参表内可含有多个变量,用逗号分隔开。 

例如: 

float sum(a,b) 

float a,b; 

{ 

return(a+b); 

} 

例4.4 函数power()实现xn,其中n是整数

#include <stdio.h>

double power(x,n) /* 函数定义 */

double x; /* 形式参数说明 */

int n; /* 形参说明 */

{ double p;

if (n>0)

for (p=1.0; n>0; n--)

p=p*x;

else p=1.0;

return (p); /* 返回函数值 */

}

main ()

{ double m;

38

m = power(12.0,2); /* 函数调用,参数类型,个数必须匹配 */ printf(“\n计算结果为: %e\n”,m);

printf(“\n计算结果为:%e\n”,power(12.0,3));

}

4.3函数返回值 

1.函数返回值的类型说明 

(1)函数返回值类型说明的一般格式为: 

类型说明符 函数名() 

(2) 函数默认类型为int,可省去类型说明符。 

(3) void f(b) 表示函数f(b)没有返回值。 

2.返回语句return 

有返回值的函数需要用返回语句来传送返回值。 

(1)返回语句的一般格式为: 

return; 

或 

return(返回变量名或表达式值); 

(2)返回语句的两种用途 

a) 表示程序结束,从函数返回调用点,不返回函数的值,可以不用return语句。 例如: 

printmessage() 

{ 

printf(″programming is fun \n″); 

return; 

} 

b) 从函数返回调用点,并返回函数的值。 

例如:计算阶乘 

factorial3(n) 

 

{ 

int n;

int result=1; 

if(n==0||n==1) 

return(result); 

while(n! =0){ 

result*=n――; 

} 

return(result); 

} 

在 return语句括号内的retult是被返回的变量, 带回函数值。

 

39

4.4 函数的调用 

1.函数的参数 

函数定义格式为: 

<类型说明符> 函数名(形参表)

{ 

<说明语句序列> 

<可执行语句序列>

函数的调用格式为: 

函数名(实参表) 

注意: 

(1) <类型说明符>指出该函数通过return返回值的类型,除了取常用的各种数据类型(如int、float、char等)外,还有一种特殊类型即void。void类型的函数无返回值。默认的数据类型值为int。

(2)函数形参表或实参表中,可有一个或多个参数,多个形参或实参表要用逗号隔开,也可能没有参数,但函数后仍须有(); 

(3)实参类型应与形参类型一致,项数相同,且类型与项数一一对应; 

(4) 函数返回值只能有一个,要想得到多个参数a1,a2,…,值, 则需定义全局变量a1,a2,…,或用地址指针操作。 

(5)对于void型的函数不能包含return语句,其他类型函数至少包含一个return语句。

(6) <说明语句序列>和<可执行语句序列>构成了函数体;

(7)C语言规定,函数不能嵌套定义,即函数体内又包含另外一个函数的定义。这保证了每一个函数是一个独立的、功能单一的程序单元。函数的定义次序不影响函数的调用顺序。 总之,一个C语言程序实质上是一系列相互独立的函数的定义,函数之间只存在调用和被调用的关系。

调用函数的执行过程:

(1)传递实参给被调用函数;

(2)将控制传给被调用函数,于是被调用函数开始执行;

(3)被调用函数保存调用函数的执行现场,其中包括断点等;

(4)执行被调用函数的函数体,每遇到其他函数是,就重复步骤(2)调用其他函数;

(5)被调用函数执行结束时(如遇到return语句,或遇到函数体的结束括号“}”时),恢复现场,控制返回调用函数(如主函数)的调用处。

被调函数说明:

在程序中调用一个函数时,需要说明该函数的数据类型等,这称为函数说明。函数说明在程序的数据说明部分。C语言规定,如果调用一个函数发生在该函数的定义之前,则在调用前必须对该函数进行说明。当调用系统提供的标准函数时,它们的函数说明已经包含在文件中,不必在程序中再次说明。

2.函数调用顺序及类型说明 

(1)调用前,已对该函数加以定义说明。 

例4.5:调用函数求两数之和。

40

float sum(float a,float b)  /* 先定义函数 */   { 

return(a+b); 

} 

main() 

{ 

float first,second; 

first=1.0; 

second=2.0; 

printf(″%f\n″,sum(first,second);  /* 后调用函数 */ } 

注意:简单的函数适于放在前头。因主函数中未对函数定义,放在后头易出错。 

(2)函数调用前,已对函数返回的值加以说明。 

在主函数中说明函数类型,对该函数的定义放在后面。 

例4.6:调用函数求两数之和。 

main() 

{ 

float first,second,sum(); /* 说明函数sum() */

first=1.0; 

second=2.0; 

printf(″%f\n″,sum(first,second); 

} 

float sum(float a,float b)  /* 后定义函数sum(a,b) */ { 

return(a+b); 

} 

例:将整数x转换成m进制(1<m<11)的数并输出。

#include <stdio.h>

void change(int x,int m)

{

static ch[]={'0','1','2','3','4','5','5','7','8','9'};

int i=0,r;

char b[80];

while(x)

{

r=x%m;

x=x/m;

b[i++]=ch[r];

}

for(--i;i>=0;i--)

printf("%c",b[i]);

printf("\n");

}

void main()

41

{

change(20,2);

change(20,8);

}

10100

24

4.5 递归函数与递归调用

 

1. 递归函数又称为自调用函数。其特点是在函数内部可以直接或间接地自己调用自己。C

语言可以使用递归函数。

从函数定义的内容上看,在函数体内出现调用该函数本身的语句时,它就是递归函数。递归函数的结构十分简练。

递归模型:

递归模型反映一个递归问题的递归结构,例如:

f(1)=1

f(n)=n*f(n-1) n>1

其中第一个式子给出了递归的终止条件,称之为递归出口;第二个式子给出了f(n)和f(n-1)之间的关系。称为递归体。

递归的执行过程:

递归是把一个不能或不好直接求解的“大问题”转化成一个或几个“小问题”来解决,再把这些“小问题”进一步分解成更小的“小问题”来解决。如此分解,直至每个“小问题”都可以直接解决(此时分解到递归出口)。

递归的执行过程由分解和求值两部分构成。

注:递归分解不是随意的分解,递归分解要保证“大问题”和“小问题”相似,即求解过程与环境都相似。

例:n的阶乘 

1 当n=0时 

n!=

n*(n-1)!  当n>0时  

2. 递归调用 

递归函数的计算程序,可采用递归调用算法实现。 

例如:计算n的阶乘 

foctorial4(n) 

int n; 

{ 

int result; 

if(n==0) 

result=1; 

else 

result=n*foctorial4(n-1); /* 递归调用 */

return(result); 

} 

42

例:用递归法编写求最大公因子函数。

#include <stdio.h>

int gcd(int x,int y)

{

if(y<=x && x%y==0)

return y;

else if(y>x)

return gcd(y,x);

else

return gcd(y,x%y);

}

void main()

{

int a,b;

printf("请输入两个正整数:");

scanf("%d,%d",&a,&b);

printf("最大公因子为:%d",gcd(a,b));

}

例:编写求Fibonacci数列的第n项的函数。Fibonacci数列为:0,1,1,2,3,5,8,13,21,…,。即从第3项开始,每一项为前两项之和。

int fibo(int n)

{

if(n ==0 || n==1)

return n;

else

return fibo(n-1)+fibo(n-2);

}

void main()

{

long m ;

m=fibo(10);

printf("Fibonacci数列第10个数是%d",m);

}

4.6 外部函数和内部函数

1.基本概念

根据函数能否被其它源文件调用,将函数区分为内部函数和外部函数。主要解决不同源文件中函数之间的调用问题。

如果一个函数只能被本文件中其它函数所调用,称为内部函数。在定义函数时,指明其存储类是extern,则该函数就是外部函数。当函数类型省略时,该函数为外部函数。

内部函数和外部函数的定义

static(extern) 类型标识符 函数名(形参表)

43

如 static int fun(a,b) /*定义fun 为内部函数,函数类型为int*/

extern int f1(a,b) /*定义f1为外部函数*/

int f2(a,b) /*定义f2为外部函数,缺省时为外部类型*/

3.外部函数应用

便于程序设计结构化,将常用的函数放在一个文件中,其它的程序均可调用它。 例4.7外部函数

file1.c(源文件1)

#include <stdio.h>

extern prime(); // 说明本文件中要用到其它文件中的函数

main()

{ int number;

printf(“请输入一个正整数:\n”);

scanf(“%d”,&number);

if (prime(number))

printf(“\n %d是素数.”,number);

else

printf(“\n %d不是素数.”,number);

}

file2.c(源文件2)

int prime(number) /*定义外部函数,此函数用于判别素数*/

int number;

{ int flag=1,n;

for (n=2;n<number/2 && flag==1;n++)

if (number%n==0) flag = 0;

return(flag);

}

4.7 变量的存储类型

 

1. 局部变量 

定义在某一函数或某一部分程序内部的变量,称为局部变量。局部变量仅在其所定义的局部范围内起作用,离开该范围,它们将自动消失,因此,又称为局部自动变量。  例4.8: 

#include <stdio.h> 

main() 

{ 

int x;  /*局部变量*/

x=1; 

func1(); 

func2(); 

44

printf(″x=%d\n″,x); /* 值为1 */

} 

func1() 

{ 

int x;  /*局部变量*/

x=10; 

printf(″x1=%d\n″,x); 

return; 

} 

func2(); 

{; 

float x;  /*局部变量*/

x=1.2; 

printf(″x2=%f\n″,x); 

return; 

} 

其中,局部变量x在三个函数中彼此无关。 

执行结果: 

x1=10 

x2=1.2 

x=1 

例4.9: 局部变量x的变化

#include <stdio.h> 

main() 

{ 

int x=1; 

{ 

int x=2; 

{ 

int x=3; 

printf(″x1=%d\n″,x); /* 输出值为3 */ }  /* 释放x值3 */

printf(″x2=%d\n″,x); /* 输出值为2 */ }  /* 释放x值2 */

printf(″x3=%d\n″,x); /* 输出值为1 */

} 

执行结果: 

x1=3 

x2=2 

x3=1 

2. 静态局部变量 

静态局部变量局限于定义它的函数之内,但并不随函数的退出而消失。 例 4.10:静态局部变量 

/* Program to illustrate local and static variables */ 

45

#include <stdio.h> 

main() 

{ 

int i; 

for(i=0;i<5;i++) 

as(); 

} 

as() 

{ 

int 1v=0; 

static int sv=0; /*静态局部变量每次调用的保留上次调用的值*/  printf(″local=%d,static=%d\n″,1v,sv); 

1v++; 

sv++; 

return; 

} 

执行结果: 

1ocal=0,static=0 

1ocal=0,static=1 

1ocal=0,static=2 

1ocal=0,static=3 

1ocal=0,static=4 

静态局部变量的特点: 

(1)只在第一次调用时,才作初始化赋值,即编译时赋一次值。 

(2) 下次调用的初值,是前一次退出时留下的值。

 

3.全程变量

 

定义在所有函数之外,可供所有函数访问的变量,称为全程变量或全局变量。 例4.11: 求输入数的阶乘。

#include <stdio.h> 

int number; /* 定义number全程变量*/ 

main() 

{ 

printf(″Please type in the number \n″); 

scanf(″%d″,&number); 

printf(″%d!=%d\″,number,factorial2()); 

} 

factorial2() 

{ 

int result=1;  /* 定义result局部变量*/ 

while(number!=0)  /* 直接使用全局变量number */

{ 

result*=number; 

46

number--; 

} 

return(result); 

} 

函数factorial2()没有形参,可直接对全程变量number进行处理,函数调用时也不需要实参。 例4.12 程序分析

(1) #include <stdio.h>

main ()

{

int i,j=1; /* main定义变量 */

for (i=0;i<3;i++) /* 使用main中定义的变量i */

{

int i=0;

i=i+j++; /* 使用循环体内定义的变量i */

printf("%d,",i); /* 使用循环体内定义的变量i */

}

printf("%d, %d\n",i,j);/* 使用main内定义的变量i */

}

程序的输出结果:

1,2,3,3,4

(2) #include <stdio.h>

int func();

int i=5; /* 外部变量 */

main ()

{

int i,x; /* 自动变量 */

x=10;

for (i=0;i<3;i++) /* 使用main中定义的变量i */

{

int x=1;

printf("%d,",++x); /* x先取值后自加 */

x=func();

}

printf("%d, \n",x);

}

int func()

{

printf("%d, ",i);

i++;

return(i);

}

程序执行结果:

47

2,5,2,6,2,7,10

(3) #include <stdio.h>

void inner();

void st_pr();

char c='A';

main ()

{ int i;

for (i=0; i<3; i++) {

inner();

st_pr();

printf("%c;",c);

c++;

}

}

void inner ()

{ char c='a';

printf("%c",c);

c++;

}

void st_pr()

{ static char c='Z';

printf("%c",c);

c--;

}

程序执行后的输出结果:

aZA; aYB; aXC;

4.寄存器变量

(1)寄存器变量的定义和说明 

变量的存储类型除了真正使用内存单元的全程变量、局部自动变量和静态变量之外,还有一种称为寄存器变量。指定它使用寄存器存贮。其说明格式如下: 

Register 类型说明符 变量名; 

(2)寄存器变量的使用 

1)使用限制 

寄存器变量仅适用于整数和字符类型的变量名,不能用结构或数组等复杂数据类型; 一个程序模块只允许使用少量寄存器变量; 

寄存器变量仅适用于局部变量,不能用于全程变量; 

当指定的寄存器变量个数超过系统所能提供的寄存器数量时,多出的寄存器变量视同自动变量。 

2)寄存器变量可提高程序的效率,应将使用频率最高的变量说明成为寄存器变量。一般常用于循环变量,例如: 

 … 

{ 

48

register int i; 

for(i=0;i<LIMIT;i++){ 

… 

} 

 …

} 

例4.13 求阶乘。

int fac(n)

int n;

{register int i,f=1; /*定义寄存器变量*/

for (i=1;i<=n;i++)

f=f*i;

return(f);

}

main()

{int i;

for (i=1;i<=5;i++)

printf("%d!=%d\n",i,fac(i));

}

4.8 函数的数据传递

在一个函数中调用另一个函数时,实参的值传递给对应的形参。从而实现了把数据由调用函数传递给被调用函数。在使用参数传递数据时,可以采用两种不同的方式,即数据值传递和地址传递。此外,使用参数还可以把函数的处理结果返回到调用它的函数中。

1、值传递方式

使用数据值传递方式在函数间传递数据,就是把数据本身作为实参传递给形参,在背调用函数运行完毕后,并不将形参的结果回传给实参。

使用值传递方式传递数据的特点是:由于数据在传递方和被传递方占用的的不同内存空间,所以接收数据的变量在被被调函数中无论怎么变化,都不会影响调用函数中相应实参的值。

例:

#include <stdio.h>

void swap(int x,int y)

{

int temp;

temp=x;

x=y;

y=temp;

}

void main()

{

int a=2,b=3;

swap(a,b);

49

printf("a=%d b=%d",a,b);

}

2、地址传递方式

使用地址传递方式传递数据时,传递的不是数据本身,而是存储该数据的地址。在种方式中,以数据的存储地址作为实参调用一个函数,而被调函数的形参必须是可以接受地址值的指针变量,并且它的数据类型必须与被传递数据的数据类型相同。

使用地址传递方式传递数据的特点是:由于数据无论是在调用函数中还是被调函数中都使用同一个存储空间,所以在被调函数中对该存储空间的值做出来某种变动后,必定会影响使用该空间的调用函数中的变量值。

例:

#include <stdio.h>

void swap(int *x,int *y)

{

int temp;

temp=*x;

*x=*y;

*y=temp;

}

void main()

{

int a=2,b=3;

swap(&a,&b);

printf("a=%d b=%d",a,b);

}

利用地址传递方式的特点:可以从函数中返回多个结果。

3、return传递数据

从被调函数传递数据给调用函数,一般采用函数的返回值来实现。返回值是被调函数用函数执行后返回给调用函数的一个数据,它是通过return语句实现的。

return语句的使用格式如下:

return(<表达式>);或return <表达式>;

其功能是把程序控制从被调函数返回到调用函数中,如果函数有返回值,则同时吧返回值带给调用函数。

使用return语句只能把一个返回值传递给调用函数,因此,C语言的函数都是单值函数。返回值本身可以是普通数据,也可以是地址值。当然,C语言的函数中不要求必需有return语句,没有return语句时,程序执行达到包围函数的下面的大括号是,自动返回调用函数。 例:已知三角形的3个边长,编写一个函数,求该三角形的面积。

float area(float a,float b,float c)

{

float s;

s=(a+b+c)/2;

return (sqrt(s*(s-a)*(s-b)*(s-c));

}

4、全局变量传递数据

函数外定义的变量是全局变量,它在所有的函数中都是可见的,因此可以利用这个特性 50

在函数间传递数据。

练习题:

分析以下程序的执行结果。

#include <stdio.h>

int func(int a)

{

int b=0;

static int c=3;

a=c++,b++ ;

return a;

}

void main()

{

int a=2,i,k;

for(i=0;i<2;i++)

{

k=func(a++);

}

printf("%d\n",k);

}

4

例:执行以下程序,输入100,给出其执行结果:(值传递)#include <stdio.h>

void func(int n)

{

int i;

for(i=n-1;i>=1;i--)

n=n+i;

printf("n=%d\n",n);

}

void main()

{

int n;

printf("输入n:");

scanf("%d",&n);

func(n);

printf("n=%d\n",n);

}

输入n:100

n=5050

n=100

分析以下程序的执行结果。

#include <stdio.h>

int func(int a,int b)

51

{

int c;

c=a+b;

return c;

}

void main()

{

int x=6,y=7,z=8,r;

r=func((x--,y++,x+y),z--);

printf("%d\n",r);

}

21

例:分析以下程序的执行结果。(知识点:静态变量、递归函数)#include <stdio.h>

#include <math.h>

int fact()

{

static int i=5;

if(i == 0)

return 1;

else

{

i--;

return (i+1)*fact();

}

}

void main()

{

printf("fact=%d\n",fact());

}

120

52

第5章 指针 

5.1指针和地址

 

指针是C语言中一个极其重要的概念,也是C语言程序设计的难点。 

1.指针的定义 

指针变量是用于存放其他变量的地址,简称为指针。指针变量所包含的是内存地址,而该地址便是另一个变量在内存中存储的位置。指针它本身也是一种变量,和其他变量一样,要占有一定数量的存储空间,用来存放指针值(即地址)。 

2.指针的作用 

C语言中引入指针类型变量,不仅简单地实现了类似于机器间址操作的指令,更重要的是使用指针可以实现程序设计中利用其他数据类型很难实现,甚至无法实现的工作。指针的作用主要体现如下: 

(1)使代码更为紧凑和有效,提高了程序的效率。 

(2)便于函数修改其调用参数,为函数间各类数据传递提供简捷而便利的方法。 

(3)实现动态分配存储空间。 

正确地使用指针会带来许多好处,但指针操作也有难以掌握的一面,若使用不当,可能会产生程序失控甚至造成系统崩溃。

5.2指针变量和指针运算符

 

1.指针变量及其说明 

指针变量是存放地址的变量。和其他变量一样,必须在使用之前,加以定义说明。其定义说明的一般形式: 

类型说明符 *指针变量名; 

例如: 

int *P;   说明指针变量P是指向int类型变量的指针.

char *s;   说明指针变量s是指向char类型变量的指针。

float *f; 说明指针变量f是指向float类型变量的指针。

Double *d; 说明指针变量d是指向double类型变量的指针。 

static int pa; 说明指针变量pa是指向int类型变量的指针,而pa本身的存储 类型是静态变量。 

int*fpi();  说明fpi()是一个函数,该函数返回指向int类型变量的指针。 Int (*pfi)(); 说明pfi是指向一个函数的指针,该函数返回int类型变量。  指针变量值表达的是某个数据对象的地址,只允许取正的整数值。然而,不能因此将它与整数类型变量相混淆。所有合法指针变量应当是非0值。如果某指针变量取0值,即为NULL,则表示该指针变量所指向的对象不存在。这是NULL在C语言中又一特殊用处。  53

2.指针运算符&和* 

指针变量虽是正的整数值,但不是整数类型变量。指针变量最基本的运算符是&和*。 (1)&——取地址 

它的作用是返回后随变量(操作数)的内存地址。请注意,它只能用于一个具体的变量或数组元素,不可用于表达式。 

例: 

int *p;

int m; 

m=200; 

p=&m; 

这是将整数类型变量m的地址赋给整数类型的指针变量p。 

(2) *——取值 

它的作用是返回其后随地址(指针变量所表达的地址值)中的变量值。 

例: 

int *p; 

int m; 

int n; 

m=200; 

p=&m; 

n=*p; 

这是将指针变量p所指向的整数类型变量(即m)的值赋给整数类型变量n。其结果与赋值语句m=n;一样,但操作过程用了指针变量p。 

&与*运算符互为逆运算。对指针变量px指向的目标变量*px实行取址运算:  &(*px) 

其结果就是px。对变量x的地址实行取值运算: 

*(&x) 

其结果就是x。 

注意,x与*px同级别可写成x=*px或*px=x。px与&x同级别可写成px=&x,但决不可写成&x=px,也不能写成px=x。 

3.指针的初始化和赋值运算 

指针的初始化往往与指针的定义说明同时完成,初始化一般格式: 

类型说明符 指针变量名=初始地址值; 

例如: 

char c; 

char *charp=&c; 

请注意: 

·任何一个指针在使用之前:

必须加以定义说明。

必须经过初始化。

未经过初始化的指针变量禁止使用。 

·在说明语句中初始化,也是把初始地址值赋给指针变量,只有在说明语句中,才允许这样写。而在赋值语句中,变量的地址只能赋给指针变量本身。 

·指针变量初始化时,变量应当在前面说明过,才可使用&变量名作为指针变量的初始值。否则,编译时将出错。 

54

·必须以同类型数据的地址来进行指针初始化。

指针变量可以进行赋值运算,这种赋值运算操作也仅限制在同类之间才可实现。 

例5.1:指针变量的引用。 

main() 

{ 

int x=10 

int *p1=&x,*p2;  /* p1指向 x */

p2=p1;  /* p2指向 x */

printf(″%d\n″,*p2); 

} 

执行结果应显示:10 

例5.2:指针变量的运算。 

main() 

{ 

char c=′A′; /*变量c的初始值为′A′*/ 

char *charp=&c; /*变量c的地址作为指针变量charp的初始值*/  printf(″%c%c\n″,c,*charp); 

c=′B′; /*变量c赋值为′B′*/ 

printf(″%c%c\n″,c,*charp); 

*charp=′a′; /*将指针变量charp所指向地址的内容改为′a′*/  printf(″%c%c\n″,c,*charp); 

} 

执行结果应显示:

AA 

BB 

aa 

4.sizeof运算符 

sizeof运算符可以准确地得到在当前所使用的系统中某一数据类型所占字节数。sizeof运

算的格式: 

sizeof(类型说明符) 

其值为该类型变量所占字节数。用输出语句可方便地打印出有关信息,例如: 

printf(″%d\n″,sizeof(int)); 

printf(″%d\n″,sizeof(float)); 

printf(″%d\n″,sizeof(double)); 

5.指针的算术运算 

指针的算术运算是按地址计算规则进行。由于地址计算与相应数据类型所占字节数有关,所

以,指针的算术运算应考虑到指针所指向的数据类型。

指针的算术运算只有如下四种: 

+,-,++,-- 

(1)指针自增1运算++和指针自减1运算-- 

指针自增运算意味着指针向后移动一个数据的位置,指向的地址为原来地址+sizeof(类

型说明符)。例如: 

int i,*p; 

55

p=&i; 

p++; 

结果是p指向了变量i的地址+sizeof(int)。 

(2)指针变量的+和-运算 

指针变量px加(+)正整数n,表示指针向后移过n个数据类型,使该指针所指向的地址变为原指向的地址+sizeof(类型说明符)*n。指针变量减(-)正整数n,表示指针往前移回n个数据,使该指针所指向的地址变为原指向的地址-sizeof(类型说明符)*n。 

6.指针的关系运算 

指向同一种数据类型的指针,才有可能进行各种关系运算。指针的关系运算符包括有: ==,!=,<,<=,>,>=。

它们实际所进行的是两个指针所指向的地址的比较。

不同类型指针之间的比较是没有意义的。

指针与整数常量或变量的比较也没有意义。

唯独常量0是例外的,一个指针变量为0(NULL)表示该指针指向空间不存在,称为空指针。 如果有一个指针p,可以用p==0或p!=0来判断p是否为空指针。 

7.使用指针编程中的常见错误 

(1)使用未初始化的指针变量,例如: 

main() 

{ 

int x,*p; 

x=0; 

*p=x;  /*指针变量未初始化*/

… 

} 

*p未被初始化。 

(2)指针变量所指向的数据类型与其定义的类型不符,例如: 

main() 

{ 

float x,y; 

short int *p; 

p=&x; /*数据类型不符*/

y=*p; 

 … 

} 

x,y与*p数据类型不符。 

(3)指针的错误赋值,例如: 

main() 

{ 

int x,*p; 

x=10 

p=x 

 … 

} 

错在以普通变量x赋给指针变量p。 

56

(4)用局部变量的地址去初始化静态的指针变量,例如: 

int glo; 

func() 

{ 

int 1oc1,1oc2; 

static int *gp=&glo; 

static int *1p=&1oc1; 

int *rp=&1oc2; 

 … 

} 

用局部变量1oc1去初始化静态的指针变量1p是错误的。

 

5.3 指针与函数参数 

在函数调用中,使用指针作为参数,可以改变调用环境中的变量之值。这是使用一般变量作为参数所难以实现的。让我们来观察一个函数的调用: 

例5.3:x和y的值互换。 

swap1(x,y) 

int x,y;  /*局部变量不能带出函数*/

{ 

int temp; 

temp=x; 

x=y; 

y=temp; 

} 

main() 

{ 

int a,b; 

a=10; 

b=20; 

swap1(a,b); 

printf(″a=d%d b=%d\n″,a,b); 

} 

执行结果:

a=10 b=20

虽然,swap1()函数中,x和y的值互相交换了,但x,y是局部变量,主函数对该函数的调用swap1(a,b)是“传值”调用,x与y的交换结果,不可能影响调用者中的变量a,b,a与b之值并未交换。 

改用指针作为参数,也就是用变量的地址作为参数,即所谓“传址”(或称为“传名”)的调用,就可改变相应地址中的变量。 

57

例5.4:x和y值互换。 

swap2(px,py) 

int *px,*py;  /* 地址变量 */

{ 

int temp; 

temp=*px; 

*px=*py; 

*py=temp;  /* 返回后地址变量释放 */

} 

main() 

{ 

int a,b; 

a=10; 

b=20; 

swap2(&a,&b);  /*传地址*/

printf(″a=%d b=%d\n″,a,b); /*得到交换的值*/

} 

执行结果:

a=20 b=10

a与b的地址,即&a与&b并未交换,但其值已被交换。

 

5.4 指针、数组和字符串指针 

1.指针和数组 

用指针和用数组名访问内存的方式几乎完全一样,却又有微妙而重要的差别。指针是地址变量,数组名则是固定的某个地址,是常量。 

若定义一个数组: 

int a[10]; 

则在内存中占有从一个基地址开始的一块足够大的连续的空间,以包容a[0],a[1]…,a[9]。基地址(或称为首地址)是a[0]存放的地址。其他依下标顺序排列。 

若定义一个指针: 

int *p; 

则意味着:p=&a[0];即

p指向a[0] 

p+1指向a[1] 

p+2指向a[2] 

 … 

p+i=&a[i];

p+i指向a[i] (i=0,…,9) 

(1)在C语言中,数组名本身是指向零号元素的地址常量。因此,p=&a[0]可写成p=a,a当作常量指针,它指向a[0]。p=a+i与p=&a[i]等价,故数组名可当指针用,它指向0号元素。 

58

(2)若定义p为指针,a为数组名,且p=a,则p+i指向a[i],a+i也指向a[i]。

这样,a[i]与*(p+i)或*(a+i)可看成是等价的,可互相替代使用。 

注意:

由于a只是数组名,是地址常量,而不是指针变量,所以可以写p=a,但不能写a=p,可以用p++,但不能用a++。 

2.字符串指针 

在C语言中,可用char型数组来处理字符串,而数组又可用相应数据类型的指针来处理。所以可以用char型指针来处理字符串。通常把char型指针称为字符串指针或字符指针。例5.5:计算串长度的函数strlen()是使用字符串指针的一个实例。

strlen(s) 

char *s; 

{ 

int n; 

for(n=0;*s!=′\0′;s++) 

n++ 

return(n); 

} 

main() 

{ 

static char str[]={″Hello! ″}; 

printf(″%c\n″,*str); 

printf(″%d\n″,strlen(str)); 

printf(″%c\n″,*(str+1)); 

} 

执行结果:H 

6 

e 

定义字符指针可以直接用字符串作为初始值,来初始化字符指针。例如:  ①char *message=″Hello!″; 

或 

②char *message; 

message=″Hello!″; 

这样的赋值并不是将字符串复制到指针中,只是使字符指针指向字符串的首地址。但对于数组操作,例如: 

char text[80]; 

则不可写成: 

text=″Hello!″; 

因为,此处text是数组名,而不是指针,只能按字符数组初始化操作。即:  static char text[80]={″Hello!″}; 

当字符串常量作为参数(实参)传递给函数时,实际传递的是指向该字符串的指针,并未进行字符串拷贝。例如: 

例5.6:向字符指针赋字符串 

main() 

{ 

59

char s=″Hello!″; /*相当于s数组*/

char *p; 

while(*s!=′\0′) 

printf(″%c″,*s++); 

printf(″\n″); 

p=″Good-bye!″; /*指针指向字符串*/

while(*p!=′\o′) 

printf(″%c″,*p++); 

printf(″\n″) 

} 

执行结果:

Hello! 

Good-bye! 

此例中字符串是逐个字符输出,也可以字符指针为参量,作字符输出,例如:  printf(″%s″,s); 

或 

printf(″%s″,p); 

输入函数scanf()也可以字符指针为参量,如: 

scanf(″%s″,p); 

例5.7:以字符指针为参数来调用串比较函数strcomp()。

strcomp(s,t) 

char *s,*t;  /*形式参数为字符指针*/

{ 

for(;*s==*t;s++,t++) 

if(*s==′\o′) 

return(0);  

return(*s-*t); /* 字符串相同返回零值 */

} 

main() 

{ 

static char s1[]={″Hello!″}; 

char *s2=″Hello!″; 

printf(″%d\n″,strcomp(s1,s2)); 

printf(″%d\n″,strcomp(″Hello″,s2)); 

} 

s和t初值已由实参传递过来了,所以for语句的第一个表达式为空语句。 例如:用指针处理字符串的拷贝,比用数组处理更精练 

strcpy(s,t) 

char *s,*t; 

{ 

while(*s++=*t++); 

} 

如果用数组处理则要复杂些,例如: 

strcpy(s,t) 

60

char s[],t[]; 

{ 

int i; 

i=0; 

while((s[i]=t[i])!=′\o′) 

i++; 

} 

直接演化出指针处理,即: 

strcpy(s,t) 

char*s,*t; 

{ 

while((*s=*t)!=′\o′) 

{ 

s++; 

t++; 

} 

} 

优化后便是如前所示的指针处理方法的程序。其中,也使用了空语句(;),而且不必进行与′\0′的比较。

 

5.5 指针数组 

1.指针数组的定义和说明 

同类指针变量的集合,就形成了指针数组。或者说,以指针变量为元素的数组,就称为指针数组。这些指针变量应具有相同的存储类型,并且,指向的目标数据类型也应相同。  指针数组的一般表示格式: 

类型说明符 *指针数组名[元素个数]; 

例如: 

int *p[2]; 

p[2]是含有p[0]和p[1]两个指针的指针数组,指向int型数据。

2.指针数组的初始化 

指针数组的初始化可以在说明的同时进行。与一般数组一样,只有全局的或静态的指针数组才可进行初始化。而且,不能用局部变量的地址去初始化静态指针。 

例5.8:指针数组。 

#include <stdio.h> 

main() 

{ 

static int b[2][3]={{1,2,3},{4,5,6}}; 

static int *pb[]={b[0],b[1]}; / *指针数组指向行首 */

int i,j; 

for(i=0;i<2;i++){ 

for(j=0;j<3;j++) 

61

printf(″b[%d][%d]=%d″,i,j,*(pb[i]+j)); 

printf(″\n″) 

} 

} 

执行结果: 

b[0][0]=1 b[0][1]=2 b[0][2]=3 

b[1][0]=4 b[1][1]=5 b[1][2]=6 

此例中,将一个二维数组b[2][3]分解成两个一维数组。它们的首地址,分别为b[0]和b[1],并被赋给指针pb[0]和pb[1]。 

3.字符指针数组 

字符指针可以用来处理一个字符串。字符指针数组可以用来处理多个字符串。这正是字符指针数组的主要作用。 

例5.9 :

main()

{void sort();

void print();

static char *name[ ]={"Follow me","BASIC","Great Wall",

"FORTRAN","Computer design"}; /*指针数组指向各字符串*/ int n=5;

sort(name,n);

print(name,n);

}

void sort(name,n)

char *name[ ]; int n;

{char *temp;

int i,j,k;

for (i=0;i<n-1;i++) /*指针数组排序*/

{k=i;

for (j=i+1;j<n;j++)

if (strcmp(name[k],name[j])>0) /*指向各字符串的比较*/

k=j;

if (k!=i)

{temp=name[i]; name[i]=name[k]; name[k]=temp;}

}

}

void print(name,n)

char *name[ ]; int n;

{int i;

for (i=0;i<n;i++)

printf("%s\n",name[i]); /*按排好序的指向输出字符串*/

}

运行结果为:

BASIC

62

Computer design

FORTRAN

Follow me

Great Wall

5.6 多级指针

 

指针的指针,就形成了多级指针,也就出现了多级间址访问的现象。多级指针也称为指针链。一般很少使用超过二级的指针。过多的间址难于调试跟踪,也容易出错。常用的多级指针也只是二级指针,其定义说明的一般格式如下: 

类型说明符 **指针变量名 

例如: 

int **p; 

这里p为二级指针,它所指向的是一个指向整数型数据的指针。又如: 

static char **charp; 

说明charp本身是静态的二级指针,指向的是个指向最终目标为字符型变量的指针。  例5.10:二级指针 

main() 

{ 

int x,*p,**q; 

x=10; 

p=&x; 

q=&p; 

printf(″%d\n″,* *q); 

} 

用指针来处理数组是很自然的,在实际使用中,二级指针常用来处理字符指针数组。  例5.11: 

#include <stdio.h> 

main() 

{ 

char **pp; 

static char *di[]={″up″,″down″,″left″,″right″,″″}; /* 字符指针

数组指向字符串*/

pp=di; /*二级指针pp指向字符指针数组di*/

while(**pp!=NULL) 

printf(″%s\n″,*pp++); 

} 

执行结果: 

up 

down 

left 

right

63

例5.12: 指针数组指向整型数组

main()

{static a[5]={1,3,5,7,9}; /*整型数组*/

static int *num[5]={&a[0],&a[1],&a[2],&a[3],&a[4]}; /* 指针数组刺 */

int **p ,i;

p=num; /*指向指针数组*/

for(i=0;i<5;i++)

{printf("%d\t",**p);p++;}

}

运行结果:

1 3 5 7 9

5.7 返回指针的函数

一个函数被调用后,可以返回各种类型的数据,也可以返回指针数据,也就地址。 例5.13: 有若干学生,每个学生4门课,要求输入学生序号后,能输出该学生的全部成绩。 main()

{static float score[][4]={{60,70,80,90,},{56,89,67,88},{34,78,90,66}};

float *search(); /*说明函数返回指针*/

float *p;

int i,m;

printf("enter the number of stuednt:");

scanf ("%d",&m);

printf("The scores of No. %d are:\n",m);

p=search(score,m); /* 得到该位同学的行首地址,指向列 */

for(i=0;i<4;i++)

printf("%5.2f\t",*(p+i));

}

float *search(pointer,n)

float (*pointer)[4];

int n;

{float * pt;

pt=*(pointer+n); /* 指向列 */

return(pt);

}

运行结果:

enter the number of student:1↙

  The score of No.1 are:

56.00 89.00 67.00 88.00

64

5.8 函数指针 

1.函数指针的定义和说明 

函数也具有与数组类似的特性,可以用函数名表示函数的存储首地址,即执行该函数的入口地址。指向函数入口地址的指针,就称为函数指针。函数指针定义说明的一般格式如下:  数据类型说明 (*函数指针名)() 

例如: 

int (*func)(); 

2.函数指针的作用 

对于指向某函数的函数指针func,实现访问目标的运算*,即(*func)(),其结果是使程序控制转移到指针所指向的地址去执行该函数。所以,函数指针所指向的是程序代码区,而不是数据区。这与一般数据指针变量有原则的区别,一般数据指针指向数据区。

5.9 命令行参数

main()函数也可以带有参数。在程序执行时,通过命令行将参数传递给程序,以控制程序的执行,这就是命令行参数。其格式如下:

main(int argc, char *argv[])

按照约定,main()可以带两个名为argc和argv的参数,以便建立同操作系统之间的通信联系。变量argc给出命令行参数的个数;参数argv是一个指向char的指针数组,其中的指针元素分别指向包含这些命令行的字符串数组。

例:编写一个程序打印命令行参数。

#include <stdio.h>

void main(int argc ,int *argv[])

{

int count=0;

printf("argc=%d\n",argc);

while(argc-- > 0)

{

printf("参数:%d:%s \n",count++,*argv++);

}

}

C:\Debug>lichao one two three for five

argc=6

参数:0:lichao

参数:1:one

参数:2:two

参数:3:three

参数:4:for

参数:5:five

65

第6章 结构与联合 

6.1 结构的定义

1.结构的定义及其一般格式 

数组是将同类型元素组成单个逻辑整体的一种数据类型。而结构则是将不同类型元素组成单个逻辑整体的一种数据类型。结构是C语言中一种强有力的数据类型, 是很重要的概念之一。 

例如,日期由年、月、日构成: 

int year; 

int month; 

int day; 

上述表示方法不能看出它们是彼有关系的三个量。可以采用下面的定义表示:  struct date 

{  

int month; 

int day; 

int year; 

}; 

这样,就可把年、月、日当作一个整体处理。年、月、日是该结构的成员。结构的成员也可以是不同数据类型的变量,例如: 

struct wage 

{ 

char name[30]; 

float salary; 

}; 

上面定义了两个结构数据类型,date和wage。可用这些已定义的结构类型来说明一组具体结构类型变量。例如: 

struct date today,tomorraw; /*定义了两个结构类型变量today,tomorraw*/ struct wage worker1,worker2,worker3;/*定义了三个结构类型变量*/  

结构定义的一般格式为: 

struct[结构类型名] 

{ 

类型说明符 成员变量名; 

 … 

类型说明符 成员变量名; 

}[结构变量列表]; 

结构变量列表是指以逗号分隔开的若干结构类型变量,例如: 

66

struct date 

{ 

int month; 

int day; 

int year; 

}today; 

或 

struct 

{ 

int month; 

int day; 

int year; 

}today; 

或 

struct date /*定义了一种结构类型 */ 

{ 

int month; 

int day; 

int year; 

}; 

struct date today;/*说明了一个具体的结构变量*/ 

这三种说明结构类型变量的方式都可以使用,第三种写法的风格较好。

 

2.结构的存取 

结构变量成员可作为单独变量来操作,也就是说,可以直接访问结构中的一个成员变量,其格式为: 

结构变量名.成员变量名 

例6.1: 显示输入的年、月、日

#include <stdio.h> 

main() 

{ 

struct date /*定义了结构类型 */ 

{

int month; 

int day; 

int year; 

}; 

struct date today; /*结构体变量today */ 

printf(″Enter today′s date(年,月,日)\n″); 

scanf(″%d,%d,%d″,&today.year,&today.month,&today.day); 

printf(″Today′s date is %d/%d%/d\n″,today.year, today.month, today.day ) 

} 

执行后可以显示出所输入的今天的年、月、日。

67

例6.2:对外部存储类型的结构体变量的初始化。

/*L10-1*/ 

struct student /*定义结构类型 */

{ long int num;

char name[20];

char sex;

char addr[20];

}a={89031,”Li Lin”,’M’,”123 Beijing Road”}; /*结构体变量赋初值*/

main ()

{printf(“No.:%ld\nname:%s\nsex:%c\naddress:%s\n”,a.num,a.name,

a.sex,a.addr);

}

例6.3: 对静态存储类型的结构体变量的初始化。

Main ()

{

static struct student /*静态结构类型 */

{long int num;

char name[20];

char sex;

char addr[20];

}a={89031,”Li Lin”,’M’,”123 Beijing Road”}; /*赋初值*/

printf(“No.:%ld\nname:%s\nsex:%c\naddress:%s\n”,a.num,a.name,

a.sex,a.addr);

}

6.2结构数组 

不仅结构变量成员可以是数组,而且,同一类结构变量的集合还可构成结构数组,例如:  struct wage 

{ 

char name[30]; 

float salary; 

}; 

static wage persons[100]; 

这就说明了100个结构变量所组成的数组。

 

例6.4:输入后选人名,对后选人投票计数,统计输出每个人的得票结果。

Struct person /* 全局结构类型 */

{char name[20];

int count;

}leader[3]={“Li”,0,”Zhang”,0,”Fun”,0}; /*初始化*/

main ()

{int I,j;

68

char leader_name[20];

for (I=1;I<=10;I++)

{scanf(“%s”,leader_name); /*输入人名*/

for (j=0;j<3;j++)

if (strcmp(leader_name,leader[j].name)==0)

leader[j].count++; /*累加票数*/

}

printf(“\n”);

for (I=0;I<3;I++)

printf(“%5s:%d\n”,leader[I].name,leader[I].count);/* 每人得票数*/ }

 

6.3 结构与函数 

1.向函数传递结构变量成员 

结构变量成员可作为参数传递给函数。例如有一结构为: 

struct fred 

{ 

char x; 

int y; 

float z; 

char s[10]; 

}mike; 

其中各个成员均可作为函数的参数,例如: 

func0(mike.x); /*字符型参数*/ 

func1(mike.y); /*整数型参数*/ 

func2(mike.z); /*浮点型参数*/ 

func3(mike.s); /*字符型数组参数*/ 

func4(mike.s[2]); /*字符型参数*/ 

2.向函数传递完整的结构。 

不仅结构元素可作为参数传递给函数,整个结构也可作为参数传递的函数。完整结构向函数传递的操作中,应注意: 

(1)整个结构按传值方式传递。和普通变量一样,与数组不同,在函数内所引起结构参数中某个值的变化,只影响函数调用时所产生的结构拷贝,不影响原来的结构。 

(2)结构分全局定义和局部定义,定义在所有函数外部的结构称为全局结构,定义在函数内部的结构称为局部结构。它们被引用的范围不同。 

例6.5:局部定义的结构 

main() 

{ 

struct  /*局部定义*/

{ 

int a,b; 

69

char ch; 

}arg; 

arg.a=1000; 

fl(arg); 

printf(″a=%d\n″,arg.a); 

} 

fl(parm) 

struct  /*形参说明定义*/ { 

int x,y; 

char ch; 

}parm; 

{  /*函数体*/ printf(″x1=%d\n″,parm.x);  parm.x=20; 

printf(″x2=%d\n″,parm.x);  return; 

} 

执行结果:

x1=1000 

x2=20 

a=1000

 

例6.6:全局定义的结构 

struct st  /*全局定义*/

{ 

int a,b; 

char ch; 

}; 

main() 

{ 

struct st arg; 

arg.a=1000; 

f1(arg); 

} 

f1(parm)  /*定义函数据库*/

struct st parm;  /*形参说明*/

{ 

printf(″%d\n″,parm.a); 

return; 

} 

其中,结构类型st是全局定义的,各函数都可引用。  

70

6.4 结构的初始化 

1.简单结构的初始化 

结构说明时,行尾加上分号,随后在花括号中按结构定义的各成员顺序给以各自的初始值,并用逗号分隔之。例如: 

struct date 

{ 

int month; 

int day; 

int year; 

};  /*行尾加上分号*/

static struct date today={11,19,1991}; 

注意:局部结构变量不能初始化。

 6.5 联合(union) 

1.联合的定义说明及其一般格式 

在C语言中,不同数据类型的数据可以共用同一个存贮区。这是又一种构造类型的数据类型,称为联合。联合定义说明的一般格式如下: 

union[联合类型名] 

{ 

类型说明符 变量名; 

 … 

类型说明符 变量名; 

}[联合变量列表]; 

例如: 

union mixed 

{ 

char c; 

float f; 

short int i; 

}x; 

或 

union 

{ 

char c; 

float f; 

short int i; 

}x; 

或 

union mixed 

71

{ 

char c; 

float f; 

short int i; 

}; 

union mixed x; 

从形式上看,联合的定义说明与结构很相似,然而,在内存的配合和使用上,两者有本质的区别。上述例子中的联合变量X所占字节数应为三个成员变量c,f和i之中占用字节数的最大值,即变量f所占字节数,可占4字节。同一时刻,只能存有三个成员变量之一。 联合的好处是可以节省空间。 

2.联合的存取 

联合成员的用引用,在表示方法上,类似于结构成员的引用,其一般形式如下:  联合类型变量名.成员变量名 

72

第7章 预处理程序 

7.1什么是预处理程序

 

预处理程序是一些行首以#开始的特殊语句,例如:#include,#define等就是预处理语句。在编译程序的编译过程中,进行其它编译处理(词法分析、语法分析、代码生成、优化和连接等)之前,先进行这些语句的分析处理。

预处理语句使用的目的在于帮助程序员编写出易读、易改、易移植并便于调试的程序。预处理语句主要有四种:

宏定义和宏替换、文件包含、条件编译和行控制。

预处理语句的作用范围是从被定义语句开始直至被解除定义或是到包含它的文件结术为止均有效。

7.2宏定义和宏替换 

“宏”是借用汇编语言中的概念。为的是在C语言程序中方便的作一些定义和扩展。这些语句以#define开头,分为两种:符号常量的宏定义和带参数的宏定义。

1.符号常量的宏定义和宏替换 

符号常量的宏定义语句是一般格式: 

#define 标识符 字符串 

其中标识符就叫作宏名称。

注意:标识符与字符串之间不要用‘=’,结尾不要加‘;’。 

73

2.带有参数的宏定义及其替换 

复杂的宏定义带有参数列表,参数列表中可有不止一个参数,其一般格式: 

#define 标识符(参数列表) 字符串 

对带有参数的宏定义进行宏替换时,不仅对宏标识符作字符串替换,还必须作参数的替换。

例如: 

#define SQ(x) ((x)*(x)) 

那么SQ(a+b)将被宏替换成(a+b)*(a+b)。 

宏定义也可嵌套使用,即一个宏定义可用另一个宏定义来定义。例如: 

#define SQ(x) ((x)*(x)) 

#define CUBE(x) (SQ(x)*(x)) 

3.宏定义类函数 

宏定义常用于把直接插入的代码来代替函数,以提高执行效率。这一类的宏,就称做宏定义类函数,例如: 

#define MIN(x,y) (((x)<(y))?(x):(y)) 

有了这样的宏之后,就可以直接引用,例如: 

m=MIN(a,b); 

这语句将被预处理成: 

m=(((a)<(b))?(a):(b)); 

7.3文件包含 

74

文件包含是指一个程序文件将另一个指定义文件的内容包含进来,用#include语句来说明。

一般有两种格式: 

(1) #include <文件名> 

(2) #include ″文件名″ 

第一种,用尖括号表示在标准库目录下找该文件;第二种,用双引号表示先在当前目录(源文件所在目录)中找包含文件,若找不到,再到标准库目录中找。系统的标准库文件都是.h文件。例如: 

#include <stdio.h> /* 标准输入输出的基本常量和宏或函数文件 */ 

#include <string.h> /* 串函数文件 */ 

#include <malloc.h> /* 内存分配函数文件 */ 

#include <ctype.h> /* 字符函数文件 */ 

#include <math.h> /* 数学函数库文件 */ 

用文件包含,可以减少重复工作,提高程序正确性,还便于维护修改。程序员可以把自己常用的一些符号常量、类型定义和带参数的宏定义,以及一些常用自编函数都放在.h文件中,通过#include语句包含引用之。 

7.4条件编译 

提供条件编译措施使同一源程序可以根据不同编译条件(参数)产生不同的目录代码,其作用在于便于调试和移植。 

条件编译控制语句有不同形式,下面分别讨论。 

1.#ifdef语句及其使用 

一般格式: 

#ifdef 标识符 

75

语句块1 

#else 

语句块2 

#endif 

7.4格式化输入/输出

 

格式化的控制台I/O函数有两种,它们都与标准I/O库有关。源程序开头应包含标准输入输出头文件: 

#include <stdio.h> 

1.printf() 

printf()函数功能为按指定格式输出显示各种基本类型数据,其一般格式: 

printf(“控制串”,参数列表) 

控制串分两部分,即:要显示的字符和格式串。格式串以“%”开头,后跟格式码。格式串与参数一一对应。

2.scanf() 

scanf()的功能是读入各种类型数据,并自动将其转换为恰当的格式,其一般格式为:  scanf(“控制串”,参数列表) 

控制串与前述printf()中的控制串类似,也包含有以“%”开头加格式码组成的格式串。参数列表所列出的应当是变量的地址,而不是变量名。

76

第8章 枚举、位操作 

8.1枚举 

1.枚举的定义和说明 

枚举是一个有名字的某些整数型常量的集合。这些整数常量是该类型变量可取的所有合法值。枚举定义应当列出该类型变量的可取值。 

一个完整的枚举定义说明语句的一般格式: 

enum枚举名{枚举列表}变量列表; 

枚举的定义和说明也可写成两句,即: 

enum枚举名{枚举列表}; 

enum枚举名 变量列表; 

例如: 

enum day{Sun,Mon,Tue,Wed,Thu,Fri,Sat}d1,d2; 

enum day d1,d2; 

用上述枚举定义说明语句,表明枚举类型day的变量d1和d2只可能取Sun,Mon等七个值。 

2.枚举类型的使用 

使用枚举类型有两个好处,其一,使程序更清晰,既提高可读性,也减少书写的错误;其二,迫使编译程序对所定义的类型加以严格检查,防止某些错误发生。  例如:计算第二天为星期几 

enum day{Sun,Mon,Tue,Wed,Thu,Fri,Sat}; 

enum day day_after(d) 

enum day d; 

{ 

return((enum day)(((int)d+1)%7));

} 

8.2 位操作运算符

 

利用位操作运算符可对一个数按二进制格式进行位操作。 

1.按位“与”运算& 

b3=b1&b2 

例如: char b1=25,b2=77,化成二进制(或十六进制)表示为:

   

b1=00011001(或0x1B) 

b2=01001101(或0x4D) 

b3=00001001(或0x09) 即b3=9 

&可用作“掩码”运算,即屏蔽掉某些位。例如,掩码为127,化成二进制表示为01111111,可屏蔽掉第7位。 

2.按位“或”运算 | 

77

b3=b1|b2 

b1=00011001(或0x1B  b2=01001101(或0x4D) 

  b3=01011101(或0x5D) 即b3=93

 

3.按位“异或”运算^ 

b3=b1^b3 

b1=00011001(0x1B) 

b2=01001101(0x4D) 

b3=01010100(0x54) 即b3=84 

很容易验证:b3=b1^b2^b2=b1。这个特性可用于某些数据处理。例如,一个文本的每个字经过与一个关键字做异或处理,就被简单地加密了。要解密时,只需用同一个关键字再做一次异或处理,使其恢复原样。又如,用下列异或处理程序:

 

swap1(a,b) 

int a,b; 

{ 

a=a^b; 

b=a^b; 

a=a^b; 

} 

代替下列程序: 

swap(a,b)  

int a,b; 

{ 

int temp; 

temp=a; 

a=b; 

b=temp; 

} 

同样实现a和b交换,异或处理的速度更快。 

4.按位取反运算符~ 

w2=~w1,w2为w1二进制按位取反。 

例如:unsigned int w1=0122457,w2; 

二进制表示 (8进制表示) 

w1 1010010100101111 (0122457) 

w2 010xxxxxxxxxxxx (0055320) 

5.左移运算符<< 

将二进表示的数值各位顺序左移,最高位丢失,最低位补0。 

78

例如:w1=014712,左移1位,即w2=w1<<1。 

二进制表示 (8进制表示) 

w1 0001100111001010 (014712) 

w2 0011001110010100 (031624) 

左移n位相当于乘2的n次方,但左移速度比乘法快。

 

6.右移运算符>> 

将二进制表示的数值各位顺序右移,最低位丢失,对无符号整数最高位补0。  例如:w=040273,右移2位,即w2=w1>>2。 

二进制表示 (8进制表示) 

w1 010xxxxxxxxxxxx (040273) 

w2 0001000000101110 (010056) 

右移n位相当于除以2的n次方,但右移速度比除法快。 

例如:用移位方法测试出机器的字长(即int含二进制位数) 

woldlength() 

{ 

int i; 

usinged v=~0; /*变量v为全1*/ 

for(i=1;(v=v>>1)>0;i++) 

; 

return(i); 

} 

79

第9章文件

9.1 ASCII码文件的存取

1.fopen()函数

对文件的各种操作都要涉及到文件指针。进行文件操作,必须加上: #include <stdio.h>

并对文件指针进行说明:

FILE *fp;

对文件操作时,应先打开文件取得文件指针。 fopen()函数的功能是打开指定的文件并返回该文件指针。其一般调用格式是:

FILE *fp;

Fp=fopen(文件名,模式);

其中,模式有

“r”—— 读

“w”—— 写

“a ”—— 插入

如果该文件不存在,就不能为读或插入而打开文件,将返回空指针(NULL)。如果为写打开文件,已存的文件将被重写(覆盖原有的内容),要是文件不存在,将建立新文件以便写入。若磁盘空间已满,则无法写入,将返回空指针(NULL)。

例;

FILE *fp;

If ((fp=fopen(“test”,”r”))==NULL{

puts(“Cannot open the file”);

Exit(0);

}

2. fclose()函数

一般来说, 计算机系统能允许同时打开的文件数量是有限的, 所以应当关闭当前暂时不用的文件, fclose()函数的功能就是关闭指定的文件,调用格式为:

fclose(fp)

其中, fp为指向要关闭的文件的指针。

3.getc()和 putc()

getc()和 putc()分别是对指定的文件进行单个字符的读写操作,需要以文件指针作为参数。一般调用格式为:

FILE *infp ;

char ch;

ch=getc(infp); /*从指定的文件中读取一个字符*/

FILE *outfp ;

char ch;

putc(ch,outfp); /*向指定的文件中写入一个字符*/

80

9.2 二进制文件的存取

二进制文件比ASCII文件少占磁盘空间, 处理速度快, 但文件不能直接显示出来。

1.open()函数

与ASCII文件相似,对文件操作时应先打开文件取得文件指针。 只是模式不同。其一般调用格式是:

FILE *fp;

Fp=fopen(文件名,模式);

其中,模式有

“rb”—— 读二进制代码

“wb”—— 写二进制代码

“ab ”—— 插入二进制代码

要是该文件不存在,就不能为读或插入而打开文件,返回空指针(NULL)。如果为写打开文件,已存的文件将被重写(覆盖原有的内容),若文件不存在,将建立新文件以便写入。如果磁盘空间已满,无法写入返回空指针(NULL)。

2. fclose()函数

与ASCII文件一样,应当关闭当前暂时不用的文件, fclose()函数的功能就是关闭指定的文件,调用格式为:

fclose(fp)

其中, fp为指向要关闭的文件的指针。

3.fread()和fwrite()

这两个文件操作函数实现内存缓冲区与文件之间指定数量的二进制数据的传递,因此需指定缓冲区和文件的指针,及读或写的数据量。一般调用格式:

fread(buffer,bn,dn,fp);

fwrite(buffer,bn,dn,fp);

其中:

buffer为指向缓冲区指针(应事先定义类型);

bn为每一个数据项所包含的字节数;

dn为每一次读写操作的数据项数;

fp为打开文件的指针。

81

第二篇 习题篇

82

相关推荐