配色: 字号:
谭浩强《C语言程序设计》考研考点讲义
2022-11-24 | 阅:  转:  |  分享 
  
考情分析及复习介绍(1)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第一章 C语言概述(14)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第二章 算法(19)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第三章 数据类型、运算符与表达式(33)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第四章 单的C程序设计(49)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第五章 择结构程序设计(60)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第六章 循环控制(69)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第七章 数组(80)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第八章 函数(99)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第九章 预处理命令(130)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第十章 指针(139)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第十一章 结构体与共同体(172)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第十二章 位运算(201)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

第十三章 文件(209)G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21 G21

谭浩强《C语言程序设计》考点精讲及复习思路

1    

考情分析及章节介绍

考情分析、复习方法

◆课程特点

知识点众多,不利于记忆,难于掌握

算法的概念及其表示;

数据类型、运算符与表达式;

程序设计结构;

数组;

函数;

指针;

预处理命令;

结构体、共同体、枚举类型;

位运算、位段;

文件。

书写形式类似、功能相近,容易混淆

给定以下几组定义,说明其含义:

1.intp;intp;

2.intp[n];intp[n];int(p)[n];

3.intp( );intp( );int(p)( );

关于typedef与#define的相似之处

     typedefintCOUNT;

     #defineCOUNTint

作用:都是用COUNT代表int。但事实上,二者是不同的。#define是在预编译时处理的,只能作

简单的字符串替换,而typedef是在编译时处理的,并不是作简单的字符串替换,而是声明一个类型。

知识点综合运用,难于理解

#include<stdio.h>

structstu{intx;inty;}p;

intdt[4]={10,20,30,40};

structstu

  a[4]={50,&dt[0],60,&dt[1],70,&dt[2],80,&dt[3]};

2    

main()



  p=a;

  printf("%d,",++p->x);

  printf("%d,",(++p)->x);

printf("%d\n",++(p->y));

C.是任意的    D.由所用机器的机器字长所决



运行情况输出:

51,60,21

◆出题思路:按内容分布、按题型分布探讨

按内容分布

按知识点的基础内容出题

例如:针对数据类型、运算符与表达式;

在C语言中,int、char和short种类型数据在内存中所占用的字节数(D)。

A.由用户自己定义    B.均为2个字节



例如:针对函数;

在C语言程序中,在一个源程序中,main函数的位置(C)。

A.C语言程序总是从第一个定义的函数开始执行

B.在C语言程序中,要调用的函数必须在main函数中定义

C.C语言程序总是从main函数开始执行

D.C语言程序中的main函数必须放在程序的开始部分

例如:针对循环结构; 

写出下面程序的输出结果(A)

 main(  )



Intn[2]={0},i,j,k=2;

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

for(j=0;j<k;j++)n[j]=n[i]+1;

 Printf(“%d\n”,n[k]);

   }

A.不确定的值

B.3

谭浩强《C语言程序设计》考点精讲及复习思路

3    

C.2

D.1

例如:综合选择结构、自增运算符;

#include<stdio.h>

main()



int x,y;

scanf(“%d”, &x);

if(x++<0)y=-1;

elseif(x==0)y=0;

elsey=1;

printf(“%d”,y);



如果从键盘上输入-1,则程序的输出是(A)。

A.-1     B.0     C.1     D.不确定的值

例如:综合选择分支结构、自增运算符;

#include<stdio.h>

main()



int x=1,y=0,a=0,b=0;

switch(x)



 case1:

switch(y)

 {

  case0:a++;break;

  case1:b++;break;

 }

case2:

  a++;b++;break;



printf(“a=%d,b=%d\n”, a,b);



4    

A.a=2,b=1   B.a=1,b=1   C.a=1,b=0   D.a=2,b=2

出题思路

◆按题型角度:填空题、选择题、程序补充、程序阅读、程序改错、程序设计

填空题:

描述“一个整数n能被3或7整除”的C语言逻辑表达式( )。

    (n%3==0)||(n%7==0)

以下程序的运行结果是(11)。

#defineADD(x,y)x+y

voidmain()



printf(“%d\n”,ADD(1,2)ADD(3,4));



按题型角度:知识点的基本内容及综合

选择题:单项

下列标识符中,(B)是合法的用户定义的标识符。

A.A#C     B.getch     C.void     D.ab

以下程序的运行结果是(B)            

voidmain()



inti=0,j=0,k=6;

if((++i)||(++j>0))k++;

printf(“%d,%d,%d\n”,i,j,k);

   }

A.0,0,6

B.1,0,7

C.1,1,7

D.0,1,7

选择题:多项

下面关于枚举类型的说法正确的是(B、C、D)。

A.可以为枚举元素赋值

B.枚举元素可以进行比较

C.枚举元素的值可以在类型定义时指定

D.枚举元素可以作为常量使用

谭浩强《C语言程序设计》考点精讲及复习思路

5    

程序补充:

从键盘上输入10个数,求其平均值。

main()



for(i=1,sum=0.0;i<11;i++)      scanf("%f",&f)

{sum=sum+f

 ________________;

 ________________;



Printf(“average=%f\n”,sum/10);

}scanf("%f",&f){sum=sum+f

以下函数的功能是:把字符串str2接在字符串str1的后面。

voidfun(charstr1,charstr2)



(p1+i)!=‘\0’

(p2++)inti=0;

charp1=str1,p2=str2;

while(____________)i++;    (p1+i)=\‘0’

for(;p2;i++)(p2++)

(p1+i)=___________;

(p1+i)=‘\0’;



(p1+i)=\‘0’

(p2++)

程序阅读:

写出程序的输出结果。

main()



4inta=3;





=(a<1)?a:1;    

printf(“%d”,a);

}      4

程序阅读:

6    

写出程序的输出结果。

voidmain()



  inti,b,k=0;

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

  {

    b=i%2;    

    while(b-->=0)k++;



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

}       8,-2

程序改错:

下面程序用以计算整数阶乘。

voidmain()



  inti,n;

  longnpp;

  scanf("%d",&n);

  npp=1;i=2;

  while(i++<=n)    while(i<=n)

   npp=nppi;npp=npp



(i-1);

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

}   

while(i<=n)

npp(i-1);

程序改错:

输入一个三位数,分别求这个数的个、十、百位输出。

voidmain( )



  intt,x,y,z;

  scanf("%d",t);    scanf(“%d”,&t);

  x=t/100;

  y=(t-x100)/10;y=(t-x100)%10;

  z=t%10;

谭浩强《C语言程序设计》考点精讲及复习思路

7    

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

}  

scanf(“%d”,&t);

y=(t-x100)%10;

◆按题型角度:算法及知识点的综合

程序设计:课后习题、综合性弱

编程输出10至100之间的所有素数。

编程输出100至999之间的所有水仙花数。

(水仙花数定义,如:153=13+53+33,则153为水仙花数)

设任意含有10个元素的一维数组A和指针变量p,请用指针与数组关系编程输出该数组元素

内容。

◆程序设计:综合性强

假设一个文本文件中保存着100个整数,请将这100个整数从大到小的顺序写到另一个文件中

去,并且在新文件中每10个整数占一行。源文件名和目标文件名通过命令行参数获取。

#include<stdio.h>

voidmain(intargc,charargv[])



FILEfin,fout;

inta[100],i,j,t;

If(argc!=3)



  printf(“Youforgottoenterafilename.\n”);

  return;



fin=fopen(argv[1],”r”);

fout=fopen(argv[2],”w”);

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

 fscanf(fin,”%d”,&a[i]);

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

 for(j=0;j<99-i;i++)

   if(a[j]>a[j+1]

   {t=a[j];a[j]=a[j+1];a[j+1]=t;}

8    

◆复习思路

从教材的角度(按章节顺序,较全面)(第一阶段)

从考题的角度(抓重点,找遗漏)(第二阶段)

从教材的角度(按章节顺序,较全面)(第一阶段)

认真复习每个知识点,掌握C语言规则。

例如:针对字符数组和字符指针变量

字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串

第1个字符的地址),决不是将字符串放到字符指针变量中。

对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。

char str[14];

str=″IloveChina!″;

而对字符指针变量,可以采用下面方法赋值:

chara;

a=″IloveChina!″;

对于容易混淆的知识点,善于归纳和总结

有关整型的数据类型

定义含义

int p;定义整型变量p

int p;p为指向整型数据的指针变量

int p[n];定义整型数组p,它有n个元素

intp[n];定义指针数组p,它由n个指向整型数据的指针元素组成

int (p)[n];p为指向含n个元素的一维数组的指针变量

int p();p为带回整型函数值的函数

int p();p为带回一个指针的函数,该指针指向整型数据

int (p)();p为指向函数的指针,该函数返回一个整型值

int p;p是一个指针变量,它指向一个指向整型数据的指针变量

谭浩强《C语言程序设计》考点精讲及复习思路

9    

培养认真分析的习惯,建立地址分配模型。

#include<stdio.h>

structstu{intx;inty;}p;

intdt[4]={10,20,30,40};

structstu

a[4]={50,&dt[0],60,&dt[1],70,&dt[2],80,&dt[3]};

  main()

  {

p=a;

printf("%d,",++p->x);

printf("%d,",(++p)->x);

printf("%d\n",++(p->y));

  }

运行情况输出:

51,60,21

10   

从考题的角度(抓重点,有遗漏)(第二阶段)

主要是出题几率小的知识点。

例如:下面符合要求的位段定义是(B)。

要求:1)a有两位;2)b有两位;3)c有两位;4)d有四位。

A.structbitfields

 {

  unsigneda,b,c:2;

  unsignedd:4;

  };

B.structbitfields

 {

  unsigneda:2,b:2;

  unsignedc:2,d:4;

 };

C.structbitfields

 {

  unsigneda,b,c:6;

  unsignedd:4;

  };

D.structbitfields

 {

  unsigneda,b:4;

  unsignedc,d:6;

  };

●按章节介绍知识点分布

重点内容必考;

数据类型、运算符与表达式、结构化程序设计、数组、函数、结构体、指针、文件;

主要内容

●算法

算法的概念、算法的特性、算法的表示

用自然语言表示算法

用流程图表示算法:基本程序设计结构

用N-S流程图表示算法

谭浩强《C语言程序设计》考点精讲及复习思路

11   

用伪代码表示算法

●数据类型、运算符与表达式

整型数据:整型常量的表示方法、整型变量、整型常量

浮点型数据:浮点型常量的表示方法、浮点型变量、浮点型常量

字符型数据:字符常量、字符变量、字符串常量

各类数值型数据间的混合运算

算术运算符和算术表达式

赋值运算符和赋值表达式

逗号运算符和逗号表达式

●C语言程序设计

赋值语句

数据输入输出

字符数据输入输出

格式输入与输出

●选择结构

关系运算符和关系表达式

逻辑运算符和逻辑表达式

if语句:If语句的基本形式、If语句的嵌套、条件运算符

switch语句

●循环控制

goto语句以及用goto语句构成循环

用while语句实现循环

用do-while语句实现循环

用for语句实现循环

循环的嵌套

break语句continue和语句

●数组

一维数组的定、引用、初始化

二维数组的定义、引用、初始化

字符数组的定义、引用、初始化

字符串和字符串结束标志

字符数组的输入输出

字符串处理函数

12   

●函数

函数定义的一般形式、函数参数和函数的返回值

函数的调用、函数原型、嵌套调用、递归调用

数组作为函数参数

局部变量和全局变量

变量的存储类别:动态存储方式与静态存储方式

auto变量、用static声明局部变量、register变量

用extern声明外部变量、用static声明外部变量

●预处理命令

宏定义:不带参数的宏定义、带参数的宏定义、带参数的宏和函数的区别

“文件包含”处理

条件编译

●指针

地址和指针

变量的指针和指向变量的指针变量

数组与指针

字符串与指针

指向函数的指针、返回指针值的函数

指针数组和指向指针的指针

void指针类型

●结构体与共同体

结构体变量的定义、引用、初始化

结构体数组

指向结构体类型数据的指针

用指针处理链表

共用体

枚举类型

用typedef定义类型

●位运算 

位运算符和位运算

位段

●文件

文件类型指针

谭浩强《C语言程序设计》考点精讲及复习思路

13   

文件的打开与关闭

文件的读写

文件的定位

出错的检测

文件输入输出

复习思路

知识点较多、知识点之间可以综合应用,复习难度大;

