C语言期末复习资料

C语言期末复习资料
C语言期末复习资料

期末复习

第一部分:C中的数据

首先,从主调函数和被调函数(模块间)要处理的任务数据的角度来总结数据的知识点:

C语言中所讲授的数据有基本类型(整型、实型和字符型)和构建类型(数组、结构体、共用体、链表结点)之分。构建类型是由基本类型构建而成,因此我们有必要对基本类型的数据做以更细致的研究。

一.基本类型的数据

程序中的数据无非是以常量和变量两种形式描述。在这里我们不妨思考两个问题:(1)为什么要用变量?(2)常量经常用在什么地方?

(一)常量

对于基本类型常量我们要掌握:常量分几类?每类的书写规则是什么?

(1)整型常量:有十、八、十六进制三种表示形式。(3.3.1、3.3.3)

(2)实型常量:有小数和指数两种表示形式.(3.4.1、3.4.3)

(3)字符型常量:有普通字符(可以是ASCII码表中的任何一个字符)、转义字符(表示控制命令的转义字符和用八进制十六进制描述的转义字符)(3.5.1)(4)字符串常量(3.5.4)

(5)符号常量:实际上是不带参数的宏定义(3.2.1 、9.1.1)

对于常量部分的考察无非是辨别书写形式,字符类的常量特别注意转义字符的辨认。这就要求我们熟记书写规则。例如以下题目:

t-1:下面哪个是正确的整型常量表示

A)019 B)0X34g C)-123D)o56 E) 12,000

t-2: 下面哪个是错误的实型常量表示

A).23 B)3.8E-4.5C)45E12 D)92.45

t-3: 下面哪个是正确的字符常量表示:

A)”b”B)’\\’C)’\34’D)’\0x9’E)?0123?

t-4: 下面那个是正确的字符串常量表示:

A)?abc?B)“”C)“”D)“\0x120”

E)”abRT\0\\”

F)”A”

t-5: 字符串“ab034\0\\t\v\012”存储需要多少个字节?先组合转义字符,11个字节

t-6: puts(“ab034\0\\t\v\012”);的结果是ab034?strlen(“ab034\0\\t\v\012”)的值是5?

(二)变量

对于基本类型的变量,我们知道要使用变量必须先定义变量,定义的基本格式是:数据类型变量名;要正确定义变量就必须清楚两点(1)什么样的变量名才是合法的?(3.2.2)(2)如何用类型符来描述所要定义的变量,这就涉及到变量所分配的字节数、存储特点、有无符号及各自表示的数值范围等。

变量的三大类:

(1)整型变量(3.3.2)

分配字节:2字节(int/short) 4字节(long)(TC2.0)

存储特点:存储的是数值的补码形式

符号标识:signed unsigned

表示数值范围:表3-1

(2)实型变量(3.4.2)

分配字节:4字节(float) 8字节(double) 16字节(long double)

存储特点:图3-7

表示数值范围:表3-2

(3)字符型变量(3.5.2、3.5.3)

分配字节:1字节(char)

存储特点: 存储字符的ASCII值

符号标识:signed unsigned

表示数值范围:signed char: -128~+127

Unsigned char: 0~255

基本类型变量这部分知识主要的考察形式:

(1)变量名(标识符)的正确与否鉴别

(2)变量类型标识符的识别与应用

(3)变量定义语句中对变量进行初始化的形式正确与否鉴别(3.6)

例如以下题目:

t-7: 以下正确的变量标识符是:

A)3sum B)INT C)_3max D)case E)p&sum F)s_m_a

t-8: 以下正确的变量类型声明是:

A)signed int B)int long C)unsigned long D)short long

t-9: 下面程序段在tc2.0和cfree3.0运行时的输出各为-1 / 65535?

unsigned int a=65535;

printf("%d",a);

t-10: 以下正确的定义语句是:

A)int a=b=5 ; B)char ch=97 ;C)unsigned int x, y=-5; D)int a=3,b=5;

E)int c=3

(二)建构类型的数据

程序给出的任务数据可以是基本类型的,也可以是集合性质的。集合中的所有数据可以是同类型的即数组(7.1.1~7.1.3、7.2.1~7.2.3、7.3.1~7.3.4),也可以是不同类型的(例如学生记录)即结构体(11.1~11.5)。