按章节顺序、全面掌握知识点,培养综合分析考题的能力;

从考题的角度、有重点地针对性地复习。

14   

第一章 C语言概述

本章考点

考点1 C语言出现的历史背景

C语言是国际上广泛流行的高级语言,C语言是在B语言的基础上发展起来的。

B(BCPL)语言是1970年由美国贝尔实验室设计的,并用于编写了第一个UNIX操作系统,优点:

精练,接近硬件;缺点:过于简单,数据无类型。

1975年UNIX第6版发布,C优点突出引起关注。

1977年出现《可移植C语言编译程序》,推动UNIX在各种机器上实现,C语言得到推广,其发展

相辅相成

1973年贝尔实验室的D.M.Ritchie在B语言的基础上设计出C语言,对B取长补短,并用之改写

原来用汇编编写的UNIX,但仅在贝尔实验室使用。



1978年影响深远的名著《TheCProgrammingLanguage》由BrianW.Kernighan和DennisM.Ritchie

合著,被称为标准C。

1983年,美国国家标准化协会(ANSI)根据C语言各种版本对C的发展和扩充,制定了新的标准

ANSIC,比标准C有了很大的发展。

1988年,K&R按照ANSIC修改了他们的《TheCProgrammingLanguage》。

1987年,ANSI公布新标准———87ANSIC。

1990年,国际标准化组织接受了87ANSIC为ISOC的标准(ISO9899—1990)。

1994年,ISO又修订了C语言标准。

目前,流行的C语言编译系统大多是以ANSIC为基础进行开发的。

说明:不同版本的C编译系统所实现的语言功能和语法规则略有差别,因此了解所用的C语言编

译系统的特点。

考点2 C语言的特点

(1)语言简洁、紧凑,使用方便、灵活。32个关键字、9种控制语句,程序形式自由。

(2)运算符丰富。34种运算符。

(3)数据类型丰富,具有现代语言的各种数据结构。

(4)具有结构化的控制语句,是完全模块化和结构化的语言。

(5)语法限制不太严格,程序设计自由度大。

谭浩强《C语言程序设计》考点精讲及复习思路

15   

(6)允许直接访问物理地址,能进行位操作,能实现汇编语言的大部分功能,可直接对硬件进行

操作。

(7)目标代码质量高,程序执行效率高。只比汇编程序生成的目标代码效率低10%-20%。

(8)程序可移植性好。基本上不做修改就能用于各种型号的计算机和各种操作系统。 

考点3 简单的C语言程序介绍

#include<stdio.h>            /文件包含/

void main()           /主函数/

{                 /函数体开始/

 printf("ThisisaCprogram.\n");   /输出语句/

}              /函数体结束/

说明:main-主函数名,void-函数类型

每个C程序必须有一个主函数main

{}是函数开始和结束的标志,不可省

每个C语句以分号结束

使用标准库函数时应在程序开头添加:#include<stdio.h>

例:求3个数中较大者。

#include<stdio.h>

voidmain()                /主函数/



 intmax(intx,inty);            /对被调用函数max的声明/

 inta,b,c;               /定义变量a、b、c/

 scanf(″%d,%d″,&a,&b);        /输入变量a和b的值/

 c=max(a,b);              /调用max函数,将得到的值赋给c/

 printf(″max=%d\\n″,c);       /输出c的值/



int max(intx,inty)



 intz;

 if (x>y) z=x;

 elsez=y;

 return(z);



例:求两数之和

16   

#include<stdio.h>

voidmain()     /求两数之和/



 inta,b,sum;    /声明,定义变量为整型/

 /以下3行为C语句/

 a=123;b=456;

 sum=a+b;

 printf(″sumis%d\n″,sum);



说明:/……/表示注释。注释对编译和运行不起作用,可以用汉字或英文字符表示,可以出

现在一行中的最右侧,也可以单独成为一行。

C程序:

(1)C程序是由函数构成的,使程序容易实现模块化。

(2)一个函数由两部分组成:

函数的首部:例3中的max函数首部

intmax(intx,inty)

函数体:花括号内的部分。若一个函数有多个花括号,则最外层的一对花括号为函数体的范围。

函数体包括两部分:

声明部分:inta,b,c;可缺省

执行部分:由若干个语句组成,可缺省

注意:

函数的声明部分和执行部分都可缺省,例如:

voiddump()





这是一个空函数,什么也不做,但是合法的函数。

总结:

(1)C程序总是从main函数开始执行的,与main函数的位置无关。

(2)C程序书写格式自由,一行内可以写几个语句,一个语句可以分写在多行上,C程序没有

行号。

(3)每个语句和数据声明的最后必须有一个分号。

(4)C语言本身没有输入输出语句。输入和输出的操作是由库函数scanf和printf等函数来完成

的。C对输入输出实行“函数化”。

谭浩强《C语言程序设计》考点精讲及复习思路

17   

考点4 运行C程序的步骤

一、运行C程序的步骤

(1)上机输入与编辑源程序

(2)对源程序进行编译

(3)与库函数连接

(4)运行可执行目标程序

二、上机运行C程序的方法

(1)目前使用的大多数C编译系统都是集成环境(IDE)的。可用不同的编译系统对C程序进行

操作。

(2)常用的有TurboC2.0、TurboC++3.0、VisualC++等。

(3)TurboC++3.0:是一个集成环境,具有方便、直观和易用的界面,虽然是DOS环境下的集成

环境,但是可以把启动TurboC++3.0集成环境的DOS执行文件tc.exe生成快捷方式,也可以用鼠

标操作。

(4)VisualC++:可以用VisualC++对C程序进行编译。

18   

檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪





















檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪





























本章小结

主要知识点:

C语言的特点:运算符丰富、模块化和结构化、允许直接访问物理地址、进行位操作、可移植

性好等

简单的C语言程序:函数、函数的首部、函数体等

运行C程序的步骤:输入与编辑源程序、编译、连接、运行等

谭浩强《C语言程序设计》考点精讲及复习思路

19   

第二章 算法

本章考点

考点1 算法的概念

一个程序应包括两个方面的内容:

对数据的描述:数据结构(datastructure)

对操作的描述:算法(algorithm)

著名计算机科学家沃思提出一个公式:

数据结构+算法=程序

完整的程序设计应该是:

数据结构+算法+程序设计方法+语言工具

广义地说,为解决一个问题而采取的方法和步骤,就称为“算法”。

对同一个问题,可有不同的解题方法和步骤。

例如求:



100

n=1



方法1:1+2,+3,+4,一直加到100,加99次

方法2:100+(1+99)+(2+98)+…+(49+51)+50

=100+49×100+50  加51次

为了有效地进行解题,不仅需要保证算法正确,还要考虑算法的质量,选择合适的算法。希望方

法简单,运算步骤少。

计算机算法可分为两大类别:

数值运算算法:求数值解,例如求方程的根、求函数的定积分等。

非数值运算:包括的面十分广泛,最常见的是用于事务管理领域,例如图书检索、人事管理、行车

调度管理等。

例1 求1×2×3×4×5

步骤1:先求1×2,得到结果2

步骤2:将步骤1得到的乘积2再乘以3,得到结果6

步骤3:将6再乘以4,得24

步骤4:将24再乘以5,得120

如果要求1×2×…×1000,则要写999个步骤

20   

可以设两个变量:一个变量代表被乘数,一个变量代表乘数。不另设变量存放乘积结果,而直接

将每一步骤的乘积放在被乘数变量中。设p为被乘数,i为乘数。用循环算法来求结果,算法可改写:

S1:使p=1

S2:使i=2

S3:使p×i,乘积仍放在变量p中,可表示为:p×i->p

S4:使i的值加1,即i+1->i。

S5:如果i不大于5,返回重新执行步骤S3以及其后的步骤S4和S5;否则,算法结束。最后得到p

的值就是5!的值。

用这种方法表示的算法具有通用性、灵活性。S3到S5组成一个循环,在实现算法时,要反复多次

执行S3,S4,S5等步骤,直到某一时刻,执行S5步骤时经过判断,乘数i已超过规定的数值而不返回

S3步骤为止。此时算法结束,变量p的值就是所求结果。

例题

例2 有50个学生,要求将成绩在80分以上者打印出来。设n表示学号,n1代表第一个学生学

号,ni代表第i个学生学号。用g代表学生成绩,gi代表第i个学生成绩,算法表示如下:

S1:1->i

S2:如果gi≥80,则打印ni和gi,否则不打印。

S3:i+1->i

S4:如果i≤50,返回S2,继续执行。否则算法结束。

变量i作为下标,用来控制序号(第几个学生,第几个成绩)。当i超过50时,表示已对50个学生

的成绩处理完毕,算法结束。

例3 判定2000~2500年中的每一年是否闰年,将结果输出。

分析:闰年的条件是:(1)能被4整除,但不能被100整除的年份都是闰年,如1996,2004年是闰

年;(2)能被100整除,又能被400整除的年份是闰年。如1600,2000年是闰年。不符合这两个条件

的年份不是闰年。

设y为被检测的年份,算法可表示如下:

S1:2000->y

S2:若y不能被4整除,则输出y“不是闰年”。然后转到S6。

S3:若y能被4整除,不能被100整除,则输出y“是闰年”。然后转到S6。

S4:若y能被100整除,又能被400整除,输出y“是闰年”,否则输出“不是闰年”。然后转到S6。

S5:输出y“不是闰年”。

S6:y+1->y

S7:当y≤2500时,转S2继续执行,如y>2500,算法停止。

例4 求1-

















+……-



99





100

谭浩强《C语言程序设计》考点精讲及复习思路

21   

算法如下:

S1:sign=1

S2:sum=1

S3:deno=2

S4:sign=(-1)×sign

S5:term=sign×(1/deno)

S6:sum=sum+term

S7:deno=deno+1

S8:若deno≤100返回S4,否则算法结束。

反复执行S4到S8步骤,直到分母大于100为止。一共执行了99次循环,向sum累加入了99个

分数。sum最后的值就是多项式的值。

例5 对一个大于或等于3的正整数,判断它是不是一个素数。

概念:所谓素数,是指除了1和该数本身之外,不能被其它任何整数整除的数。例如,13是素数。

因为它不能被2,3,4,…,12整除。

分析:判断一个数n(n≥3)是否素数的方法:将n作为被除数,将2到(n-1)各个整数轮流作为

除数,如果都不能被整除,则n为素数。

算法如下:

S1:输入n的值

S2:i=2(i作为除数)

S3:n被i除,得余数r

S4:如果r=0,表示n能被i整除,则打印n“不是素数”,算法结束。否则执行S5

S5:i+1->i

S6:如果i≤n-1,返回S3。否则打印n“是素数”,然后结束。

实际上,n不必被2到(n-1)的整数除,只需被2到n/2间整数除,甚至只需被2到 之间的整数

除即可。

考点2 算法的特性

一个算法应该具有以下特点:

有穷性:包含有限的操作步骤。

确定性:算法中的每一个步骤都应当是确定的。

有零个或多个输入:输入是指在执行算法时需要从外界取得必要的信息。

有一个或多个输出:算法的目的是为了求解,“解”就是输出。

有效性:算法中的每一个步骤都应当能有效地执行,并得到确定的结果。

考点3 算法的表示

22   

可以用不同的方法表示算法,常用的有:

自然语言

传统流程图

结构化流程图

伪代码

PAD图

一、用自然语言表示算法

自然语言就是人们日常使用的语言,可以是汉语或英语或其它语言。用自然语言表示通俗易懂,

但文字冗长,容易出现“歧义性”。自然语言表示的含义往往不大严格,要根据上下文才能判断其正确

含义,描述包含分支和循环的算法时也不很方便。因此,除了那些很简单的问题外,一般不用自然语

言描述算法。 

二、用流程图表示算法

美国国家标准化协会ANSI规定了一些常用的流程图符号:

 例6图

例6 将求5!的算法用流程图表示

例7 将例2的算法用流程图表示。打印50名学生中成绩在80分以上者

的学号和成绩。

谭浩强《C语言程序设计》考点精讲及复习思路

23   

例8 将例3判定闰年的算法用流程图表示

24   

例9 将例4的算法用流程图表示

例10 将例5判断素数的算法用流程图表示

(图见视频)

用流程图表示算法

总结:流程图是表示算法的较好的工具。一个流程图包括以下几部分:

(1)表示相应操作的框;

(2)带箭头的流程线;