在这部分知识中主要考察的形式有:

(1)数组及结构体的定义、初始化和引用

(2)计算集合(数组、结构体变量)存储所需要的空间

(3)数组定义时缺省形式的一些规则:

●在定义一维数组时,若给出了所有初始值则一维中括号内的数值可以省

略,由初始值的个数来确定其值。但若没有给出初始值,则不能省略中括

号内的值。例如:int a[]={1,2,3}; int b[]={3};都对。int a[]; 不对。

●二维数组的第2维列数任何情况下都不能缺省,第1维中的数值若在给

出初始值的按情况下可以缺省。例如int a[2][]={{1,2}{3,4}}不对。 int

a[][2]={1,2,3,4,5};正确。

(4)只有在数组定义时可以给数组整体赋值,不能单独用赋值运算“=”给数组整体赋值,特别是字符串数组。例如int a[3]; a={1,2,3}; char b[10];

b=”china”; char c[2][10]; c[0]=”chian”;c[1]=”japan”都不对, 只能是

int a[3]={1,2,3};char b[10]=”china”; char c[2][10]={“china”,”japan”};

(5)函数体内定义的数组名是地址常量,而由数组名表示的一些表达式也是常量表达式。如int a[3]={1,2,3}; a,a+i都是常量。int b[3][3]={..};

b,b[0],b+i,*(b+i)也都是常量形式。常量形式意味着该式只能出现在等

号的右边,而不能作为左值。

例如以下题目:

t-11: 以下正确的数组定义形式是:

A)int n; scanf(“%d”,&n); int a[n];

B)#define N 10 int a[N]; C)int b[3][4];

D)char b[ ][4];

t-12: 以下不正确的数组定义和初始化形式是:

A)int a[]={1,2,3}; B) int a[]={0}; C) int a[][3]={1,2,3,4};

D)int a[2][2]={{1,1},{2,2},{3,3}} E) char a[10]={“hello”}; F) char a[]=”hello”;

G)int a[2][]={{1,1},{2,2}}; H) char a[10]; a=”hello” ;

I)char a[2][10]; a[0]=”hello ”;a[1]=”tom”; J) char a[2][10]={“hello”, “tom”};

t-15: 若有数组int a[2][2]={1,2,3,4},则能引用数组元素4的形式为:

A) a[1][2] B) *(a+1) C) *(*(a+1)+1) D) *(a+3) E) a[1][1] F) *(a[1]+1)

t-16: 若有数组int a[3]={1,2,3};则能引用数组元素2的形式为:

A) a[1]B) *(a+1) C) &a[1] D) a+1t-11: 以下正确的数组定义形式是:

A)int n; scanf(“%d”,&n); int a[n];

B)#define N 10 int a[N];

C)int b[3][4];

D)char b[ ][4];

t-17: 若有如下程序段,则对于“TOM”串中首字符T的正确引用形式为:

struct A{

int age;

char name[10];

};

Struct A t[2]={1,”TOM”,2,”MIKE”},*p=t;

A)(*p).name B) t[2].name C)t[1].name[0] D)p->name[0]

第二部分:数据的输入输出

讨论完C中的数据我们再讨论各种数据的输入与输出。如果数据输入不当再完美的程序也不可能显示出预期的结果。这部分作业的输入出现的问题较多,我们要熟记输入规则但不能死记硬背。下面的这三对函数都包含在文件“stdio.h“中,因他们的输入输出终端是标准设备-键盘和显示器,因此称为标准的IO函数,标准IO函数的使用与缓冲区有密切的联系。学习本部分时最重要的是要理解输入缓冲区中数据流和输入函数格式匹配的输入机制(参见本章手册中的参考资料)

C中的输入输出是调用系统库函数来实现的,常见有三对库函数:

(1)s canf()/printf(): 可输入输出任何类型的数据。一次函数调用可以实现多个数据的IO。

(2)g ets()/puts():只对字符串数据进行输入输出操作。一次函数调用只可实现一个串的IO。

(3)g etchar()/putchar():只对字符数据进行输入和输出操作。一次函数调用只可实现一个字符的IO。

对于这几对函数的使用主要考察:

(1)s canf、printf的使用规则(4.5,可参考本章教学目标)

(2)g etchar和putchar的使用格式(4.4)

(3)g ets与scanf的使用区别

例如以下题目:

t-18:若有程序段int a; float x; char c[10];scanf(“%2d%f%s”,&a,&x,c);

当程序执行时从键盘输入 23456.78abcd ,a=23 x=456.78、和 c=”abcd”t-19:若有定义int a;float b; 则scanf(“a=%d;b=%f”,&a,&b);的输入形式是a=3;b=4.5

t-20:若有定义float x;则语句scanf(“%5.2f”,&x)是否正确?不正确

t-21:若有定义char a,b;对于scanf(“%c%c”,&a,&b);若输入A B,则a=A b=空格?

t-22:若用getchar()函数输入一个字符存储到字符变量ch中再将其输出,则以下正确的写法是:

A)getchar(ch); B) ch=getchar(); C) ch=putchar(); D) putchar(ch);

t-23: 若有语句scanf(“%s”,a); 程序运行时若输入hello tom!,则存储到a中的串是“hello”

t-24: 若有int a=97; float x=34.56; char c=?B?;则语句printf(“%c %d %d”,a,x,c)的输出是a 34 66

t-25: 若有程序段:

char a, b , s[10],t[10];

a=getchar();b=getchar();

gets(s);gets(t);

printf("a=%c,b=%c,s=%s,t=%s",a,b,s,t);

程序运行时若输入A

B

Hello

TOM

程序输出是a=A,b= ,s=B,t=HELLO

t-26: 若有如下定义,写出每个变量对应的格式符。

int a ; %d long int b; %ld unsigned long c;%lu

float x; %f long double y; %lf char ch; %c

t-27: 若要分别输入两个串“hello”和“tom”至数组s 和t中,下面四个程序段运行结果是否一样?可以有什么样的推论?若运行后输入的两个串以回车为结束符程序段1:程序段2:程序段3:

char s[10],t[10]; char s[10],t[10]; char s[10],t[10];

gets(s);gets(t); scanf(“%s”,s); gets(t); gets(s);

scanf(“%s”,t);

程序段4:

char s[10],t[10];

scanf(“%s”,s); scanf(“%s”,t);

程序段2结果不一样。Scanf和gets连用时对回车符的处理不同。Scanf 将回车符留在缓冲区中,而gets的回车符带走。

t-28: 第4章实验一的强化练习。

第三部分:函数之间对任务数据的处理方法

在编写多模块程序也即存在函数调用这样的程序时,我们必须首先设计模块之间任务数据的交接方法,即主调函数如何让被调函数得到要访问的任务数据。部分初学者对此不解,不明白为什么在子函数中不能通过直接引用主调函数中的变量名来访问主调函数中的任务数据,例如这样的题目:在main函数中定义了变量x和变量y并赋予值,而子函数sum的任务就是对主函数中的这两个数据求和,部分初学者也许认为在sum函数中直接用表达式x+y就可以求出主调函数中这两个变量的和了。

要明白这个错误的原因我们还是要对程序的模块化设计思想做以分析。这个思想的设计起因很简单,正如我们可以理解为什么学校里要划分院系,而院系里再分班;也正如为什么公司里都划分各个职能部门一样。变量的作用域属性和存储属性也就是

这个设计思想规则的体现。模块之间只能通过接口(实参/形参返回值)来互通信息。

为了保证模块内部信息(数据)的隐藏性,规定模块内部定义的变量也只能在本块内被引用,这就是作用域规则下的局部变量。在某些时候可能几个模块需要共享一些数据,这些数据可以被多个模块访问,这就是作用域规则下的全局变量。例如在战争游戏中,指挥部和后勤部都会需要战士的人数,指挥部用这个数目来调兵遣将,后勤部用这个数目来供给口粮和装备。

不同的模块之间存在信息共享(全局变量),而同一个模块的多次调用之间也可以共享某些信息(静态局部变量)。

对于作用域和存储类型这两部分内容要掌握:

(1)完整的变量定义格式:存储类型符数据类型符变量名;

(2)能根据变量的定义位置来判断哪些是局部变量,哪些是全局变量,他们各自的作用域范围是什么。