(3)框内外必要的文字说明。

优点:用流程图表示算法要比用文字描述算法逻辑清晰、易于理解。

谭浩强《C语言程序设计》考点精讲及复习思路

25   

三、三种基本结构和改进的流程图

1.传统流程图的弊端

传统流程图用流程线指出各框的执行顺序,对流程线的使用没有严格限制。因此,使用者可以毫

不受限制地使流程随意地转向,使流程图变得毫无规律,阅读者要花很大精力去追踪流程,使人难以

理解算法的逻辑。如图:

缺点:难以阅读、修改,使算法的可靠性和可维护性难以保证。

解决办法:必须限制箭头的滥用,即不允许无规律地使流程随意转向,只能顺序地进行下去。

这种如同乱麻一样的算法称为BS型算法,意为一碗面条(ABowlofSpaghetti),乱无头绪。

2.三种基本结构

Bohra和Jacopini提出了以下三种基本结构:

顺序结构、选择结构、循环结构

用这三种基本结构作为表示一个良好算法的基本单元。

循环结构

26   

3.三种基本结构的共同特点:

(1)只有一个入口;

(2)只有一个出口;(请注意:一个菱形判断框有两个出口,而一个选择结构只有一个出口。不要

将菱形框的出口和选择结构的出口混淆。)

(3)结构内的每一部分都有机会被执行到;

(4)结构内不存在“死循环”(无终止的循环)。

不正确的流程表示:

总结:

由三种基本结构顺序组成的算法结构,可以解决任何复杂的问题。由基本结构所构成的算法属

于“结构化”的算法,它不存在无规律的转向,只在本基本结构内才允许存在分支和向前或向后的

跳转。

只要具有上述四个特点的都可以作为基本结构。可以自己定义基本结构,并由这些基本结构组

成结构化程序。下图符合基本结构的特点:

谭浩强《C语言程序设计》考点精讲及复习思路

27   

如下是一个多分支选择结构。虚线框内的结构是一个入口一个出口,并且有上述全部的四个特

点。由此构成的算法结构也是结构化的算法。可以认为这是由三种基本结构所派生出来的。

(图见视频)

四、用N-S流程图表示算法

1973年,美国学者I.Nassi和B.Shneiderman提出一种新的流程图形式,完全去掉了带箭头的流

程线。全部算法写在一个矩形框内,在该框内还可以包含其它的从属于它的框。这种流程图又称N

--S结构化流程图。

用三种N-S流程图中的基本框,可以组成复杂的N-S流程图。图中的A框或B框,可以是一

个简单的操作,也可以是三个基本结构之一。如下图:A框可以是一个选择结构,B框可以是一个循

环结构。

28   

例11 将例1的求5!算法用N-S图表示

例12 将例2的算法用N-S图表示。(打印50名学生中成绩高于80分的学号和成绩)

例13 将例3判定闰年的算法用N-S图表示

例14 将例4的算法用N-S图表示

谭浩强《C语言程序设计》考点精讲及复习思路

29   

例15 将例5判别素数的算法用N-S流程图表示

流程图不符合基本结构特点!不能分解为三种基本结构,无法直接用N-S流程图的三种基本结

构的符号来表示。因此,应当先作必要的变换。

(图见视频)

用N-S流程图表示算法

总结:一个结构化的算法是由一些基本结构顺序组成的。在基本结构之间不存在向前或向后的

跳转,流程的转移只存在于一个基本结构范围之内(如循环中流程的跳转);一个非结构化的算法可

以用一个等价的结构化算法代替,其功能不变。如果一个算法不能分解为若干个基本结构,则它必然

不是一个结构化的算法。

优点:比文字描述直观、形象、易于理解;比传统流程图紧凑易画。尤其是它废除了流程线,整个

算法结构是由各个基本结构按顺序组成的,N--S流程图中的上下顺序就是执行时的顺序。用N-

-S图表示的算法都是结构化的算法,因为它不可能出现流程无规律的跳转,而只能自上而下地顺序

执行。

五、用伪代码表示算法

概念:伪代码是用介于自然语言和计算机语言之间的文字和符号来描述算法。

特点:如同一篇文章一样,自上而下地写下来。每一行(或几行)表示一个基本操作。不用图形

符号,书写方便、格式紧凑,比较好懂,便于向计算机语言算法(即程序)过渡。

用处:适用于设计过程中需要反复修改时的流程描述。

例:“打印x的绝对值”的算法可以用伪代码表示为:

30   

IFxispositiveTHEN

   printx

  ELSE

   print-x

可以用汉字伪代码表示:

若x为正

打印x

否则

打印-x

也可以中英文混用。

例16 求5!。用伪代码表示算法

开始

置t的初值为1

置i的初值为2

当i<=5,执行下面操作:

使t=t×i

使i=i+1

{循环体到此结束}

输出t的值

结束

例17 输出50个学生中成绩高于80分者的学号和成绩。用伪代码表示算法:

BEGIN{算法开始}

   1->i

   whilei≤50

    {input and

    i+1->i}

   1->i

 whilei≤50

  {if≥80print and  i+1->i}

  END{算法结束}

六、用计算机语言表示算法

概念:用计算机实现算法。计算机是无法识别流程图和伪代码的。只有用计算机语言编写的程

序才能被计算机执行。因此在用流程图或伪代码描述出一个算法后,还要将它转换成计算机语言

谭浩强《C语言程序设计》考点精讲及复习思路

31   

程序。

特点:用计算机语言表示算法必须严格遵循所用的语言的语法规则,这是和伪代码不同的。

用处:要完成一件工作,包括设计算法和实现算法两个部分。设计算法的目的是为了实现算法。

例20 将例16表示的算法(求5!)用C语言表示。

#include<stdio.h>

voidmain()