(3)能根据变量定义中的存储类型符来判断变量的存储类型,从而确定变量在内存生存期的起点和终点。

(4)所有的全局变量都是静态存储的,也即存储类型符只能是static/extern两种之一,这两个的区别是前者限定这个全局变量只能被当前文件所使用,而

后者声明的变量可以被其他文件所使用。若全局变量缺省存储类型符则默认

为extern。

(5)静态变量在使用前都应有初始值,若未显示赋值则系统有默认值-0或者NULL。

(6)局部变量有静态类型也有动态类型,若存储类型符缺省则应默认为auto。

常见题型:

t-28:读程序写结果:

#include

int fun(int);

int n=1;

void main()

{ int x=2, y=3;

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

{ int x=7;

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

}

y=fun(x);

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

y=fun(x);

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

}

int fun(int x)

{ static m=3;

m++;

n+=x;

printf("5:x=%d,m=%d,n=%d\n",x,m,n);

return m+n;

}

因变量作用域的限制使得接口传递信息时采取一种机制:将主调函数的实参值进行复制,形参到的是实参的副本,即传值调用的机制。但这种机制在处理集合数据时带来问题,我们不可能将集合中的每一个数据都做为一个实参,为了解决这个问题考虑将集合数据在内存中存储的首地址(是一个地址值)拷贝给子函数,这样子函数就

需要定义指针变量来接收传递来的地址值,这种处理机制称传址调用,子函数能利用指针变量间接访问到主调函数中的数据。对于返回结果是集合性质的数据也采用相同的处理方法,即返回这个结果集合的首地址,也即返回值是指针的函数功能。

若主调函数要子函数处理的是多个同类型的数据集合,例如多个一维整型数组,或者多个串,那么为了传递简便通常在主调函数中先对这多个数据集合做些处理,例如将这几个集合的首地址存储在一个指针数组中,这样传递实参时只要将这个指针数组的首地址传递给子函数,而子函数将定义一个二级指针来存放指针数组的首地址,继而通过这个二级指针来间接访问到这多个数据集合。

例如 char a[10]=”..”,b[10]=”..”,c[10]=”..”; char *p[3]={a,b,c}; 有函数调用fun(p,3),则形参定义相应为:(char **pp, int n ).

例如如下题型:

请分析下面函数调用表达式中的实参来写出对应的子函数中形参变量的定义,并说出这种调用形式下数据的访问方法(传值调用还是传址调用):

t-29:根据实参写出对应的形参变量的定义

(1)主调函数中若有定义:int x=2,y=3;

函数调用: fun(x,y);

形参定义:

(2)主调函数中若有定义:int x=2,y=3;

函数调用:fun(&x,&y);

形参定义:

(3)主调函数中若有定义:char x=?A?,y=?B?;

函数调用:fun(x,y);

形参定义:

(4) 主调函数中若有定义:int a[5]={1,2,3,4,5};

函数调用:fun(a[0]);

形参定义:

(5) 主调函数中若有定义:int a[5]={1,2,3,4,5};

函数调用:fun(&a[0]);或者fun(a);

形参定义:利用形参变量对数组元素的间接访问形式

(6) 主调函数中若有定义:int a[3][3]={1,2,3,4,5,6};

函数调用:fun(a[2][2]);

形参定义:

(7) 主调函数中若有定义:int a[3][3]={1,2,3,4,5,6};

函数调用:fun(&a[2][2]);

形参定义:利用形参变量对数组元素的间接访问形式(8) 主调函数中若有定义:int a[3][3]={1,2,3,4,5,6};

函数调用:fun(a,3,3);

形参定义:

(9) 主调函数中若有定义:int a[3][3]={1,2,3,4,5,6};

函数调用:fun(*a,9);或者fun(&a[0][0],9);或者fun(a[0],9)

形参定义:利用形参变量对数组元素的间接访问形式(10) 主调函数中若有定义:char a[3][10]={“hello”,”jack”,”tom”}; 函数调用:fun(a,3);

形参定义:利用形参变量对数组元素的间接访问形式(11) 主调函数中若有定义:

int a[5]={1,1,1,1,1};

int b[5]={2,2,2,2,2};

int c[5]={3,3,3,3,3};

int *p[3]={a,b,c};

函数调用:fun(p,3);

形参定义:利用形参变量对数组元素的间接访问形式(12) 主调函数中若有定义:

char a[5]=”hello”;

char b[5]=”jack”;

char *p[2]={a,b,c};

函数调用:fun(p,2);

形参定义:利用形参变量输出串jack

(13) 主调函数中若有定义:

char a[3][10]={”hello”, ”jack”,”tom”};

char *p[3]={a[0],a[1],a[2]};

函数调用:fun(p,3);

形参定义:利用形参变量输出串jack

(14)主调函数中若有定义:

Struct A {

Int x;

Char n[10];

};

Struct A m={1,”mike”};

函数调用:fun(m);

形参定义:

Struct A a[3]={1,”mike”,2,”jack”,3,”tom”};

函数调用:fun(a,3); 利用形参变量输出串mike

形参定义:

函数调用:fun(a[0])

形参定义:

在多模块的程序中应能写出或者找出函数的三要素-函数调用、函数声明(原型)、函数定义,函数声明根据情况可以缺省。或者给出其中一个要素能写出其它的两个要素,例如以下题目:

t-30:

函数原型: double fun(int,float);

函数调用:

函数定义头部:

t-31:

函数原型: char *fun(char *,char );

函数调用:

函数定义头部:

t-32:

函数原型: int fun(int [], int);或者 int fun(int *, int);

函数调用:

函数定义头部:

t-33:

函数原型: int fun(int (*)[3], int,int);

函数调用:

函数定义头部:

t-34:

函数原型: int fun(int **, int,int);

函数调用:

函数定义头部:

t-35:

函数原型:

函数调用: p=fun(a,b); /* 若以有定义 char *p,a[10],b[10];*/

函数定义头部:

t-36:

函数原型:

函数调用: sum=fun(a,5); /* 若以有定义 int sum,a[5]={..};*/

函数定义头部:

t-37:

函数原型:

函数调用: sum=fun(a,5,5); /* 若以有定义 int sum,a[5][5]={..};*/

函数定义头部:

t-38:

函数原型:

函数调用: fun(p,3); /* 若以有定义 char (*p)[3]={“..”,“..”,“..”}*/ 函数定义头部:

t-39:

函数原型:

函数调用: sum=fun(&a[0][0],9); /* 若以有定义 int sum, a[3][3]={...}*/ 函数定义头部:

t-40:

函数原型:

函数调用: fun(x); /* 若以有定义 struct A x={...}*/

函数定义头部:

t-41:

函数原型:

函数调用: fun(a,3); /* 若以有定义 struct A a[3]={...}*/

函数定义头部:

t-42:

函数原型:

函数调用:

函数定义头部:int sum(int (*p)[3],int m,int n)

t-43:

函数原型:

函数调用:

函数定义头部:int *sum(int *p)

在这部分内容中,对于给出的程序我们要非常明确程序采用的调用方式是传值还是传址,两个模块的接口处实参和形参的匹配,子函数返回值和主函数接收值的匹配。

第四部分:模块内部的任务实现(算法:解题思路)

多数题目在解题的过程中可能都会用到选择和循环,因此我们有必要首先对程序的这两种结构进行讨论。

选择结构的应用比较简单,只是把我们思维中的选择逻辑依葫芦画瓢的用计算机语句翻译出来就可以,而循环结构的应用对于初学者来说需要花费一定的精力,理解循环的构成,分析每一类题目循环的应用特点。

C语言中的分支语句(第5章)只提供了四种,其中三种if语句(简单if/两分支if/多分支if),一种switch语句。而应用这四种语句或嵌套能实现任何复杂情况

的选择逻辑。