{inti,t;

 t=1;

 i=2;

 while(i<=5)

  {t=tI;

  i=i+1;

  }

 printf(“%d\n”,t);



应当强调说明:

写出了C程序,仍然只是描述了算法,并未实现算法。只有运行程序才是实现算法。应该说,用

计算机语言表示的算法是计算机能够执行的算法。

考点4 结构化程序设计方法

一个结构化程序:就是用高级语言表示的结构化算法。用三种基本结构组成的程序必然是结构

化的程序,这种程序便于编写、便于阅读、便于修改和维护。

结构化程序设计强调程序设计风格和程序结构的规范化,提倡清晰的结构。

结构化程序设计方法的基本思路是:把一个复杂问题的求解过程分阶段进行,每个阶段处理的问

题都控制在人们容易理解和处理的范围内。

采取以下方法来保证得到结构化的程序:

自顶向下;

逐步细化;

模块化设计;

结构化编码。

两种不同的方法:

自顶向下,逐步细化;

自下而上,逐步积累。

(图见视频)

32   

用这种方法逐步分解,直到作者认为可以直接将各小段表达为文字语句为止。这种方法就叫做

“自顶向下,逐步细化”。

自顶向下,逐步细化方法的优点:

考虑周全,结构清晰,层次分明,作者容易写,读者容易看。如果发现某一部分中有一段内容不

妥,需要修改,只需找出该部分修改有关段落即可,与其它部分无关。

模块设计的方法:

(1)模块化设计的思想实际上是一种“分而治之”的思想,把一个大任务分为若干个子任务,每一

个子任务就相对简单。

(2)在拿到一个程序模块以后,根据程序模块的功能将它划分为若干个子模块,如果这些子模块

的规模还嫌大,还再可以划分为更小的模块。这个过程采用自顶向下方法来实现。

(3)子模块一般不超过50行。

(4)划分子模块时应注意模块的独立性,即:使一个模块完成一项功能,耦合性愈少愈好

檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪

























檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪



































本章小结

主要知识点:

程序、算法的概念:运算掌握简单的算法举例

算法的特性:有穷性、确定性、有效性等

算法的表示:用自然语言表示、流程图、N-S流程图、伪代码、三种基本结构的流程图及N-

S流程图等

结构化程序设计方法:自顶向下、模块化设计等

谭浩强《C语言程序设计》考点精讲及复习思路

33   

第三章 数据类型、运算符与表达式

本章考点

考点1 C的数据类型

C语言提供的数据类型:

指针类型

考点2 常量与变量

一、常量和符号常量

在程序运行过程中,其值不能被改变的量称为常量。

常量区分为不同的类型:

例1 符号常量的使用

#define

PRICE 30

#include<stdio.h>

voidmain()

 {

80   

第七章 数组

本章考点

考点1 一维数组的定义和引用

数组:是一组具有相同数据类型的数据的有序集合。

一、一维数组的定义

1.定义格式为:

类型说明符 数组名[常量表达式];

例如:inta[10];

表示定义了一个整形数组,数组名为a,此数组有10个元素。

2.说明:

(1)数组名定名规则和变量名相同,遵循标识符定名规则。

(2)在定义数组时,要指定数组中元素的个数,方括弧中的常量表达式用来表示元素的个数,即数

组长度。

例:a[10],表示a数组有10个元素,注意下标是从0开始的,这10个元素是a[0],a[1],a[2],a

[3],a[4],a[5],a[6],a[7],a[8],a[9]。注意:按上面的定义,不存在数组元素a[10]。

(3)常量表达式中可以包括常量和符号常量,但不能包含变量。即,C语言不允许对数组的大小

作动态定义,即数组的大小不依赖于程序运行过程中变量的值。例如,下面这样定义数组是不行的:

intn;

scanf(“%d″,&n); /在程序中临时输入数组的大小/

inta[n];

数组说明中其他常见的错误

①floata[0];/数组大小为0没有意义/

②intb(2)(3); /不能使用圆括号/

③intk,a[k]; /不能用变量说明数组大小/

3.一维数组在内存中的存放

一维数组:floatmark[100];

谭浩强《C语言程序设计》考点精讲及复习思路

81   

每个数据元素占用的字节数,就是基类型的字节数。

一个元素占4个字节。

二、一维数组元素的引用

1.数组元素的引用方式:

数组名[下标]

下标可以是整型常量或整型表达式。

例如:a[0]=a[5]+a[7]-a[23]

注意:定义数组时用到的“数组名[常量表达式]”和引用数组元素时用到的“数组名[下标]”是

有区别的。

例如∶inta[10];  /定义数组长度为10/

t=a[6];   /引用a数组中序号为6的元素。此时6不代表数组长度/

2.一维数组元素引用的程序实例

#include<stdio.h>

voidmain()



 inti,a[10];

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

   a[i]=i;

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

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

 printf("\n″);



运行结果如下:

9876543210

三、一维数组的初始化

1.对数组元素初始化的实现方法:

82   

(1)在定义数组时对数组元素赋以初值。例如:

inta[10]={0,1,2,3,4,5,6,7,8,9};

将数组元素的初值依次放在一对花括弧内。经过上面的定义和初始化之后:

a[0]=0,a[1]=1,a[2]=2,a[3]=3,a[4]=4,

a[5]=5,a[6]=6,a[7]=7,a[8]=8,a[9]=9

(2)可以只给一部分元素赋值。例如:

inta[10]={0,1,2,3,4};

定义a数组有10个元素,但花括弧内只提供5个初值,这表示只给前面5个元素赋初值,后5个

元素值为0。

(3)如果想使一个数组中全部元素值为0,可以写成

int a[10]={0,0,0,0,0,0,0,0,0,0};或

int a[10]={0};

不能写成:inta[10]={010};

这是与FORTRAN语言不同的,不能给数组整体赋初值。

(4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长度。例如:

inta[5]={1,2,3,4,5};

或 inta[]={1,2,3,4,5}; 

在第二种写法中,花括弧中有5个数,系统就会据此自动定义a数组的长度为5。但若数组长度

与提供初值的个数不相同,则数组长度不能省略。例如,想定义数组长度为10,就不能省略数组长度

的定义,而必须写成  

inta[10]={1,2,3,4,5}; 

只初始化前5个元素,后5个元素为0。

四、一维数组程序举例

举例1:用数组来处理,求解Fibonacci数列。

Fibonacci数列公式:a1=a2=1,an=an-1+an-2

程序实例:

#include<stdio.h>

voidmain()



   inti; intf[20]={1,1};

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

    f[i]=f[i-2]+f[i-1];

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

谭浩强《C语言程序设计》考点精讲及复习思路

83   

  {

     if(i%5==0)printf(″\n″);

     printf(″%12d″,f[i])

 }/For循环结束/

} /程序结束/

举例2:用起泡法对10个数排序(由小到大)。

起泡法的思路是:将相邻两个数比较,将小的调到前头

注意:经过第一趟(共5次比较与交换)后,最大的数9已“沉底”。然后进行对余下的前面5个

数第二趟比较。

注意:经过第二趟(共4次比较与交换)后,得到次大的数8。

如果有n个数,则要进行n-1趟比较。在第1趟比较中要进行n-1次两两比较,在第j趟比较

中要进行n-j次两两比较。

程序流程图如下:

#include<stdio.h>

voidmain()



84   

 inta[10];

 inti,j,t;

 printf(″input10numbers:\n″);

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

 scanf("%d",&a[i]);

 printf("\n");

 for(j=0;j<9;j++)

  for(i=0;i<9-j;i++)

   if(a[i]>a[i+1])

   {

    t=a[i];a[i]=a[i+1];

    a[i+1]=t;

   }

printf(″thesortednumbers:\n″);

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

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

printf(″\n″);



程序运行结果如下:

input10numbers:

10481265-76100-45123G21

thesortednumbers:

-76-4501481265100123

考点2 二维数组的定义和引用

一、二维数组的定义

二维数组定义的一般形式为

类型说明符数组名[常量表达式][常量表达式];

例:定义a为3×4(3行4列)的数组,b为5×10(5行10列)的数组。如下:

floata[3][4],b[5][10];

不能写成

floata[3,4],b[5,10];

注意:可以把二维数组看作是一种特殊的一维数组:它的元素又是一个一维数组。

例如:可以把a看作是一个一维数组,它有3个元素:a[0]、a[1]、a[2],每个元素又是一个包含4

谭浩强《C语言程序设计》考点精讲及复习思路

85   

个元素的一维数组。

二维数组在内存中的存放

二维数组中的元素在内存中的排列顺序是:按行存放,即先顺序存放第一行的元素,再存放第二

行的元素…

下图表示对a[3][4]数组存放的顺序

例:整型数组b[3][3]={{1,2,3},{4,5,6},{7,8,9}};

问题:有了二维数组的基础,那么多维数组如何定义呢?

定义三维数组: floata[2][3][4];

多维数组元素在内存中的排列顺序:第一维的下标变化最慢,最右边的下标变化最快。

二、二维数组的引用

二维数组元素的表示形式为:

数组名[下标][下标]

86   

例如:a[2][3]

下标可以是整型表达式,如a[2-1][22-1]

注意:不要写成a[2,3],a[2-1,22-1]形式

数组元素可以出现在表达式中,也可以被赋值,例如:b[1][2]=a[2][3]/2

在使用数组元素时,应该注意下标值应在已定义的数组大小的范围内。

常出现的错误有:

inta[3][4]; /定义a为3×4的数组/



a[3][4]=3;

三、二维数组的初始化

数据类型数组名[常量表达式1][常量表达式2]={初始化数据};

可以用下面4种方法对二维数组初始化

(1)分行给二维数组赋初值。如:

inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

(2)可以将所有数据写在一个花括弧内,按数组排列的顺序对各元素赋初值。如:

inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

(3)可以对部分元素赋初值。如

inta[3][4]={{1},{5},{9}};

1 0 0 0

5 0 0 0

9 0 0 0

也可以对各行中的某一元素赋初值,如

inta[3][4]={{1},{0,6},{0,0,0,11}};

1 0 0 0

0 6 0 0

0 0 0 11

也可以只对某几行元素赋初值。如

inta[3][4]={{1},{5,6}};

1 0 0 0

5 6 0 0

0 0 0 0

(4)如果对全部元素都赋初值,则定义数组时对第一维的长度可以不指定,但第二维的长度不能

省。如:

谭浩强《C语言程序设计》考点精讲及复习思路

87   

inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

等价于:

inta[][4]={1,2,3,4,5,6,7,8,9,10,11,12};

在定义时也可以只对部分元素赋初值而省略第一维的长度,但应分行赋初值。如:

inta[][4]={{0,0,3},{},{0,10}};

0 0  3 0

0 0  0 0

0 10 0 0

四、二维数组程序举例

例4 将一个二维数组行和列元素互换,存到另一个二维数组中。

#include<stdio.h>

voidmain()



  inta[2][3]={{1,2,3},{4,5,6}};

  intb[3][2],i,j;

  printf(″arraya:\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(″arrayb:\n″);

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

  {

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

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

    printf(″\n″);

  }

 }/程序结束/

运行结果如下:

88   

arraya:

  1 2 3

  4 5 6

arrayb:

  1 4

  2 5

  3 6

例5 有一个3×4的矩阵,要求编程序求出其中值最大的那个元素的值,以及其所在的行号和

列号。

用N-S流程图表示算法,如下:

程序如下:    

#include<stdio.h>

voidmain()



inti,j,row=0,colum=0,max;

inta[3][4]={{1,2,3,4},{9,8,7,6},

{-10,10,-5,2}};

max=a[0][0];

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

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

   if(a[i][j]>max)

{max=a[i][j];

row=i;

colum=j;



谭浩强《C语言程序设计》考点精讲及复习思路

89   

printf(″max=%d,row=%d,colum=%d\n″,

  max,row,colum);

  }/程序结束/

考点3 字符数组

一、字符数组的定义

定义方法与前面介绍的类似。例如:

charc[10];

c[0]=′I′;c[1]=′′;c[2]=′a′;

c[3]=′m′;c[4]=′′;c[5]=′h′;c[6]=′a′;

c[7]=′p′;c[8]=′p′;c[9]=′y′;

用来存放字符数据的数组是字符数组。字符数组中的一个元素存放一个字符。

二、字符数组的初始化

对字符数组初始化,最容易理解的方式是逐个字符赋给数组中各元素。如:

charc[10]={’I′,’′,’a′,’m′,’’,′h′,′a′,′p′,′p′,′y′};

如果在定义字符数组时不进行初始化,则数组中各元素的值是不可预料的。

如果花括弧中提供的初值个数(即字符个数)大于数组长度,则按语法错误处理。

如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动定为空

字符(即′\0′)。

例如:

charc[10]={′c′,′′,′p′,′r′,′o′,′g′,′r′,′a′,′m′};

(图见视频)

如果提供的初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值

个数确定数组长度。

例如:

charc[]={′I′,′′,′a′,′m′,′′,′h′,′a′,′p′,′p′,′y′};数组c的长度自动定为10。

也可以定义和初始化一个二维字符数组。

例如: 

106  

二、函数调用的方式

按函数在程序中出现的位置来分,可以有以下三种函数调用方式:

1.函数语句

把函数调用作为一个语句。如例1中的printstar(),这时不要求函数带回值,只要求函数完成一

定的操作。

2.函数表达式

函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参

加表达式的运算。例如:c=2max(a,b);

3.函数参数

函数调用作为一个函数的实参。例如:

m=max(a,max(b,c));

其中max(b,c)是一次函数调用,它的值作为max另一次调用的实参。m的值是a、b、c三者中

的最大者。又如: printf("%d",max(a,b));也是把max(a,b)作为printf函数的一个参数。

函数调用作为函数的参数,实质上也是函数表达式形式调用的一种,因为函数的参数本来就要求

是表达式形式。

三、对被调用函数的声明和函数原型

在一个函数中调用另一函数(即被调用函数)需要具备哪些条件呢 ?

(1)首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。但光有这一

条件还不够。

(2)如果使用库函数,还应该在本文件开头用#include命令将调用有关库函数时所需用到的信息

“包含”到本文件中来。

(3)如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同

一个文件中),应该在主调函数中对被调用的函数作声明。

函数原型的一般形式为

(1)函数类型函数名(参数类型1,参数类型2……);

谭浩强《C语言程序设计》考点精讲及复习思路

107  

(2)函数类型函数名(参数类型1参数名1,参数类型2参数名2,……);

声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用

时,编译系统能正确识别函数并检查调用是否合法。(例如函数名是否正确,实参与形参的类型和个

数是否一致)。

注意:函数的“定义”和“声明”不是一回事。函数的定义是指对函数功能的确立,包括指定函数

名,函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。而函数的声明的作用

则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统

按此进行对照检查。

例5 对被调用的函数作声明

include<stdio.h>

voidmain()

{  float add(floatx,floaty); 

/对被调用函数add的声明/

   float a,b,c;

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

   c=add(a,b);

   printf("sumis%f \n",c);



floatadd(floatx,floaty)   /函数首部/

{ floatz;              / 函数体/

    z=x+y;

    return(z);



如果被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系统已经先知道了

已定义函数的有关情况,会根据函数首部提供的信息对函数的调用作正确性检查。

改写例5

#include<stdio.h>

floatadd(floatx,floaty)   

/函数首部/

{ float z;              / 函数体/

    z=x+y;

    return(z);



108  

voidmain()



   float a,b,c;

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

   c=add(a,b);

   printf("sumis%f \n",c);



考点5 函数的嵌套调用

嵌套定义就是在定义一个函数时,其函数体内又包含另一个函数的完整定义。

C语言不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另

一个函数。

例6 用弦截法求方程f(x)=x3-5x2+16x-80=0的根

方法:

(1)取两个不同点x1,x2,如果f(x1)和f(x2)符号相反,则(x1,x2)区间内必有一个根。如果f

(x1)与f(x2)同符号,则应改变x1,x2,直到f(x1)、f(x2)异号为止。注意x1、x2的值不应差太大,以

保证(x1,x2)区间内只有一个根。

(2)连接(x1,f(x1))和(x2,f(x2))两点,此线(即弦)交x轴于x。

(3)若f(x)与f(x1)同符号,则根必在(x,x2)区间内,此时将x作为新的x1。如果f(x)与f(x2)

谭浩强《C语言程序设计》考点精讲及复习思路

109  

同符号,则表示根在(x1,x)区间内,将x作为新的x2。

(4)重复步骤(2)和(3),直到|f(x)|<ε为止,ε为一个很小的数,例如10-6.此时认为f

(x)≈0

N-S流程图

(图见视频)

分别用几个函数来实现各部分功能:

(1)用函数f(x)代表x的函数:x3-5x2+16x-80.

(2)用函数调用xpoint(x1,x2)来求(x1,f(x1))和(x2,f(x2))的连线与x轴的交点x的坐标。

(3)用函数调用root(x1,x2)来求(x1,x2)区间的那个实根。显然,执行root函数过程中要用到函

数xpoint,而执行xpoint函数过程中要用到f函数。

#include<stdio.h>

#include<math.h>

floatf(floatx)

/定义f函数,以实现f(x)=x3-5x2+16x-80/



 floaty;

 y=((x-5.0)x+16.0)x-80.0;

 return(y);



floatxpoint(floatx1,floatx2)

/定义xpoint函数,求出弦与x轴交点/



 floaty;

 y=(x1f(x2)-x2f(x1))/

    (f(x2)-f(x1));

 return(y);



floatroot(floatx1,floatx2)  

 /定义root函数,求近似根/



  floatx,y,y1;

  y1=f(x1);

  do{

110  

   x=xpoint(x1,x2);

   y=f(x);

   if(yy1>0)

   {y1=y;x1=x;}

   else{y2=y;x2=x;}

  }while(fabs(y)>=0.0001);

  return(x);



voidmain()/主函数/



  float x1,x2,f1,f2,x;

  do{

   printf("inputx1,x2:\n");

   scanf("%f,%f",&x1,&x2);

   f1=f(x1);

   f2=f(x2);

  }while(f1f2>=0);

  x=root(x1,x2);

  printf("Arootofequationis %8.4f\n",x);



考点6 函数的递归调用

在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言

的特点之一就在于允许函数的递归调用。例如:

intf(intx)



  inty,z;

谭浩强《C语言程序设计》考点精讲及复习思路

111  

  z=f(y);

  return(2z);

 }

例7 有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说

比第3个人大2岁。问第3个人,又说比第2个人大2岁。问第2个人,说比第1个人大2岁。最后

问第1个人,他说是10岁。请问第5个人多大。

age(5)=age(4)+2

age(4)=age(3)+2

age(3)=age(2)+2

age(2)=age(1)+2

age(1)=10

可以用数学公式表述如下:

age(n)=10      (n=1)

age(n-1)+2    (n>1)

可以用一个函数来描述上述递归过程:

intage(intn)    /求年龄的递归函数/

{ intc;/c用作存放函数的返回值的变量/

  if(n==1)c=10;

  elsec=age(n-1)+2;

  return(c);



用一个主函数调用age函数,求得第5人的年龄。

#include<stdio.h>

voidmain()



   printf(″%d″,age(5));



例8 用递归方法求n!

求n!也可以用递归方法,即5!等于4!×5,而4!=3!×4…1!=1。

可用下面的递归公式表示:

n!=1           (n=0,1)

n·(n-1)!     (n>1)

例9 Hanoi(汉诺)塔问题。这是一个古典的数学问题,是一个用递归方法解题的典型例子。问

112  

题是这样的:古代有一个梵塔,塔内有3个座A、B、C,开始时A座上有64个盘子,盘子大小不等,大的

在下,小的在上。现在想把这64个盘子从A座移到C座,但每次只允许移动一个盘,且在移动过程中

在3个座上都始终保持大盘在下,小盘在上。在移动过程中可以利用B座,要求编程序打印出移动的

步骤。

为便于理解,先分析将A座上3个盘子移到C座上的过程:

(1)将A座上2个盘子移到B座上(借助C);

(2)将A座上1个盘子移到C座上;

(3)将B座上2个盘子移到C座上(借助A)。

其中第(2)步可以直接实现。第1步又可用递归方法分解为:

1.1将A上1个盘子从A移到C;

1.2将A上1个盘子从A移到B;

1.3将C上1个盘子从C移到B。

由上面的分析可知:将n个盘子从A座移到C座可以分解为以下3个步骤:

(1)将A上n-1个盘借助C座先移到B座上。

(2)把A座上剩下的一个盘移到C座上。

(3)将n-1个盘从B座借助于A座移到C座上。

程序如下:

#include<stdio.h>

voidmain()



voidhanoi(intn,charone,chartwo,charthree);

/对hanoi函数的声明/

intm;

printf("inputthenumberofdiskes:");

scanf(“%d”,&m);

printf("Thesteptomoveing%ddiskes:\n",m);

hanoi(m,'A','B','C');



voidhanoi(intn,charone,chartwo,charthree)

/定义hanoi函数,将n个盘从one座借助two座,移到three座/



 voidmove(charx,chary);  /对move函数的声明/

 if(n==1)move(one,three);

谭浩强《C语言程序设计》考点精讲及复习思路

113  

 else

 { hanoi(n-1,one,three,two);

  move(one,three);

  hanoi(n-1,two,one,three);

 }



voidmove(charx,chary)     /定义move函数/



  printf(“%c-->%c\n",x,y);

 }

运行情况如下:

inputthenumberofdiskes:3G21

Thestepstonoving3diskes:

   A-->C

   A-->B

   C-->B

   A-->C

   B-->A

   B-->C

   A-->C

考点7 数组作为函数参数

一、数组元素作函数实参

由于实参可以是表达式,而数组元素可以是表达式的组成部分,因此数组元素当然可以作为函数

的实参,与用变量作实参一样,是单向传递,即“值传送”方式。

例10有两个数组a和b,各有10个元素,将它们对应地逐个相比(即a[0]与b[0]比,a[1]与b

[1]比等)。如果a数组中的元素大于b数组中的相应元素的数目多于b数组中元素大于a数组中相

应元素的数目(例如a[i]>b[i]6次,b[i]>a[i]3次,其中i每次为不同的值),则认为a数组大于b

数组,并分别统计出两个数组相应元素大于、等于、小于的次数。

#include<stdio.h>

voidmain()

{ intlarge(intx,inty);    / 函数声明/

  inta[10],b[10],i,n=0,m=0,k=0;

  printf(″enterarraya∶\n″);

114  

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

    scanf(″%d″,&a[i]);

  printf(″\n″);

  printf(″enterarrayb∶\n″);

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

    scanf(″%d″,&b[i]);

  printf(″\n″);

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

  { if(large(a[i],b[i])==1) n=n+1;

   elseif(large(a[i],b[i])==0)m=m+1;

   elsek=k+1;

  }

printf("a[i]>b[i]%dtimes\na[i]=b[i]%d

      times\na[i]<b[i]%dtimes\n",n,m,k);

 if(n>k)printf("arrayaislargerthanarrayb\n");

 elseif(n<k)printf("arrayaissmallerthanarrayb\n");

 else printf("arrayaisequaltoarrayb\n");



large(intx,inty)

{ intflag;

 if(x>y)flage=1;

 elseif(x<y)flag=-1;

 elseflag=0;

 return(flag);



运行情况如下:

  enterarraya:

1357986420G21

  enterarrayb:

   5 3 8 9G221G223 5 6 0 4G21

a[i]>b[i]4times

a[i]=b[i]1times

a[i]<b[i]5times

谭浩强《C语言程序设计》考点精讲及复习思路

115  

   arrayaissmallerthannarrayb

二、数组名作函数参数

可以用数组名作函数参数,此时形参应当用数组名或用指针变量。

例11有一维数组score,内放10个学生成绩,求平均成绩。

#include<stdio.h>

voidmain()

{float average(floatarray[10]);

  /函数声明/

  floatscore[10],aver;

inti;

printf(″input10scores:\n″);

for(i=0;i<10;i++=

     scanf(″%f″,&score[i]);

printf(″\n″);

aver=average(score);

printf(″averagescoreis%5.2f\n″,aver);



float average(floatarray[10])

{inti;

float aver,sum=array[0];

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

   sum=sum+array[i];

 aver=sum/10;

 return(aver);



运行情况如下:

input10scores:

  100567898.576879967.57597G21

averagescoreis83.40

例12 形参数组不定义长度

#include<stdio.h>

void main()

{ float average(floatarray[],intn)

116  

   float score_1[5]={98.5,97,91.5,60,55};

   floatscore_2[10]={67.5,89.5,99,69.5,

                77,89.5,76.5,54,60,99.5};

printf(“theaverageofclassAis%6.2f\n”,

         average(score_1,5));

  printf(“theaverageofclassBis%6.2f\n”,

         average(score_2,10));



float average(floatarray[],intn)

{ inti;

 float aver,sum=array[0];

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

   sum=sum+array[i];

  aver=sum/n;

 return(aver);



运行结果如下:

theaverageofclassAis80.40

TheaverageofclassBis 78.20

例13 用选择法对数组中10个整数按由小到大排序。所谓选择法就是先将10个数中最小的数

与a[0]对换;再将a[1]到a[9]中最小的数与a[1]对换……每比较一轮,找出一个未经排序的数中

最小的一个。共比较9轮。

a[0] a[1] a[2] a[3] a[4]

 3   6   1    9    4未排序时的情况

 1   6   3    9    4

将5个数中最小的数1与a[0]对换

 1   3   6    9    4

将余下的4个数中最小的数3与a[1]对换

 1   3   4    9    6

将余下的3个数中最小的数4与a[2]对换

 1   3   4    6    9

将余下的2个数中最小的数6与a[3]对换,                 

至此完成排序

谭浩强《C语言程序设计》考点精讲及复习思路

117  

程序实例

#include<stdio.h>

void main()

{ void sort(intarray[],intn);

   inta[10],i;

   printf(″enter the array\n″);

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

   scanf(″%d″,&a[i]);

   sort(a,10);

   printf(″the sortedarray∶\n″);

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

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

   printf(″\n″);

} 

void sort(intarray[],intn)

{ inti,j,k,t;

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

 {  k=i;

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

   if(array[j]<array[k])k=j;

t=array[k];

       array[k]=array[i];array[i]=t;





三、多维数组名作函数参数

14.程序如下:

#include<stdio.h>

void main()

{max_value(intarray[][4]);

 inta[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}};

 printf(″maxvalueis%d\n″,

          max_value(a));

}  

118  

max_value( intarray[][4])

{ inti,j,k,max;

  max=array[0][0];

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

 for(j=0;j<4;j++=

if(array[i][j]>max)

          max=array[i][j];

  return(max);

}  

运行结果如下:

Maxvalueis34

考点8 局部变量和全局变量

一、局部变量

在一个函数内部定义的变量是内部变量,它只在本函数范围内有效,也就是说只有在本函数内才

能使用它们,在此函数以外是不能使用这些变量的。这称为“局部变量”。

floatf1(inta)     /函数f1/

{intb,c;

…          a、b、c有效



charf2(intx,inty)   /函数f2/

{inti,j;        x、y、i、j有效



voidmain()       /主函数/

{intm,n;

…            m、n有效

}  

说明:

(1)主函数中定义的变量(m,n)也只在主函数中有效,而不因为在主函数中定义而在整个文件或

程序中有效。主函数也不能使用其他函数中定义的变量。

(2)不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。例如,上面在f1函

数中定义了变量b和c,倘若在f2函数中也定义变量b和c,它们在内存中占不同的单元,互不混淆。

(3)形式参数也是局部变量。例如上面f1函数中的形参a,也只在f1函数中有效。其他函数可

以调用f1函数,但不能引用f1函数的形参a。

谭浩强《C语言程序设计》考点精讲及复习思路

119  

(4)在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语

句也称为“分程序”或“程序块”。

voidmain()

{inta,b;



{intc;

c=a+b; c在此范围内有效 a,b在此范围内有效



    }



} 

intp=1,q=5;         /外部变量/

floatf1(inta)        /定义函数f1

二、全局变量

在函数内定义的变量是局部变量,而在函数之外定义的变量称为外部变量,外部变量是全局变量

(称全程变量)。全局变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始

到本源文件结束。



{intb,c;





charc1,c2;           /外部变量/

charf2(intx,inty)   /定义函数f2/

{inti,j;全局变量p,q的作用范围

 …        全局变量c1,c2的作用范围



voidmain()         /主函数/

{intm,n;



}  

例15 有一个一维数组,内放10个学生成绩,写一个函数,求出平均分、最高分和最低分。

#include<stdio.h>

float Max=0,Min=0;  /全局变量/

void main()

120  

{ float average(floatarray[],intn);

   float ave,score[10];

   inti;

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

    scanf(″%f″,&score[i]);

  ave=average(score,10);

 printf(“max=%6.2f\nmin=%6.2f\n

         average=%6.2f\n“,Max,Min,ave);

 }

floataverage(floatarray[],intn)

/定义函数,形参为数组/

{inti;

  float aver,sum=array[0];

  Max=Min=array[0];

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

{ if(array[i]>Max)

       Max=array[i];

  else if(array[i]<Min)

       Min=array[i];

 sum=sum+array[i];



  aver=sum/n;

  return(aver);



建议不在必要时不要使用全局变量,原因如下:

①全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。

②使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬时各个外部变量

的值。在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。

③它使函数的通用性降低,因为函数在执行时要依赖于其所在的外部变量。如果将一个函数移

到另一个文件中,还要将有关的外部变量及其值一起移过去。但若该外部变量与其他文件的变量同

名时,就会出现问题,降低了程序的可靠性和通用性。一般要求把C程序中的函数做成一个封闭体,

除了可以通过“实参———形参”的渠道与外界发生联系外,没有其他渠道。

例16外部变量与局部变量同名

谭浩强《C语言程序设计》考点精讲及复习思路

121  

#include<stdio.h>

inta=3,b=5;     /a,b为外部变量/ a,b作用范围

voidmain()

{ inta=8;   /a为局部变量/  局部变量a作用范围

  printf(″%d″,max(a,b)); 全局变量b的作用范围



max(inta,intb)     /a,b为局部变量/

{ intc;

  c=a>b?a∶b;       形参a、b作用范围

  return(c);



运行结果为8

考点9 变量的存储类别

一、动态存储方式与静态存储方式

前面已介绍了从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。那么从变

量值存在的时间(即生存期)角度来分,又可以分为静态存储方式和动态存储方式。

所谓静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。而动态存储方式则

是在程序运行期间根据需要进行动态的分配存储空间的方式。这个存储空间可以分为三部分:

程序区

静态存储区

动态存储区

在C语言中每一个变量和函数有两个属性:数据类型和数据的存储类别。对数据类型,读者已熟

悉(如整型、字符型等)。存储类别指的是数据在内存中存储的方式。存储方式分为两大类:静态存储

类和动态存储类。具体包含四种:自动的(auto),静态的(static),寄存器的(register),外部的(extern)。

根据变量的存储类别,可以知道变量的作用域和生存期。

二、auto变量

函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动

态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调

用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局

部变量称为自动变量。自动变量用关键字auto作存储类别的声明。例如:

intf(inta) /定义f函数,a为形参/

{auto intb,c=3; /定义b、c为自动变量/

122  

 …



三、用static声明局部变量

有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不

释放,在下一次该函数调用时,该变量已有值,就是上一次函数调用结束时的值。这时就应该指定该

局部变量为“静态局部变量”,用关键字static进行声明。通过下面简单的例子可以了解它的特点。

例17 考察静态局部变量的值。

#include<stdio.h>

void main()

{intf(int);

  inta=2,i;

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

    printf(″%d″,f(a));



intf(inta)

{ autointb=0;

  staticintc=3;

b=b+1;

 c=c+1;

  return(a+b+c);



对静态局部变量的说明:

(1)静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不

释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储区空间而不占静态存储区空间,

函数调用结束后即释放。

(2)对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每

次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。而对自动变量赋初值,不是在

编译时进行的,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。

(3)如在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型

变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。这是

由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,而所分配的单元中的

值是不确定的。

(4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。

谭浩强《C语言程序设计》考点精讲及复习思路

123  

例18 输出1到5的阶乘值。

#include<stdio.h>

void main()

{int fac(intn);

   inti;

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

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



Intfac(intn)

{ static intf=1;

f=fn;

   return(f);



四、register变量

一般情况下,变量(包括静态存储方式和动态存储方式)的值是存放在内存中的。当程序中用到

哪一个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。经过运算器进行运算,如

果需要存数,再从运算器将数据送到内存存放。

如果有一些变量使用频繁(例如在一个函数中执行10000次循环,每次循环中都要引用某局部变

量),则为存取变量的值要花费不少时间。为提高执行效率,C语言允许将局部变量的值放在CPU中

的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。由于对寄存器的存取速

度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量,用关键字

register作声明。例如,例19中的程序是输出1到n的阶乘的值。

例19 使用寄存器变量

#include<stdio.h>

voidmain()

{longfac(long);

longi,n;

scanf("%ld",&n);

124  

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

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



longfac(longn)

{registerlongi,f=1;  /定义寄存器变量/

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

 f=fi;

 return(f);



五、用extern声明外部变量

外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的

末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存

储区。

有时需要用extern来声明外部变量,以扩展外部变量的作用城。

1.在一个文件内声明外部变量

例20用extern声明外部变量,扩展它在程序文件中的作用域。

#include<stdio.h>

voidmain()

{intmax(int,int);/外部变量声明/

 extern A,B;

 printf("%d\n",max(A,B));



 intA=13,B=-8;/定义外部变量/

 intmax(intx,inty)/定义max函数/

{intz;

 z=x>y?x:y;

 return(z);



2.在多文件的程序中声明外部变量

例21 用extern将外部变量的作用域扩展到其他文件。本程序的作用是给定b的值,输入a和

m,求a×b和am的值。

文件file1.c中的内容为:

#include<stdio.h>

谭浩强《C语言程序设计》考点精讲及复习思路

125  

intA;          /定义外部变量/

voidmain()

{intpower(int);     /函数声明/

  intb=3,c,d,m;

  printf(″enterthenumberaanditspowerm:\n″);

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

c=Ab;

  printf(″%d%d=%d\n″,A,b,c);

d=power(m);

  printf(″%d%d=%d\n″,A,m,d);



文件file2.c中的内容为:

extern A;

/声明A为一个已定义的外部变量/

intpower(intn);



   inti,y=1;

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

y=A;

   return(y);



六、用static声明外部变量

有时在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以

在定义外部变量时加一个static声明。

例如:

file1.c             file2.c

staticintA;         externintA;

voidmain()        voidfun(intn)

{                   {…

…                  A=An;



七、关于变量的声明和定义

对变量而言,声明与定义的关系稍微复杂一些。在声明部分出现的变量有两种情况:一种是需要

126  

建立存储空间的(如:inta;),另一种是不需要建立存储空间的(如:externa;)。

前者称为“定义性声明”(definingdeclaration),或简称定义(definition)。后者称为“引用性声明”

(referencingdeclaration)。广义地说,声明包括定义,但并非所有的声明都是定义。对“inta;”而言,

它既是声明,又是定义。而对“externa;”而言,它是声明而不是定义。

一般为了叙述方便,把建立存储空间的声明称定义,而把不需要建立存储空间的声明称为声明。

显然这里指的声明是狭义的,即非定义性声明。

例如:

voidmain()

  {externA;    /是声明不是定义。声明A是一个已定义的外部变量/

   …

  }

  intA;

存储类别小结

(1)从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:

局部变量         |自动变量,即动态局部变量

(离开函数,值就消失)

|静态局部变量(离开函数,值仍保留)

|寄存器变量(离开函数,值就消失)

|(形式参数可以定义为自动变量或寄存器变量)

全局变量  |静态外部变量(只限本文件引用)

|外部变量

(即非静态的外部变量,允许其他文件引用)

(2)从变量存在的时间(生存期)来区分,有动态存储和静态存储两种类型。静态存储是程序整

个运行时间都存在,而动态存储则是在调用函数时临时分配单元。

动态存储   |自动变量(本函数内有效)

|寄存器变量(本函数内有效)

|形式参数(本函数内有效)

静态存储   |静态局部变量(函数内有效)

       |静态外部变量(本文件内有效)

|外部变量(其他文件可引用)

(3)从变量值存放的位置来区分,可分为:

内存中静态存储区  |静态局部变量

|静态外部变量(函数外部静态变量)

谭浩强《C语言程序设计》考点精讲及复习思路

127  

|外部变量(可为其他文件引用)

内存中动态存储区:自动变量和形式参数

CPU中的寄存器:寄存器变量

(4)关于作用域和生存期的概念。从前面叙述可以知道,对一个变量的性质可以从两个方面分

析,一是变量的作用域,一是变量值存在时间的长短,即生存期。前者是从空间的角度,后者是从时间

的角度。二者有联系但不是同一回事。

(5)static对局部变量和全局变量的作用不同。对局部变量来说,它使变量由动态存储方式改变

为静态存储方式。而对全局变量来说,它使变量局部化(局部于本文件),但仍为静态存储方式。从作

用域角度看,凡有static声明的,其作用域都是局限的,或者是局限于本函数内(静态局部变量),或者

局限于本文件内(静态外部变量)。

考点10 内部函数和外部函数

函数本质上是全局的,因为一个函数要被另外的函数调用,但是,也可以指定函数不能被其他文

件调用。根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数。

一、内部函数

如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名

和函数类型的前面加static。即

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

如 staticintfun(inta,intb)

二、外部函数

(1)在定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其

他文件调用。如函数首部可以写为externintfun(inta,intb)

这样,函数fun就可以为其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外

部函数。

(2)在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外

部函数

例22有一个字符串,内有若干个字符,今输入一个字符,要求程序将字符串中该字符删去。用外

部函数实现

File.c(文件1)

#include<stdio.h>

voidmain()

{externvoidenter_string(charstr[]);

 externvoiddetele_string(charstr[],charch);

128  

 externvoidprint_string(charstr[]);/以上3行声明在本函数中将要调用的在其他文件中定

义的3个函数/

 charc;

 charstr[80];

 scanf("%c",&c);

 detele_string(str,c);

 print_string(str);



file2.c(文件2)

 #include<stdio.h>

voidenter_string(charstr[80])/定义外部函数

                              enter-string/

{ gets(str);/向字符数组输入字符串/

} 

file3.c(文件3)

voiddelete_string(charstr[],charch) /定义外部函数delete_string/

{ inti,j;

 for(i=j=0;str[i]!='\0';i++)

if(str[i]!=ch)

 str[j++]=str[i];

 str[i]='\0';



file4.c(文件4)

#include<stdio.h>

voidprint_string(charstr[])



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

} 

运行情况如下:

abcdefgcG21   (输入str)

  cG21       (输入要删去的字符)

abdefg      (输出已删去指定字符的字符串)

谭浩强《C语言程序设计》考点精讲及复习思路

129  

檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪



























檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪



































本章小结

主要知识点:

函数的概念

函数的定义与调用

函数的递归调用

变量的作用域

函数的作用域

130  

第九章 预处理命令

本章考点

ANSIC标准规定可以在C源程序中加入一些“预处理命令”,以改进程序设计环境,提高编程

效率。

这些预处理命令是由ANSIC统一规定的,但是它不是C语言本身的组成部分,不能直接对它们

进行编译(因为编译程序不能识别它们)。必须在对程序进行通常的编译之前,先对程序中这些特殊

的命令进行“预处理”

经过预处理后程序可由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目

标代码。

C语言与其他高级语言的一个重要区别是可以使用预处理命令和具有预处理的功能。

C提供的预处理功能主要有以下三种:

1.宏定义

2.文件包含

3.条件编译

这些功能分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C语句相区别,

这些命令以符号“#”开头。例如:

#define

#include

考点1 宏定义

一、不带参数的宏定义

宏定义一般形式为:#define 标识符 字符串

例如:#define PI 3.1415926

宏定义的作用是在本程序文件中用指定的标识符PI来代替“3.1415926”这个字符串,在编译预

处理时,将程序中在该命令以后出现的所有的PI都用“3.1415926”代替。这种方法使用户能以一个

简单的名字代替一个长的字符串.

这个标识符(名字)称为“宏名”

在预编译时将宏名替换成字符串的过程称为“宏展开”。#define是宏定义命令。

例1使用不带参数的宏定义

谭浩强《C语言程序设计》考点精讲及复习思路

131  

#include<stdio.h>

#definePI3.1415926

 voidmain()

  {floatl,s,r,v;

  printf("inputradius:");

  scanf("%f",&r);

  l=2.0PIr;

  s=PIrr;

  v=4.0/3PIrrr;

printf("l=%10.4f\ns=%10.4f\nv=%10.4f\n",l,s,v);



运行情况如下:

inputradius:4G21

1=25.1328

s=50.2655

v=150.7966

说明:

(1)宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,也可用小写字母。

(2)使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。

(3)宏定义是用宏名代替一个字符串,只作简单置换,不作正确性检查。只有在编译已被宏展开

后的源程序时才会发现语法错误并报错。

(4)宏定义不是C语句,不必在行末加分号。如果加了分号则会连分号一起进行置换。

(5)#define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束。

通常,#define命令写在文件开头,函数之前,作为文件一部分,在此文件范围内有效。

(6)可以用#undef命令终止宏定义的作用域。

例2:

#defineG 9.8       _______

  voidmain() ↑

 {G的有效范围

 …

 }          -----↓----

  #undefG

  f1()

132  

  {



 }

在f1函数中,G不再代表9.8。这样可以灵活控制宏定义的作用范围。

(7)在进行宏定义时,可以引用已定义的宏名,可以层层置换。

例9.2在宏定义中引用已定义的宏名

#include<stdio.h>

#defineR3.0

#definePI3.1415926

#defineL2PIR

#defineSPIRR

void main(   )



printf("L=%f\nS=%f\n",L,S);



运行情况如下:

L=18.849556

S=28.274333

经过宏展开后,printf函数中的输出项L被展开为:

  23.14159263.0

S展开为

  3.14159263.03.0

printf函数调用语句展开为:

printf(“L=%F\NS=%f\n”,

23.14159263.0,3.14159263.03.0);

(8)对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换。

(9)宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不

分配内存空间。

二、带参数的宏定义

作用:不是进行简单的字符串替换,还要进行参数替换。

带参数的宏定义一般形式为:

#define宏名(参数表) 字符串

字符串中包含在括弧中所指定的参数

谭浩强《C语言程序设计》考点精讲及复习思路

133  

#define S(a,b)ab



area=S(3,2);

程序中用3和2分别代替宏定义中的形式参数a和b,用32代替S(3,2)。因此赋值语句展开

为:area=32

对带参的宏定义是这样展开置换的:

对带实参的宏(如S(3,2),则按#define命令行中指定的字符串从左到右进行置换。若串中包含

宏中的形参(如a、b),则将程序中相应的实参(可以是常量、变量或表达式)代替形参。如果宏定义中

的字符串中的字符不是参数字符(如ab中的号),则保留。这样就形成了置换的字符串。

例3 使用带参的宏

#include<stdio.h>

#definePI3.1415926

  #defineS(r)PIrr

  voidmain(  )

  {floata,area;

   a=3.6;

   area=S(a);  printf("r=%f\narea=%f\n",a,area);



赋值语句“area=S(a);”经宏展开后为:

area=3.1415926aa;

运行情况如下:

r=3.600000

area=40.715038

说明:

(1)对带参数的宏展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中的

形参。

(2)在宏定义时,在宏名与带参数的括弧之间不应加空格,否则将空格以后的字符都作为替代字

符串的一部分。

带参数的宏和函数的区别:

(1)函数调用时,先求出实参表达式的值,然后代入形参。而使用带参的宏只是进行简单的字符

134  

替换。

(2)函数调用是在程序运行时处理的,为形参分配临时的内存单元。而宏展开则是在编译前进行

的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。

(3)对函数中的实参和形参类型要求一致。而宏名无类型,它的参数也无类型,只是一个符号代

表,展开时代入指定的字符串即可。宏定义时,字符串可以是任何类型的数据。

(4)调用函数只可得到一个返回值,而用宏可以设法得到几个结果。

例4 通过宏展开得到若干个结果

#include<stdio.h>

#definePI3.1415926

#defineCIRCLE(R,L,S,V)L=2PIR;S=PIRR;V=4.0/3.0PIRRR

 voidmain()

  {floatr,l,s,v;

   scanf("%f",&r);

   CIRCLE(r,l,s,v);   printf("r=%6.2f,l=%6.2f,s=%6.2f,v=%6.2f\n",r,l,s,

v);

   }

对宏进行预编译,展开后的main函数如下:

voidmain()

{floatr,l,s,v;

 scanf("%f",&r);

l=23.1415926r;

 s=3.1515926rr;

 v=4.0/3/03.1415926rrr;  printf(”r=%6.2f,l=%6.2f,s=%6.2f,v=%6.2f\

n”,r,l,s,v);

} 

运行情况如下:

3.5G21

r=3.50,l=21.99,s=38.48,v=179.59

(5)使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不会使源

程序变长。

(6)宏替换不占运行时间,只占编译时间。而函数调用则占运行时间(分配单元、保留现场、值传

递、返回)。

如果善于利用宏定义,可以实现程序的简化,如事先将程序中的“输出格式”定义好,以减少在输

谭浩强《C语言程序设计》考点精讲及复习思路

135  

出语句中每次都要写出具体的输出格式的麻烦。

例5 通过宏展开得到若干个结果

#include<stdio.h>

#define PRprintf

#define NL"\n"

#define D"%d"

#define D1DNL

#define D2DDNL

#define D3DDDNL

#define D4DDDDNL

#define S"%s"

voidmain(  )

{inta,b,c,d;

 charstring[]="CHINA";

 a=1;b=2;c=3;d=4;

 PR(D1,a);

 PR(D2,a,b);

 PR(D3,a,b,c);

 PR(D4,a,b,c,d);

 PR(S,string);



运行时输出结果:



12

123

1234

CHINA

考点2 “文件包含”处理

所谓“文件包含”处理是指一个源文件可以将另外一个源文件的全部内容包含进来。C语言提供

了#include命令用来实现“文件包含”的操作。

其一般形式为:

#include"文件名"

或#include<文件名>

谭浩强《C语言程序设计》考点精讲及复习思路

157  

可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。

例16 定义字符指针

#include<stdio.h>

void main()

{charstring=″IloveChina!″;

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



对字符串中字符的存取,可以用下标方法,也可以用指针方法

例17 将字符串a复制为字符串b

#include<stdio.h>

voidmain()

{char a[]=″I am a boy.″,b[20];

  inti;

  for(i=0;(a+i)!=′\0′;i++)

  (b+i)=(a+i);

   (b+i)=′\0′;

  printf(″string a is:%s\n″,a);

  printf(″stringbis:″);

  for(i=0;b[i]!=′\0′;i++)

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

  printf(″\n″);



也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。

例18 用指针变量来处理例17问题。

#include<stdio.h>

void main()

{chara[]=″Iamaboy.″,b[20],p1,p2;

   inti;

p1=a;p2=b;

   for(;p1!=′\0′;p1++,p2++)

p2=p1;

p2=′\0′;

printf(″stringais:%s\n″,a);

158  

printf(″stringbis:″);

for(i=0;b[i]!=′\0′;i++)

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

printf(″\n″);



二、字符指针作函数参数

例19 用函数调用实现字符串的复制

(1)用字符数组作参数

#include<stdio.h>

void main()

{void copy_string(charfrom[],charto[]);

 chara[]=″Iamateacher.″;

 charb[]=″youareastudent.″;

 printf(“stringa=%s\nstringb=%s\n″,

a,b);

 printf("copystringatostringb:\n");

 copy_string(a,b);

 printf("\nstringa=%s\nstringb=%s\n",a,b); 



void copy_string(charfrom[],charto[])

{int i=0;

  while(from[i]!=′\0′)

 {to[i]=from[i];i++;}

谭浩强《C语言程序设计》考点精讲及复习思路

159  

to[i]=′\0′;



程序运行结果如下:

String a=l am a teacher.

Stringa b=you  a reastudent.

 copystringatostringb:

String a=l am a teacher.

Stringa b=l am a teacher.

(2)形参用字符指针变量

#include<stdio.h>

void main()

{voidcopy_string(charfrom,charto);

 chara=″Iam a teacher.″;

 charb=″youareastudent.″;

printf("stringa=%s\nstringb=%s\n″,a,b);

    printf("copystringatostringb:\n");

copy_string(a,b);

printf("\nstringa=%s\nstringb=%s\n",a,b); 

 }

void copy_string(charfrom,charto)

{for(;from!=′\0′;from++,to++)

to=from;

to=′\0′;



(3)对copy_string函数还可作简化

1.将copy_string函数改写为

void copy_string(charfrom,charto)

{while((to=from)!=′\0′)

{to++;from++;}



2.copy_string函数的函数体还可改为



  while((to++=from++)!=′\0′);

160  



3.copy_string函数的函数体还可写成



  while(from!=′\0′)

to++=from++;

  to=′\0′;



4.上面的while语句还可以进一步简化为下面的while语句:

while(to++=from++);

它与下面语句等价:

while((to++=from++)!=′\0′);

将from赋给to,如果赋值后的to值等于′\0′,则循环终止(′\0′已赋给to)

5.函数体中while语句也可以改用for语句:

for(;(to++=from++)!=0;);



for(;to++=from++;);

6.也可用指针变量,函数copy_string可写为

void copy_string(charfrom[],charto[])

{charp1,p2;

p1=from;p2=to;

while((p2++=p1++)!=′\0′);



三、对使用字符指针变量和字符数组的讨论

虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不

应混为一谈,主要有以下几点:

(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字

符串第1个字符的地址),决不是将字符串放到字符指针变量中。

(2)赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。

char str[14];

str=″IloveChina!″;

而对字符指针变量,可以采用下面方法赋值:

chara;

a=″IloveChina!″;

谭浩强《C语言程序设计》考点精讲及复习思路

161  

但注意赋给a的不是字符,而是字符串第一个元素的地址。

(3)对字符指针变量赋初值:

chara=″IloveChina!″;等价于

chara;

a=″IloveChina!″;

而对数组的初始化:

char str[14]={″Ilove China!″};

不能等价于

charstr[14];

str[]=″IloveChina!″;

(4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符

指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址也就是说,该指针变量可

以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。

{chara=″IloveChina!″

如: charstr[10];

    scanf(″%s″,str);是可以的。

而常有人用下面的方法,目的是想输入一个字符串,虽然一般也能运行,但这种方法是危险的:

    chara;

    scanf(″%s″,a);

应当这样:

chara,str[10];

a=str;

scanf(″%s″,a);

(5)指针变量的值是可以改变的,如:

例20 改变指针变量的值

#include<stdio.h>

voidmain()



a=a+7;

printf(″%s″,a);



需要说明,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所

指的字符串中的字符。

例21

162  

#include<stdio.h>

void main()

{chara=″IloveChina!″;

  inti;

  printf(“Thesixthcharacteris%c\n",a[5]);

  for(i=0;a[i]!=′\0′;i++)

  printf(″%c″,a[i]);



考点5 指向函数的指针

一、用函数指针变量调用函数

可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配

给一个入口地址。这个函数的入口地址就称为函数的指针。

例22 求a和b中的大者。先列出按一般方法的程序。

#include<stdio.h>

void main()

{int max(int,int);

inta,b,c;

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

c=max(a,b);

printf(″a=%d,b=%d,max=%d

        ″,a,b,c);



 int max(intx,inty)

{intz;

  if(x>y)z=x;

else   z=y;

  return(z);



将main函数改写为

#include<stdio.h>

void main()

{ int max(int,int);

   int(p)();

谭浩强《C语言程序设计》考点精讲及复习思路

163  

   inta,b,c;

p=max;

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

c=(p)(a,b);

 printf(″a=%d,b=%d,max=%d″,a,b,c);



二、用指向函数的指针作函数参数

函数指针变量常用的用途之一是把指针作为参数传递到其他函数。前面介绍过,函数的参数可

以是变量、指向变量的指针变量、数组名、指向数组的指针变量等。现在介绍指向函数的指针也可以

作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。

它的原理可以简述如下:有一个函数(假设函数名为sub),它有两个形参(x1和x2),定义x1和

x2为指向函数的指针变量。在调用函数sub时,实参为两个函数名f1和f2,给形参传递的是函数f1

和f2的地址。这样在函数sub中就可以调用f1和f2函数了。

实参函数名   f1     f2

          ↓     ↓

voidsub(int(x1)(int),int(x2)(int,int))

{ inta,b,i,j;

   a=(x1)(i);/调用f1函数/

    b=(x2)(i,j);/调用f2函数/

    …

    }

例23 设一个函数process,在调用它的时候,每次实现不同的功能。输入a和b两个数,第一次

调用process时找出a和b中大者,第二次找出其中小者,第三次求a与b之和。

#include<stdio.h>

voidmain()

{intmax(int,int);          /函数声明/

intmin(int,int);             /函数声明/

intadd(int,int);            /函数声明/

voidprocess(int,int,int(fun)());    /函数声明/

inta,b;

printf(″enteraandb:″);

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

printf(″max=″);

164  

  process(a,b,max);

printf(″min=″);

process(a,b,min);

printf(″sub=″);

process(a,b,add);



intmax(intx,inty)     /函数定义/

{intz;

if(x>y)z=x;

elsez=y;

return(z);



intmin(intx,inty)     /函数定义/ 

{intz;

if(x<y)z=x;

 elsez=y;

 return(z);



intadd(intx,inty)     /函数定义/

{intz;

z=x+y;

return(z);



void process(intx,inty,int(fun)(int,int))

{intresult;

result=(fun)(x,y);

printf(″%d\n″,result);



考点6 返回指针值的函数

一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与

以前类似,只是带回的值的类型是指针类型而已。

这种带回指针值的函数,一般定义形式为

类型名函数名(参数表列);

212  

关闭成功返回值为0;否则返回EOF(-1)

考点4:文件的读写

一、字符输入输出函数(fputc( )和fgetc( ))

fputs函数

函数调用:

fputc(ch,fp);

函数功能:

将字符(ch的值)输出到fp所指向的文件中去。

返回值:

如果输出成功,则返回值就是输出的字符;

如果输出失败,则返回一个EOF.

fgetc函数

函数调用:

ch=fgetc(fp);

函数功能:

从指定的文件读入一个字符,该文件必须是以读或读写方式打开的。

返回值:

读取成功一个字符,赋给ch。

如果遇到文件结束符,返回一个文件结束标志EOF。

常见的读取字符操作

从一个文本文件顺序读入字符并在屏幕上显示出来:

  ch=fgetc(fp);

 while(ch!=EOF)



   putchar(ch);

    ch=fgetc(fp);



注意:EOF不是可输出字符,因此不能在屏幕上显示。由于字符的ASCII码不可能出现-1,因此

EOF定义为-1是合适的。当读入的字符值等于-1时,表示读入的已不是正常的字符而是文件结

束符。

常见的读取字符操作

从一个二进制文件顺序读入字符:

while(!feof(fp))

谭浩强《C语言程序设计》考点精讲及复习思路

213  



    ch=fgetc(fp);



注意:ANSIC提供一个feof()函数来判断文件是否真的结束。如果是文件结束,函数feof(fp)的

值为1(真);否则为0(假)。以上也适用于文本文件的读取。

fputc和fgetc函数使用举例:

例1从键盘输入一些字符,逐个把它们送到磁盘上去,直到输入一个“#”为止。

#include<stdlib.h>

#include<stdio.h>

void main(void)

{ FILEfp;

   charch,filename[10];

   scanf("%s",filename);

   if((fp=fopen(filename,"w"))==NULL){

printf("cannotopenfile\n");

exit(0);     /终止程序/}

  ch=getchar();    /接收执行scanf语句时最后输入的回车符/

  ch=getchar();    /接收输入的第一个字符/

  while(ch!=‘#’){

       fputc(ch,fp);putchar(ch);

 ch=getchar();}

  fclose(fp);



运行情况如下:

File1.c(输入磁盘文件名)

Computerandc#(输入一个字符串)

computerandc(输出一个字符串)

fputc和fgetc函数使用举例:

例2将一个磁盘文件中的信息复制到另一个磁盘文件中。

#include<stdlib.h>

#include<stdio.h>

main()

214  

{FILEin,out;

charch,infile[10],outfile[10];

printf("Entertheinfilename:\n");

scanf("%s",infile);

printf("Entertheoutfilename:\n");

scanf("%s",outfile);

if((in=fopen(infile,"r"))==NULL)

{printf("cannotopeninfile\n");

   exit(0);}

if((out=fopen(outfile,"w"))==NULL)

{printf("cannotopenoutfile\n");

   exit(0);}

 while(!feof(in))

   fputc(fgetc(in),out);

 fclose(in);

 fclose(out);}

运行情况如下:

Enter theinfile name:

file1.c(输入原有磁盘文件名)

Enter the out  file1 name:

File2.c(输入新复制的磁盘文件名) 

程序运行结果是将file1.c文件中的内容复制到File2.c中去。

fputc和fgetc函数使用举例:

例2的改进:复制一个二进制文件,利用main参数,在输入命令行是将两个文件名输入。

#include<stdlib.h>

#include<stdio.h>

main(intargc,charargv[])

{FILEin,out;

charch;

if(argc!=3)

{  printf("Youforgottoenterafilename\n");

   exit(0);}

   if((in=fopen(argv[1],"rb"))==NULL)

谭浩强《C语言程序设计》考点精讲及复习思路

215  

   {  printf("cannotopeninfile\n");

     exit(0);}

  if((out=fopen(argv[2],"wb"))==NULL)

    {  printf("cannotopenoutfile\n");

       exit(0);}

    while(!feof(in))fputc(fgetc(in),out);

fclose(in);

fclose(out);}

运行方法:

设经编译连接后得到的可执行文件名为a.exe,则在DOS命令工作方式下,可以输入以下的命

令行:

C>a file1.c file2.c

file1.c和file2.c,分别输入到argv[1]和argv[2]中,argv[0]的内容为a,argc的值等于3。

二、数据块读写函数(fread( )和fwrite( ))

函数调用:

fread(buffer,size,count,fp);

fwrite(buffer,size,count,fp);

参数说明:

buffer:是一个指针。

对fread来说,它是读入数据的存放地址。

对fwrite来说,是要输出数据的地址(均指起始地址)。

size:要读写的字节数。

count:要进行读写多少个size字节的数据项。

fp:文件型指针。

使用举例:

若文件以二进制形式打开:

fread(f,4,2,fp);

此函数从fp所指向的文件中读入2个4个字节的数据,存储到数组f中。

使用举例:

若有如下结构类型:

structstudent_type

{charname[10];

intnum;

216  

intage;

charaddr[30];}stud[40];

可以用fread和fwrite来进行数据的操作:

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

fread(&stud[i],sizeof(structstudent-type),1,fp); 

for(i=0;i<40,i++)

fwrite(&stud[i],sizeof(structstudent-type),1,fp); 

使用举例:

例3从键盘输入4个学生的有关数据,然后把它们转存到磁盘文件上去。

#include<stdio.h>

#defineSIZE4

structstudent_type

{charname[10];

   intnum;

   intage;

   charaddr[15];

}stud[SIZE];/定义结构/

voidsave()

{FILEfp;

inti;

if((fp=fopen("stu-list","wb"))==NULL)

{printf("cannotopenfile\n");

 return;}

for(i=0;i<SIZE;i++)     /二进制写/

if(fwrite(&stud[i],sizeof(structstudent_type),1,fp)!=1)

printf(“filewriteerror\n”); /出错处理/

fclose(fp);}       /关闭文件/ 

main()

{inti;

for(i=0;i<SIZE;i++)     /从键盘读入学生信息/

scanf("%s%d%d%s",stud[i].name,&stud[i].num,

        &stud[i].age,stud[i].addr);

save();}          /调用save()保存学生信息/

谭浩强《C语言程序设计》考点精讲及复习思路

217  

运行情况如下:

输入4个学生的姓名、学号、年龄和地址:

 Zhang    1001  19  room-101

 Fun      1002  20  room-102

 Tan      1003  21  room-103

 Ling     1004  21  room-104

验证在磁盘文件“stu-list”中是否已存在此数据,用以下程序从“stu-list”文件中读入数据,然后

在屏幕上输出。

#include<stdio.h>

#defineSIZE4

structstudent_type

{  charname[10];

   intnum;

   intage;

   charaddr[15];

}stud[SIZE];

main()

{ inti;

  FILEfp;

  fp=fopen("stu-list","rb");

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

   {fread(&stud[i],sizeof(structstudent_type),1,fp);

   printf("%\-10s%4d   %4d %\-15s\n",stud[i].name,

    stud[i].num,stud[i].age,stud[i].addr);}

   fclose(fp);}

屏幕上显示出以下信息:

Zhang   1001   19  room-101

Fun     1002   20  room-102

Tan     1003   21  room-103

Ling   1004   21  room-104 

如果已有的数据已经以二进制形式存储在一个磁盘文件“stu-dat“中,要求从其中读入数据并

输出到“stu-dat”文件中,可以编写一个load函数,从磁盘文件中读二进制数据.

voidload()

218  

{FILEfp;inti;

 if((fp=fopen("stu-dat","rb"))==NULL)

  {printf("cannotopeninfile\n");

   return;}

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

  if(fread(&stud[i],sizeof(structstudent_type),1,fp)!=1)

   {if(feof(fp)){fclose(fp);return;}

 printf("filereaderror\n");}

 fclose(fp);}

三、格式化读写函数(fprintf()和fscanf())

函数调用:

fprintf(文件指针,格式字符串,输出表列);

fscanf (文件指针,格式字符串,输入表列);

函数功能:

从磁盘文件中读入或输出字符。

例:

fprintf(fp,”%d,%6.2f”,i,t);

fscanf(fp,”%d,%f”,&i,&t);

注意:

用fprintf和fscanf函数对磁盘文件读写,使用方便,容易理解,但由于在输入时要将ASCII码转换

为二进制形式,在输出时又要将二进制形式转换成字符,花费时间比较多。因此,在内存与磁盘频繁

交换数据的情况下,最好不用fprintf和fscanf函数,而用fread和fwrite函数。

三、其他读写函数

putw()和getw()

putw函数定义如下:

putw(intI,FILEfp)



 chars;

 s=&I;

 putc(s[0],fp);

 putc(s[1],fp);

 returni;

 }

谭浩强《C语言程序设计》考点精讲及复习思路

219  

gutw函数定义如下:

gutw(FILEfp)



 chars;

 s=char&i;

 s[0]=getc(fp);

 s[1]=getc(fp);

 returni;

 }

函数调用:

putw(inti,FILEfp);

inti=getw(FILEfp);

函数功能:

对磁盘文件中读写一个字(整数)。

例:

putw(10,fp);

i=getw(fp);

用户自定义读取其他类型数据的函数

向磁盘文件写一个实数(用二进制方式)的函数putfloat:

putfloat(floatnum,FILEfp)



chars;

intcount;

s=(char)#

for(count=0;count<4;count++)

putc(s[count],fp);



fgets函数

函数作用:

从指定文件读入一个字符串。

函数调用:

fgets(str,n,fp);

从fp指向的文件输入n-1个字符,在最后加一个’\0’

220  

返回值:

str的首地址

fputs函数

函数作用:

向指定的文件输出一个字符串。

函数调用:

fputs(“china”,fp);

第一个参数可以是字符串常量、字符数组名或字符型指针。字符串末尾的′\0′不输出。

返回值:

输入成功,返回值为0;

输入失败,返回EOF.

考点5:文件的定位

rewind函数

函数作用:

使位置指针重新返回文件的开头,无返回值。

应用举例:

例4 有一个磁盘文件,第一次将它的内容显示在屏幕上,第二次把它复制到另一文件上。

#include<stdio.h>

main()

{FILEfp1,fp2;

fp1=fopen("file1.c","r");

fp2=fopen("file2.c","w");

  while(!feof(fp1))putchar(getc(fp1));

  rewind(fp1);

  while(!feof(fp1))

    putc(getc(fp1),fp2);

  fclose(fp1);fclose(fp2);



顺序读写和随机读写

顺序读写:

位置指针按字节位置顺序移动。

随机读写:

读写完上一个字符(字节)后,并不一定要读写其后续的字符(字节),而可以读些文件中任意位

谭浩强《C语言程序设计》考点精讲及复习思路

221  

置上所需要的字符(字节)。

fseek函数(一般用于二进制文件)

函数功能:

改变文件的位置指针

函数调用形式:

fseek(文件类型指针,位移量,起始点)

起始点:文件开头      SEEK_SET  0

文件当前位置    SEEK_CUR  1

文件末尾      SEEK_END  2

位移量:以起始点为基点,向前移动的字节数。一般要求为long型

fseek函数应用举例

fseek(fp,100L,0);

将位置指针移到离文件头100个字节处

fseek(fp,50L,1);

将位置指针移到离当前位置50个字节处

fseek(fp,50L,2);

将位置指针从文件末尾处向后退50个字节

例5 在磁盘文件上存有10个学生的数据。要求将第1、3、5、7、9个学生数据输入计算机,并在

屏幕上显示出来。

#include<stdlib.h>

#include<stdio.h>

structstudent_type

{   charname[10];

    intnum;

    intage;

    charsex;

}stud[10];  main()

{ inti;

 FILE fp;

 if((fp=fopen("stud-dat","rb"))==NULL)

 {printf("cannotopenfile\n");

  exit(0);}

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

222  

 {fseek(fp,isizeof(structstudent_type),0);

 fread(&stud[i],sizeof(structstudent_type),1,fp);

printf(“%s%d%d%c\n”,stud[i].name,

   stud[i].num,stud[i].age,stud[i].sex);}

   fclose(fp)}

ftell函数

函数作用:

得到流式文件中的当前位置,用相对于文件开头的位移量来表示。

返回值:

返回当前位置,出错时返回-1L。

应用举例:

i=ftell(fp);

if(i==-1L)printf(“error\n”);

考点6:出错的检测

ferror函数

调用形式:

ferror(fp);

返回值:

返回0,表示未出错;返回非0,表示出错。

在调用一个输入输出函数后立即检查ferror函数的值,否则信息会丢失。在执行fopen函数时,

ferror函数的初始值自动置为0。

clearerr函数

调用形式:

clearerr(fp);

函数作用:

使文件错误标志和文件结束标志置为0。

只要出现错误标志,就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他一

个输入输出函数。

考点7:文件输入和输出小结

分类      函数名    功能     

打开文件    fopen()    打开文件

关闭文件    fclose()   关闭文件

文件定位    fseek()    改变文件位置指针的位置

谭浩强《C语言程序设计》考点精讲及复习思路

  223

rewind()  使文件位置指针重新至于文件开头

ftell()    返回文件位置指针的当前值

文件状态    feof()     若到文件末尾,函数值为真

ferror()   若对文件操作出错,函数值为真

clearerr()  使ferror和feof()函数值置零

文件读写    fgetc()    从指定文件取得一个字符     

fputc()    把字符输出到指定文件

fgets()从指定文件读取字符串

    

     

fputs()    把字符串输出到指定文件

getw()    从指定文件读取一个字(int型)

        putw()

        fread()

把一个字输出到指定文件

从指定文件中读取数据项

fwrite()    把数据项写到指定文件中

        fscanf()

        fprintf()































檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏































从指定文件按格式输入数据

按指定格式将数据写到指定文件中

檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏

本章小结

主要知识点:

文件类型指针

文件的打开与关闭

文件的读写

文件的定位

文件输入输出

献花(0)
+1
(本文系书香园分享首藏)