按选择逻辑分类,可有两分支的选择逻辑和多分支的选择逻辑,两分支的逻辑可由简单if语句和两分支if语句实现,课堂上我们曾推论过这两种结构之间的转换关系,希望大家还记得。多分支的逻辑可有三种方法实现:(1)由两分支语句嵌套实现多分支(2)应用多分支if语句实现(3)应用switch语句实现。作业中我们曾以三分支的逻辑(分段函数)为例,要求大家尽可能写出由两分支嵌套实现的三分支各种写法,其中有一种是if...else...的else分支中又嵌套了一个if...else...,而这种逻辑结构实质上就是多分支if语句的变形,由此我们推论出(1)和(2)之间的关系,继而又用实例演示了(2)和(3)之间的转换关系,这样我们就把选择结构的所有形式有机的串联起来,它们之间是存在紧密联系的,明白了这一点在编写程序的时候用什么形式来编写选择逻辑就很清楚了。

对于循环的应用(第6章),我们在课堂讲解时首先用较简单的例题演示了三种循环语句(while循环,do-while循环,for循环)的使用,然后总结了循环的三个构成:循环变量的初始化、循环条件的设置、循环变量步长的修改。然后对用常见的应用循环的题目根据任务性质做了分类,有数值型求解题目(主要是数学类,如1+1/2!+1/3!+...等)和事务管理类题目(学生成绩统计等),对于数值求解类题目有迭代算法的,有穷举算法的,也有其他算法的,它们对于循环的应用形式各有千秋,在学习时应多归纳总结规律。

选择结构这部分内容中主要会考察:

(1)对于给定的应用选择结构的程序能清楚的找出配对关系。

(2)各分支语句之间的转换

(3)对于判断条件条件的理解,应会书写和计算条件

(4)对于switch语句的理解,switch中缺省break语句对结果的影响

(5)应用分支语句来编写程序。

(请参考第5章手册里的课后自测部分的题目)

例如以下题目:

t-44.若有a=1,b=3,c=5,d=4,x=6,则下面程序段执行后x的值是:

if(a

if(c

else if(b

else x=5;

t-45.将下列switch语句改写为if语句实现

swicth(x)

{ case 0: y=0;break;

case -1: y=-1;break;

case 1: y=1;break;

default: y=10;

}

t-46.将下列if语句改写为switch实现:

if(x<0) y=-1;

else if(x==0) y=0;

else y=1;

t-47.下面程序段输出是什么?

int x=1;

if(x=2) putchar(…*?);

else putchar(…#?);

t-48.下面程序段输出是什么?

Char a[10]=”hello”,b[10]=”hi,liou”;

Char *p1,*p2;

p1=a;p2=b+3;

if(*p1==*p2) putchar(…*?);

else putchar(…#?);

t-49: if(printf(…*?))的条件与下面哪一个等价

A)if(…1?) B)if(a=0)

t-50:下面程序段的输出是什么?

int x=3,s=0;

switch(x)

{ case 3:

case 2: s++;

default: s++;

case 1: s++;

}

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

循环结构这部分主要考察:

(1)三种循环的互相改写

(2)循环条件的判定与书写

(3)循环中执行的跳转(break/continue)

(4)嵌套循环的应用(特别是对一二维数组的访问)(5)W hile与do-while的区别

例如以下题目:

t-51:将下面的for循环改写成while循环:for(i=0;i<=10;i++)

{ if(i==6 ) continue;

printf(“*”);}

t-52: 以下程序段的输出是

int a=1,b=2,c=3,s=1;

while(c>b>a)

{ if(s++>3) printf(“%d”,s);}

printf(“%d”,s);

t-53:下面的循环条件哪个为真

A)char ch=?0? ; while(ch){…} B)int a=1;while(a=0){..} C) int a=0;

while(a++){…}

t-54:下面程序段的功能是什么?

char a[10],*p1=a,*p2=”hello”;

while( *p1++ = *p2++);

t-55: 下面程序段的输出是什么?

char a[10]=“abcdef”,*p1=a, b[10]=“ABC”,*p2=b;

while(*p1 && *p2) { *p1=*p2;p1++;p2++}

puts(a);puts(b); puts(p1);puts(p2);

t-56:若有定义int a[3][3]={1,2,3,4,5,6,7,8,9},i,j;则下面程序段的输出是什么?

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

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

{printf("%d",a[i][j]);if(i==j) break; }

printf("\n");

}

算法中使用的分支或者循环结构是构建框架的体现,而框架内就需要具体的语句来填充,算法描述中相当一部分语句是各种运算表达式语句,这就需要我们对C中的

相关主题
相关文档
最新文档