二级C语言考前冲刺串讲

来源:互联网 发布:人工智能手抄报 编辑:程序博客网 时间:2024/04/30 00:16

二级C语言考前冲刺串讲

二级C语言复习特点:

 一、了解试卷,胸中有数  

  综观近几年全国计算机等级二级基础部分和C语言程序设计的题目,笔试中大多数考题是与大纲要求的基本内容一致的,难度不高,但内容十分广泛,应牢固掌握。所以,全面复习非常重要。 

二、深刻理解,强化概念  

  对于计算机的基础知识和体系,应注意理解,切忌死记硬背。例如,可以把计算机硬件系统的结构图与实物对照着去理解:控制器和运算器常常集成在一起,称为CPU;存储器分为两种:内存和外存,只有内存才能与CPU直接打交道;所有这些部件又由总线将其联结为一体,这种计算机又称为总线式计算机;又如,计算机之所以称为电脑,就是因为计算机的硬件结构、处理问题的方式和人的大脑结构以及处理问题的方式基本一致。采用对照、总结、联想的方法来复习这些内容,比死记硬背效果要好得多。 

三、归纳整理,适当记忆  

  另一部分需要记忆的知识是计算机基础知识、基本概念。这些内容看似不起眼,但如果不适当加以记忆,考试时因此失分十分可惜。如:1946年至今,计算机发展已经历了四个时代,这四个时代是如何划分的?一个计算机系统都由哪几部分组成?各部分关系怎样?我们可以在理解的基础上归纳整理,适当记忆。需要适当记忆的内容对初学者来说还有许多,如在C语言中有几类不同类型的数据,各类数据在机器内部是如何存储的?C语言中的运算符都有哪些?运算的优先级别、结合方向怎样等。 

四、注重实践,融会贯通  

  计算机是一门理论性、实践性都很强的学科,对二级考试的参加者来说,基础理论方面要求不是特别深入,卷面中的题目大多数都可以上机实践。因此,注重实践,更显得重要。考生应在对基本知识理解的同时注意多上机实践,通过实践,将所学知识融会贯通。 

五、多做练习,查漏补缺 

  在认真地学完考试指定用书后,你可能已雄心勃勃,充满信心。但千万别高兴过早。再找一些题集,认真地测试一下,一则可以看看你究竟学习得如何,二则可以查漏补缺,将还没有掌握的内容补起来,以备正式考试时万无一失。即使在平时,也应这样。多做练习,多加思考,在解题中下功夫,是学好计算机的关键。我国著名数学家华罗庚说过:“学数学不做练习,好比入宝山而空返。”学习计算机也是同样的道理。

第一章 概述

1 C语言的特点

     语言简洁、紧凑,使用方便、灵活。共有32个关键字,9种控制语句。

C语言中的32个关键字

auto :声明自动变量 一般不使用

double :声明双精度变量或函数

int 声明整型变量或函数

struct:声明结构体变量或函数

break:跳出当前循环

else :条件语句否定分支(与 if 连用)

long :声明长整型变量或函数

switch :用于开关语句

case:开关语句分支

enum :声明枚举类型

register:声明寄存器变量

typedef:用以给数据类型取别名(当然还有其他作用)

char :声明字符型变量或函数

extern:声明变量是在其他文件中声明(也可以看做是引用变量)

return :子程序返回语句(可以带参数,也看不带参数)

union:声明联合数据类型

const :声明只读变量

float:声明浮点型变量或函数

short :声明短整型变量或函数

unsigned:声明无符号类型变量或函数

continue:结束当前循环,开始下一轮循环

for:一种循环语句(可意会不可言传)

signed:声明有符号类型变量或函数

void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)

default:开关语句中的“其他”分支

goto:无条件跳转语句

sizeof:计算数据类型长度

volatile:说明变量在程序执行中可被隐含地改变

do :循环语句的循环体

while :循环语句的循环条件

static :声明静态变量

if :条件语句

C语言中的9种控制语句:

 if() ~ else~        for()~        while()~      do~while()       continue        break     switch        goto        return

②运算符丰富,公有34种运算符。

C语言中的34运算符:

算数运算符( + - * / % )

关系运算符( > < == >= <= !=

逻辑运算符( ! && ||

位运算符( << >> ~ | ^ &

赋值运算符( =

条件运算符( ? :

逗号运算符( ,   逗号表达式的求解过程:先求解表达式1,再求解表达式2,整个表达式的值是表达式2的值。

指针运算符( * &

求字节数( sizeof

强制类型转换(类型)

分量运算符( . ->

下标运算符( [ ]

其它运算符( 如函数调用运算符( )

自增自减运算符( ++ -- )注意:++ii++的不同之处,++i使用i之前先使i加1,i++使用i之后,使i加1。

③数据结构丰富,数据类型有:整型、实型、字符型、数组、指针、结构体、共用体等。

④具有结构化的控制语句(如ifelsewhiledowhileswitchfor

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

⑥允许直接访问物理地址,能进行位(bit)操作,可以直接对硬件操作。

⑦生成目标代码质量高,程序执行效率高。

⑧可移植性好。

2 C语言的用途

C虽不擅长科学计算和管理领域,但对操作系统和系统实用程序以及对硬件进行操作方面,C有明显的优势。现在很多大型应用软件

也用C编写。

3、算法

闰年的条件是:

①能被4整除,但不能被100整除的年份;

②能被100整除,又能被400整除的年份。

算法的特性:

①有穷性;②确定性;③有零个或多个输入;④有一个或多个输出;⑤有效性。

好好利用伪代码写算法。

程序举例:

#include "stdio.h"

main()/*main为主函数*/

 

{

    printf("welcome to xi'an YDM computer school. /n");

    /*printf函数来输出我们想要输出的内容,如welcome to xi'an YDM computer school */

    getch();/*TC环境下可以省略这句*/

}

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

1 C的数据类型

C的数据类型包括:整型、字符型、实型或浮点型(单精度和双精度)、枚举类型、数组类型、结构体类型、共用体类型、指针类型和空类型。

2 常量与变量

常量其值不可改变,符号常量名通常用大写。变量其值可以改变,变量名只能由字母、数字和下划线组成,且第一个字符必须为字母或下划线。否则为不合法的变量名。变量在编译时为其分配相应存储单元。

3 整型数据

整型常量的表示方法:十进制不用说了,八进制以0开头,如0123,十六进制以0x开头,如0x1e

整型变量分为:基本型(int)、短整型(short int)、长整型(long int)和无符号型。不同机器上各类数据所占内存字节数不同,一般int型为2个字节,long型为4个字节。

4 实型数据

实型常量表示形式:十进制形式由数字和小数点组成(必须有小数点),如:0.12.123123.0.0等。指数形式如123e3代表123×10的三次方。实型变量分为单精度(float)和双精度(double)两类。在一般系统中float型占4字节,7位有效数字,double型占8字节,15~16

位有效数字。

5 字符型数据

字符变量用单引号括起来,如'a','b'等。还有一些是特殊的字符常量,如'/n','/t'等。分别代表换行和横向跳格。

字符变量以char 来定义,一个变量只能存放一个字符常量。

字符串常量是由双引号括起来的字符序列。这里一定要注意'a'"a"的不同,前者为字符常量,后者为字符串常量,c规定:每个字符串的结尾加一个结束标志'/0',实际上"a"包含两个字符:'a''/0'

6 数值型数据间的混合运算

整型、字符型、实型数据间可以混合运算,运算时不同类型数据要转换成同一类型再运算,转换规则:

char,short -> int -> unsigned -> long -> double <- float

7 运算符和表达式

算数运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、条件运算符、逗号运算符、指针运算符、求字节数( sizeof

强制类型转换(类型)、分量运算符( . -> )、下标运算符( [ ] )、 其它运算符( 如函数调用运算符( )

自增自减运算符( ++ -- )注意:++ii++的不同之处,++i使用i之前先使i加1,i++使用i之后,使i加1。

程序举例:

 

第三章 最简单的c程序设计

1.c的9种控制语句:

if() ~ else~           for()~         while()~         do~while()          continue     break    switch         goto           return

程序的三种基本结构:顺序结构,选择结构,循环结构

2.数据输出

c语言不提供输入输出语句,输入输出操作是由c的库函数完成。但要包含头文件stdio.h

putchar( ) 向终端输出一个字符

printf( )的格式字符:

d格式符 用来输出十进制整数

%d 按整型数据的实际长度输出

%md 使输出长度为m,如果数据长度小于m,则左补空格,如果大于m,则输出实际长度

%ld 输出长整型数据

o格式符 以八进制形式输出整数

x格式符 以十六进制形式输出整数

u格式符 用来输出unsigned型数据,以十进制形式输出

c格式符 用来输出一个字符

s格式符 输出一个字符串

%s 输出实际长度字符串

%ms 输出的串占m列,如果串长度小于m,左补空格,如果大于m,实际输出

%-ms输出的串占m列,如果串长度小于m,右补空格,

%m.ns 输出占m列,但只取字符串中左端n个字符并靠右对齐

%-m.ns mn含义同上,靠左对齐,如果n>m,则m自动取n

f格式符 以小数形式输出实数

%f 整数部分全部输出,小数部分输出6

%m.nf 输出数据共占m列,其中有n位小数。如果数值长度小于m,左补空格

%-m.nf 同上,右补空格

e格式符 以指数形式输出实数

%e 系统指定6位小数,5位指数(e+002 )

g格式符 输出实数,根据数值大小,自动选f格式或e格式

3.数据输入

getchar( ) 从终端输入一个字符

scanf( 格式控制,地址列表) 标准C scanf中不使用%u,对于unsigned型数据,以%d%o%x输入。%后的*,用来跳过它相应的数据。输入数据时不能规定精度如scanf( "%7.2f", &a );是不合法的。

 

转义字符及其表作用

字符形式    含义                              ASCII代码

/n          换行,当前位置移到下一行开头      10

/t          水平制表(跳到下一个tab位置)        9

/b          退格,将当前位置移到前一列        8

/r          回车,将当前位置移到本行开头      13

/f          换页,将当前位置移到下页开头      12

//          反斜杠字符"/"                       92

/'          单引号字符                         39

/"          双引号字符                         34

/ddd        138进制数所代表的字符

/xhh        1216进制数所代表的字符

程序举例:

 

 

第四章 逻辑运算和判断选取控制

1 关系运算符:

c提供6种关系运算符(> < <= >= == != )前四种优先级高于后两种。

2 If语句

C提供了三种形式的if语句

If(表达式) 语句

If(表达式) 语句1 else 语句2

If(表达式1) 语句1

Else if(表达式2) 语句2

else 语句n

3 条件运算符

(a>b)?a:b 条件为真,表达式取值a,否则取值b

4 Switch语句

Switch(表达式)

{

case 常量表达式1:语句1; break;

case 常量表达式2:语句2; break;

  

case 常量表达式n:语句n; break;

  default :语句n+1;

}

程序举例:

 

第五章 循环控制

1 几种循环语句

goto语句(现已很少使用)

while语句 先判断表达式后执行语句

do-while语句 先执行语句后判断表达式

for语句

2 Break语句和continue语句

Break语句用于跳出循环,continue用于结束本次循环。

程序举例:

 

第六章 数组

1 一维数组

c规定只有静态存储(static)和外部存储(extern)数组才能初始化。给数组初始化时可以不指定数组长度。

2 二维数组

3 字符数组

部分字符串处理函数

puts(字符数组) 将一个字符串输出到终端。

gets(字符数组) 从终端输入一个字符串到字符数组,并且得到一个函数值,为该字符数组的首地址

strcat(字符数组1,字符数组2) 连接两个字符数组中的字符串,数组1必须足够大。

Strcpy(字符数组1,字符串2)  将字符串2拷贝到字符数组1中。

Strcmp(字符串1,字符串2) 比较字符串,相等返回0,字符串1>字符串2,返回正数,小于返回负数。

Strlen(字符数组) 求字符串长度。

Strlwr( 字符串) 将字符串中的大写字母转换成小写

Strupr( 字符串)  将字符串中的小写字母转换成大写

以上是一些比较常用的字符串处理函数。

程序举例:

 

第七章 函数

1 关于形参和实参的说明

在函数被调用之前,形参不占内存

实参可以是常量、变量或表达式

必须指定形参的类型

实参与形参类型应一致

实参对形参的数据传递是"值传递",即单向传递

2 函数返回值

如果想让函数返回一个值,在函数中就要用return语句来获得,在定义函数时也要对函数值指定类型,如果不指定,默认返回整型。

3 函数调用

1)注意在函数调用时实参和形参的个数、类型应一一对应。对实参表求值的顺序是不确定的,有的系统按自左至右,有的系统则按

自右至左的顺序。这一点要注意。

2)函数调用的方式:函数语句,函数表达式,函数参数

3)如果主调函数和被调函数在同一文件中,并且主调函数在前,那么一般要在主调函数中对被调函数进行说明。除非:(1)被调函数

的返回值类型为整型或字符型(2)被调函数出现在主调函数之前。

4)对函数的说明和定义是不同的,定义是指对函数功能的确立,包括指定函数名,函数值类型,形参及其类型、函数体等。说明则

只是对已定义的函数返回值类型进行说明,只包括函数名、函数类型以及一个空的括弧,不包括形参和函数体。

5)c语言允许函数的递归调用(在调用一个函数的过程中又出现直接或间接的调用该函数本身)。

4 数组作为函数参数

1)数组元素作为函数参数和一般变量相同

2)数组名作参数应该在主调和被调函数分别定义数组,形参数组的大小可以不定义。注意:数组名作参数,不是单向传递。

3)多维数组作参数,在被调函数中对形参数组定义时可以省略第一维的大小说明,但不能省略第二维或更高维的说明。

5 局部变量和全局变量

从变量作用域角度分,变量可分为局部变量和全局变量。

1)内部变量(局部变量)

在一个函数内定义,只在函数范围内有效的变量。

2)外部变量(全局变量)

在函数外定义,可以为本文件其它函数所共用,有效范围从定义变量的位置开始到本文件结束。建议尽量少使用全局变量,因为它在程序全部执行过程中都占用资源,而且使函数的通用性降低了。如果在定义外部变量之前的函数要想使用该外部变量,则应在该函数中用extern作外部变量说明。

6 动态存储变量与静态存储变量

从变量值存在的时间(生存期)角度来分,可分为静态存储变量和动态存储变量。静态存储指在程序运行期间给变量分配固定的存储空间,动态存储指程序运行期间根据需要动态的给变量分配存储空间。

C语言中,变量的存储方法分为两大类:静态存储类和动态存储类,具体包括:自动的(auto),静态的(static),寄存器的(register),外部的(extern)

1) 局部变量的存储方式

函数中的局部变量如不作专门说明,都之auto的,即动态存储的,auto可以省略。局部变量也可以定义为static的,这时它在函数内值是不变的。静态局部变量如不赋初值,编译时系统自动赋值为0,动态局部变量如不赋初值,则它的值是个不确定的值。C规定,只有在定义全局变量和局部静态变量时才能对数组赋初值。为提高执行效率,c允许将局部变量值放在寄存器中,这种变量叫register变量,要用register说明。但只有局部动态变量和形式参数可以作为register变量,其它不行。

2) 全局变量的存储方式

全局变量在函数外部定义,编译时分配在静态存储区,可以在程序中各个函数所引用。多个文件的情况如何引用全局变量呢?假如在一个文件定义全局变量,在别的文件引用,就要在此文件中用extern对全局变量说明,但如果全局变量定义时用static的话,此全局变量就只能在本文件中引用了,而不能被其它文件引用。

3) 存储类别小结

从作用域角度分,有局部变量和全局变量

局部变量:自动变量,即动态局部变量(离开函数,值就消失)

     静态局部变量(离开函数,值仍保留)

         寄存器变量(离开函数,值就消失)

         (形参可定义为自动变量和寄存器变量)

全局变量:静态全局变量(只限本文件引用)  全局变量(允许其它文件引用)

从存在的时间分,有静态存储和动态存储

动态存储:自动变量(本函数内有效)           寄存器变量(本函数内有效)      形参

静态存储:静态局部变量(函数内有效)   静态全局变量(本文件内有效)   全局变量(其它文件可引用)

从变量值存放的位置分

静态存储区:静态局部变量  静态全局变量    全局变量

动态存储区:自动变量和形参

寄存器内:寄存器变量

7 内部函数和外部函数

内部函数:只能被本文件中的其它函数调用,定义时前加static,内部函数又称静态函数。

外部函数:可以被其它文件调用,定义时前加extern,如果省略,则隐含为外部函数,在需要调用此函数的文件中,一般要用extern说明。

程序举例:

 

第八章 预编译处理

 c编译系统在对程序进行通常的编译之前,先进行预处理。

c提供的预处理功能主要有以下三种:1)宏定义 2)文件包含 3)条件编译

1 宏定义

不带参数的宏定义

用一个指定的标识符来代表一个字符串,形式:#define 标识符 字符串

几点说明:

1) 宏名一般用大写

2) 宏定义不作语法检查,只有在编译被宏展开后的源程序时才会报错

3) 宏定义不是c语句,不在行末加分号

4) 宏名有效范围为定义到本源文件结束

5) 可以用#undef命令终止宏定义的作用域

6) 在宏定义时,可以引用已定义的宏名

带参数的宏定义

定义形式:#define 宏名(参数表) 字符串

这和函数有些类似,但他们是不同的:

1) 函数调用时,先求实参表达式值,再代入形参,而宏只是简单替换,并不求值

2) 函数调用是在程序运行时分配内存的,而宏展开时并不分配内存,也没有返回值的概念

3) 对函数中的实参和形参都要定义类型,而且要求一致,宏名无类型,其参数也没有类型。

4) 函数只有一个返回值,而宏可以得到几个结果

5) 宏替换不占运行时间,只占编译时间,而函数调用占运行时间

2 文件包含处理

#include "文件1" 就是将文件1的全部内容复制插入到#include位置,作为一个源文件进行编译。

#include命令中,文件名可以用" "也可以用< >,假如现在file1.c中包含file2.h文件," "表示系统先在file1.c所在目录中找file2.h,如果找不到,再按系统指定的标准方式检索目录,< >表示系统直接按指定的标准方式检索目录。所以用" "保险一点。

3 条件编译

条件编译指不对整个程序都编译,而是编译满足条件的那部分。条件编译有以下几种形式:

1#ifdef 标识符

  程序段1

  #else

程序段2

#endif

它的作用:当标识符在前面已经被定义过(一般用#define),则对程序段1编译,否则对程序段2编译。

2)#ifndef 标识符

程序段1

#else

程序段2

#endif

它的作用和#ifdef相反,当标识符没被定义过,对程序段1编译,否则对程序段2编译。

3)#if 表达式

程序段1

  #else

 程序段2

#endif

它的作用:当表达式值为真(非0)时,对程序段1编译,否则对程序段2编译。

 

第九章 指针

指针说白了就是地址。指针变量就是用来存放指针(地址)的变量。

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

读起来很拗口,说白了就是变量的地址和用来存放变量地址的地址变量。因为一个变量在编译的时候系统要为它分配一个地址,假如再用一个变量来存放这个地址,那么这个变量就叫做指向变量的指针变量,也就是用来存放变量地址的这么一个变量。所谓"指向"就是指存放××的地址,如指向变量的指针变量,"指向"就是指用来存放变量的地址,再如指向数组的指针变量,"指向"就是指存放数组的地址。只要理解了这个,指针也就不难了。另外,还有指向字符串的指针变量,指向函数的指针变量,指向指针的指针变量等。

1) 指针变量的定义

形式:类型标识符 *标识符 如:int *pointer;

要注意两点:*表示pointer是个指针变量,在用这个变量的时候不能写成*pointer *pointerpointer指向的变量。一个指针变量只能指向同一个类型的变量。如上面pointer只能指向int型变量。

2)指针变量的引用

两个有关的运算符:

& 取地址运算符 &a 就代表变量a的地址

* 指针运算符  *a 就代表变量a的值

2 数组的指针和指向数组的指针变量

数组的指针指数组的起始地址,数组元素的指针指数组元素的地址。

1)指向数组元素的指针变量的定义与赋值

定义和指向变量的指针变量定义相同,c规定数组名代表数组的首地址,即第一个数组元素地址。

2)通过指针引用数组元素

我们通常引用数组元素的形式是a[i],如果用指针可以这样引用,*(a+i),或定义一个指针变量p,将数组a的首地址赋给pp=a;然后用*(p+i)引用。注意:指针变量p指向数组a首地址,则p++指向数组a的下一元素地址,即a[1]的地址。

3)数组名作函数参数

形参数组和实参数组之间并不是值传递,而是共用同一段地址,所以在函数调用过程中如果形参的值发生变化,则实参的值也跟着变化。

4)指向多维数组的指针和指针变量

以二维数组为居多。假设定义了一个二维数组a[3][4],那么a代表整个二维数组的首地址,也代表第0行的首地址,同时也是第0行第0列的元素的首地址。a +0a[0]代表第0行首地址,a+1a[1]代表第一行的首地址。假设a是一个数组的首地址,那么如果a是一维的,a+I代表第I个元素的地址,如果a是二维的,则a+I代表第I行的首地址。那么第一行第二列的元素地址如何表示呢?a[1]+2&a[1][2]*(a+1)+2。我们只要记住:在二维数组中a代表整个数组的首地址,a[I]代表第I行的首地址,a[I]*(a+I)等价就行了。只要运用熟练了就没什么复杂的了。

5)指向由m个整数组成的一维数组的指针变量

如:int (*p)[4]p是一个指向包含4个元素的一维数组,如果p先指向a[0],则p+1指向a[1],即p的增值是以一维数组的长度为单位的,这里是4,举个例子:假设a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}p先指向a[0]也就是数组a的首地址,那么p+1就是a[1]的首地址即元素9的地址,因为在定义pint (*p)[4],定义一维数组长度为4,所以p+1就等于加了一个一维数组的长度4

3 字符串的指针和指向字符串的指针变量

1)字符串的表示形式

c中字符串有两种表示形式:一种是数组,一种是字符指针

char string[]="I love c!";

char *str="I love c!";

其实指针形式也是在内存中开辟了一个数组,只不过数组的首地址存放在字符指针变量str中,千万不要认为str是一个字符串变量。

2)字符串指针作函数参数                实际上字符串指针就是数组的首地址。

3)字符指针变量与字符数组的区别

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

对数组初始化要用static,对指针变量不用。

对字符数组赋值,只能对各个元素赋值,不能象下面这样:

char str[14];

str="I love c!";

对指针变量可以,

char *str;

str="I love c!";

注意:此时赋给str的不是字符,而是字符串首地址。

数组在定义和编译时分配内存单元,而指针变量定义后最好将其初始化,否则指针变量的值会指向一个不确定的内存段,将会破坏程序。如:

char *a;

scanf( "%s", a );这种方法是很危险的,应该这样:

char *a, str[10];

a = str;

scanf( "%s", a );这样字符指针就指向了一个确定的内存段。

指针变量的值是可以改变的,而字符数组名所代表的字符串首地址却是不能改变的。

4 函数的指针和指向函数的指针变量

一个函数在编译时被分配一个入口地址,这个入口地址就称为函数的指针。函数名代表函数的入口地址,这一点和数组一样。我们可以用一个指针变量来存放这个入口地址,然后通过该指针变量调用函数。如:假设有一个求两者较大的函数如下:int max( int x, int y );

当我们调用这个函数时可以这样:

int c;

c=max( a, b );这是通常调用方法,其实我们可以定义一个函数指针,通过指针来调用,如:

int (*p)(); //注意指向函数指针变量的定义形式

p=max; //此句就是将函数的入口地址赋给函数指针变量p

c=(*p)( a, b );

有些朋友可能对(*p)()不大理解,其实它的意思就是定义一个指向函数的指针变量pp不是固定指向哪个函数的,而是专门用来存放函数入口地址的变量。在程序中把哪个函数的入口地址赋给它,它就指向哪个函数。但要注意,p不能象指向变量的指针变量一样进行p++,p-等无意义的操作。

既然p是一个指针变量,那么就可以作为函数的参数进行传递。其实函数的指针变量最常用的用途之一就是作为函数参数传递到其它函数。这也是c语言中应用的比较深入的部分了。

5 返回指针值的函数

我们知道,一个函数可以带回一个整型值、字符值、实型值等,函数还可以带回一个指针型的数据,即地址。这种函数的定义形式如下:

类型标识符 *函数名(参数表) 如:int *a(x,y)返回一个指向整型的指针

使用这种函数的时候要注意:在调用时要先定义一个适当的指针来接收函数的返回值。这个适当的指针其类型应为函数返回指针所指向的类型。

这样的函数比较难于理解,其实只要把它当做一般的函数来处理就容易了。当我们觉得指针难于理解的时候,就把它暂时当做整型来看,就好理解多了。

6 指针数组

指针数组无疑就是数组元素为指针,定义形式为: 类型标识 *数组名[数组长度]

:int *p[4],千万不要写成int (*p)[4],这是指向一维数组的指针变量。指针数组多用于存放若干个字符串的首地址,注意一点,在定义指针数组时初始化,如下:

static char *name[]={"Li jing","Wang mi","Xu shang"};

不要以为数组中存放的是字符串,它存放的是字符串首地址,这一点一定要注意。

7 指向指针的指针

说的明白一点,将一个指针再用一个变量来存放,那么这个变量就是指向指针的指针。定义如:char * *p;

8 指针数组作main()函数的参数

函数形式为

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

main函数的参数是从命令行得到的,argc指命令行参数个数,注意命令名也算一个参数,命令行参数都是字符串,他们的首地址构成一个指针数组argvMain函数的形参用argcargv只是一个习惯,也可以定义成别的名字。

9 指针小结

1)有关指针的数据类型

定义含义

Int I; 定义一个整型变量I

Int *p; P为指向整型数据的指针变量

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

Int *p[n]; 定义指针数组p,它有n个指向整型的指针元素

Int (*p)[n]; P为指向含有n个元素的一维数组的指针变量

Int f(); F为返回整型值的函数

Int *p(); P为返回值为指针的函数,该指针指向整型数据

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

Int **p; 定义一个指向指针的指针变量

2)ANSI新增了一种void *指针类型,即定义一个指针变量,但不指向任何数据类型,等用到的时候再强制转换类型。如:

char *p1;

void *p2;

p1 = (char *)p2;

也可以将一个函数定义成void *型,如:

void *fun( ch1, ch2 )

表示函数fun返回一个地址,它指向空类型,如果需要用到此地址,也要对其强制转换。如(假设p1char型):

p1=(char *)fun( c1,c2 );

指针应该说是c语言中比较重要的概念,也是c语言的精华,它有很多优点,但用不好也会带来严重性的错误,这就需要我们多用,多练,慢慢的积累经验。

 

第十章 结构体与共用体

1 定义

结构体定义的一般形式:

struct 结构体名{

成员列表

};

定义一个结构体变量可以这样定义:struct 结构体名 结构体变量名;

2 结构体变量的引用

在引用结构体变量时应注意以下规则:

1)不能将结构体变量作为一个整体输入输出,只能对变量当中的各个成员输入输出。新标准C允许将一个结构体变量直接赋值给另一个具有相同结构的结构体变量。

3 结构体变量的初始化   如:

struct student

{long int num;

char name[20];

char sex;

char addr[20];

}a={89031,"Li Lin",'M',"123 Beijing Road" };

4 结构体数组

struct student stu[4];

定义了一个数组stu,其元素为struct student类型,数组有4个元素。注意数组各元素在内存中是连续存放的。

在定义结构体数组时,数组元素个数可以不指定。编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。

5 指向结构体变量的指针

因为结构体变量在内存中是连续存放各成员的,因此我们可以将结构体变量在内存中的起始地址存放到一个变量中,那么这个变量就是指向结构体变量的指针。

注意将结构体变量的首地址赋给指针变量的形式:

struct student stu_1;

struct student *p;

p=&stu_1; //要加取地址符 而指向函数和指向字符串的指针不用

在对引用结构体变量中的成员时,有三种方式:

以上面的结构体为例:设p为指向此结构体变量的指针,即p=&a;

1) a.num

2) (*p).num

3) p->num

6 指向结构体数组的指针

struct student *p;

struct student stu[4];

p=stu;

p为指向结构体数组的指针变量。这里应注意p++p指向stu[0],p++则指向stu[1]P指向的是数组中一个元素的首地址,而不能让p指向元素中的某一成员,如p=&stu[I].name是不对的。

7 用指向结构体的指针作函数参数

虽然ANSI C允许用整个结构体作为函数参数,但要将全部成员值一个一个传递,开销大。所以用指针作参数,能提高运行效率。

Struct student stu;

用整个结构体作为参数调用形式:

fun( stu );

而且被调函数fun中也要定义成结构体变量,struct student stu;

用指针作参数调用形式:

fun( &stu );

被调函数fun中定义成指针变量,struct student *p;

8 用指针处理链表

链表是一种重要的数据结构,原因就在于它可以动态的进行存储分配。链表都有一个头指针,用来存放整个链表的首地址。链表的定义形式如下:

struct node{

int num;

struct node *next;

};

next用来存放下一节点的地址。

如何进行动态的开辟和释放存储单元呢?c提供了以下有关函数:

1) malloc(size) 在内存的动态存储区开辟一个长度为size的连续空间。成功返回空间首地址,失败返回0;

2) calloc(n,size) 在内存的动态存储区开辟n个长度为size的连续空间。成功返回空间首地址,失败返回0;

3) free(ptr) 释放由ptr指向的内存区。Ptr是最近调用一次调用malloccalloc时返回的值。

上面函数中,nsize为整型,ptr为字符指针。

9 共用体

定义形式:

union 共用体名

{

成员列表

}变量列表;

共用体和结构体类似,只是有一点不同,结构体中个成员的起始地址不同,结构体变量在内存中的长度为各成员长度之和;而共用体中个成员的起始地址相同,共用体变量所占的内存长度为最长的成员的长度。

共用体类型数据的特点:

1) 同一个内存段可以存放几种不同类型的成员

2) 共用体变量中起作用的成员是最后一次存放的成员

3) 不能对共用体变量名赋值,不能在定义时初始化。

4) 不能把共用体变量作为函数参数

5) 共用体类型可以出现在结构体定义中,反之也可,也可以定义共用体数组。

另外,结构体名可以作为参数,而共用体名不可以。

这两中数据结构在不同场合中各有所用。

10 枚举类型

定义形式如下:举个例子

enum weekday{sun,mon,tue,wed,thu,fri,sat};

enum weekday workday,week_end; //定义枚举变量

workdayweek_end被定义成枚举类型,他们的值只能为sunsat之一。

也可以直接定义枚举变量,这一点与结构体相同

enum weekday{sun,mon,tue,wed,thu,fri,sat}wordday,week_end;

注意:枚举元素是作为常量存在的,他们是有值的,c在编译时使他们的值按顺序为0,1,2

如:上面的定义中,sun的值为0mon的值为1

另外:虽然枚举元素有值,但不能将一个整数直接赋给一个枚举变量。应进行强制类型转换,如:

workday=(enum weekday)2;它相当于把tue赋给了workday

11 typedef定义类型

typedef的作用就是能够让你定义一个自己喜欢的数据类型名来代替已有的数据类型名。如:

typedef int INT;那么我就可以用INT来定义整型变量了。作用和int一样。

Typedef用于结构体定义,如:

Typedef struct{

Int day;

Int month;

Int year;

}DATE;

DATE birthday;

DATE *p;等等

typedef有利于程序的通用与移植。

 

第十一章 位运算

1)概述

所谓位运算是指进行二进制位的运算。在系统软件中,常要处理二进制位的问题。

c提供的位运算符有:

& 按位与

| 按位或

^ 按位异或

~ 取反

<< 左移

>> 右移

&对于将一个单元清零、取一个数中的某些指定位以及保留指定位有很大用途。

|常被用来将一个数的某些位置1

^判断两个位值,不同为1,相同为0。常用来使特定位翻转等。

~常用来配合其它位运算符使用的,常用来设置屏蔽字。

<<将一个数的各二进制位全部左移,高位左移后溢出,舍弃不起作用。左移一位相当于该数乘2,左移n位相当于乘2n。左移比乘法运算要快的多。

>>右移时,要注意符号问题。对无符号数,右移时左边高位移入0,对于有符号数,如果原来符号位为0(正数),则左边移入0;如果符号位为1(负数),则左边移入0还是1要取决于系统。移入0的称为"逻辑右移",移入1的称为"算数右移"

2)位段

将一个字节分为几段来存放几个信息。所谓位段是以位为单位定义长度的结构体类型中的成员。如:

struct packed-data{

unsigned a:2;

unsigned b:6;

unsigned c:4;

unsigned d:4;

int I;

}data;

其中a,b,c,d分别占2,6,4,4位。I为整型,占4 个字节。

对于位段成员的引用如下:

data.a = 2;等,但要注意赋值时,不要超出位段定义的范围。如位段成员a定义为2位,最大值为3,即(11)2,所以data.a=5;就会取5的两个低位进行赋值,就得不到想要的值了。

关于位段的定义和引用,有几点重要说明:

①若某一个段要从另一个字开始存放,可以定义:

unsigned a:1;

unsigned b:2;

unsigned :0;

unsigned c:3; (另一单元)

使用长度为0的位段,作用就是使下一个位段从下一个存储单元开始存放。

②一个位段必须存放在用一个存储单元中,不能跨两个单元。

③可以定义无名位段。如:

unsigned a:1;

unsigned :2; (这两位空间不用)

unsigned b:3;

④位段的长度不能大于存储单元的长度,也不能定义位段数组。

 

第十二章 文件

1) 概述

c语言将文件看成一个字符的序列,分为ASCII文件(文本文件)和二进制文件。即一个c文件就是一个字节流或二进制流。

ASCII文件每一个字节放一个ASCII码,代表一个字符,输出与字符一一对应,便于逐个处理字符,但占用空间较多。二进制文件按内存中的存储形式原样输出到磁盘上,节省空间,由于输出与字符不对应,不能直接输出字符形式,一般用于保存中间结果。目前c对文件的处理只有缓冲文件系统一种方法,即无论是从程序到磁盘文件还是从磁盘文件到程序,数据都要先经过缓冲区,待缓冲区充满后,才集中发送。

2) 文件夹类型指针

在缓冲文件系统中,关键的概念是文件指针。因为每个被使用的文件都在内存中开辟一个缓冲区,来存放文件有关信息。这些信息保存在一个结构体变量中,该结构体类型是由系统定义的,取名为FILE,在stdio.h中定义。

FILE *fp;

定义了一个文件指针变量fp,以后对文件的操作都是通过fp进行的。

3) 文件的打开与关闭

在对文件读写之前,要先打开文件。

打开文件的函数为:fopen(),调用方式为:

FILE *fp;

fp=fopen( filename,使用文件方式 );

fopen()失败返回一个空指针NULL,成功则返回一个指向"filename"的文件指针,赋给fp,这样fp就和打开的文件联系在一起了。或者说,fp指向了"filename"

文件使用方式:r,w,a,rb,wb,ab,r+,w+,a+,rb+,wb+,ab+,具体含义要记住。

4)文件的关闭

为了防止数据丢失,程序结束前,务必将打开的文件关闭,即将文件指针与文件脱钩。用fclose(文件指针)函数关闭文件,执行函数后,先将缓冲区中的数据送到磁盘文件,然后释放文件指针。成功返回0,失败返回非0

5)文件的读写

文件打开后,就可以对其读写了,常用的文件读写函数有:

fputcfgetc

fputc将一个字符写到文件,形式为fputc( ch, fp );将字符ch写入fp所指向的文件。成功返回该字符,失败返回EOFEOFstdio.h中定义为符号常量-1

fgetc从指定文件读入一个字符,该文件必须是以读或读写方式打开的。调用形式为ch=fgetc(fp);fp指向的文件读入一个字符赋给ch,当文件结束时,fgetc返回一个EOF,我们可以用函数feof(fp)来判断是否已到文件尾,返回1表示已到文件尾,否则返回0

这个函数适用于文本文件和二进制文件。

freadfwrite函数

可以读写一组数据。调用形式如下:

fread( buffer, size, count, fp );

fwrite( buffer, size, count, fp );

buffer为一个指针,对fread来讲,是指从文件读出数据的存放地址,对fwrite来讲,是要写入文件的数据的地址。

size 要读写的字节数

count 要进行读写多少个size字节的数据项(书上这么说)其实就是读写的次数

fp 文件指针

这两个函数返回值成功为1,失败为非1,一般用于二进制文件的读写。

注意:有些c编译系统不具备这两个函数。

fprintf()fscanf()函数

格式化输出和输入函数,与printf()scanf()作用相似,只有一点不同,fprintf()fscanf()的读写对象不是终端而是磁盘文件。调用方式:

fprintf(文件指针,格式字符串,输出列表);

fscanf(文件指针,格式字符串,输出列表);

fgets()fputs()函数

作用是读写一个字符串,如:

fgets(str,n,fp);

意为从fp指向的文件读出n-1个字符,存放到str中,成功返回str的首地址。

fputs( "China", fp );

把字符串China写入fp指向的文件。成功返回0,失败为非0

6)文件的定位

文件中有一个位置指针,指向当前读写的位置,如果要强制改变位置指针的位置,可以用有关函数:

rewind 使位置指针重新返回文件的开头

fseek()

fseek()函数可以任意改变位置指针的位置,以实现随机读写文件。调用形式:

fseek( 文件指针类型,位移量,起始点 );

起始点有以下三个值:

SEEK_SET0 文件开始

SEEK_CUR1 文件当前位置

SEEK_END2 文件末尾

位移量指以起始点为基点,移动的字节数(正数向文件尾移动,负数向文件头移动),一般位移量用long型数据,以避免大于64K的文件出错。Fseek()函数一般用于二进制文件,因为文本文件要进行字符转换,计算时会发生混乱。

Fseek( fp, 100L, 0 ); 将位置指针从文件头向文件尾移动100个字节处。

Fseek( fp, 50L, 1 ); 将指针从当前位置向文件尾移动50个字节处。

Fseek( fp, -10L, 2 ); 将指针从文件尾向文件头移动10个字节处。

ftell()

得到流式文件位置指针的当前位置,成功返回相对于文件头的位移量,失败返回-1L

C语言编程易犯毛病集合

C语言的最大特点是:功能强、使用方便灵活。C编译的程序对语法检查并不象其它高级语言那么严格,这就给编程人员留下“灵活

的余地”,但还是由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里

的错误。看着有错的程序,不知该如何改起,本人通过对C的学习,积累了一些C编程时常犯的错误,写给各位学员以供参考。

1.书写标识符时,忽略了大小写字母的区别。

main()

{

int a=5;

printf("%d",A);

}

编译程序把aA认为是两个不同的变量名,而显示出错信息。C认为大写字母和小写字母是两个不同的字符。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。

2.忽略了变量的类型,进行了不合法的运算。

main()

{

float a,b;

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

}

%是求余运算,得到a/b的整余数。整型变量ab可以进行求余运算,而实型变量则不允许进行“求余”运算。

3.将字符常量与字符串常量混淆。

char c;

c="a";

在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。C规定以“/0”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:‘a'和‘/'0,而把它赋给一个字符变量是不行的。

4.忽略了“=”与“==”的区别。

在许多高级语言中,用“=”符号作为关系运算符“等于”。如在BASIC程序中可以写

if (a=3) then …

C语言中,“=”是赋值运算符,“==”是关系运算符。如:

if (a==3) a=b;

前者是进行比较,a是否和3相等,后者表示如果a3相等,把b值赋给a。由于习惯问题,初学者往往会犯这样的错误。

5.忘记加分号。

分号是C语句中不可缺少的一部分,语句末尾必须有分号。

a=1

b=2

编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一部分,这就会出现语法错误。改错时,有时在被指出有错的一行中未发现错误,就需要看一下上一行是否漏掉了分号。

{ z=x+y;

t=z/100;

printf("%f",t);

}

对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL不同的)

6.多加分号。

对于一个复合语句,如:

{ z=x+y;

t=z/100;

printf("%f",t);

};

复合语句的花括号后不应再加分号,否则将会画蛇添足。

又如:

if (a%3==0);

I++;

本是如果3整除a,则I1。但由于if (a%3==0)后多加了分号,则if语句到此结束,程序将执行I++语句,不论3是否整除aI都将自动加1

再如:

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

{scanf("%d",&x);

printf("%d",x);}

本意是先后输入5个数,每输入一个数后再将它输出。由于for()后多加了一个分号,使循环体变为空语句,此时只能输入一个数并输出它。

7.输入变量时忘记加地址运算符“&”。

int a,b;

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

这是不合法的。Scanf函数的作用是:按照ab在内存的地址将ab的值存进去。“&a”指a在内存中的地址。

8.输入数据的方式与要求不符。

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

输入时,不能用逗号作两个数据间的分隔符,如下面输入不合法:

34

输入数据时,在两个数据之间以一个或多个空格间隔,也可用回车键,跳格键tab

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

C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应输入与这些字符相同的字符。下面输入是合法的:

34

此时不用逗号而用空格或其它字符是不对的。

3 4 34

又如:

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

输入应如以下形式:

a=3,b=4

9.输入字符的格式与要求不一致。

在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。

scanf("%c%c%c",&c1,&c2,&c3);

如输入a b c

字符“a”送给c1,字符“ ”送给c2,字符“b”送给c3,因为%c只要求读入一个字符,后面不需要用空格作为两个字符的间隔。

10.输入输出的数据类型与所用格式说明符不一致。

例如,a已定义为整型,b定义为实型

a=3;b=4.5;

printf("%f%d/n",a,b);

编译时不给出出错信息,但运行结果将与原意不符。这种错误尤其需要注意。

11.输入数据时,企图规定精度。

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

这样做是不合法的,输入数据时不能规定精度。

12.switch语句中漏写break语句。

例如:根据考试成绩的等级打印出百分制数段。

switch(grade)

{ case 'A':printf("85~100/n");

case 'B':printf("70~84/n");

case 'C':printf("60~69/n");

case 'D':printf("<60/n");

default:printf("error/n");

由于漏写了break语句,case只起标号的作用,而不起判断作用。因此,当grade值为A时,printf函数在执行完第一个语句后接着执

行第二、三、四、五个printf函数语句。正确写法应在每个分支后再加上“break;”。例如

case 'A':printf("85~100/n");break;

13.忽视了whiledo-while语句在细节上的区别。

(1)main()

{int a=0,I;

scanf("%d",&I);

while(I<=10)

{a=a+I;

I++;

}

printf("%d",a);

}

(2)main()

{int a=0,I;

scanf("%d",&I);

do

{a=a+I;

I++;

}while(I<=10);

printf("%d",a);

}

可以看到,当输入I的值小于或等于10时,二者得到的结果相同。而当I>10时,二者结果就不同了。因为while循环是先判断后执行,而do-while循环是先执行后判断。对于大于10的数while循环一次也不执行循环体,而do-while语句则要执行一次循环体。

14.定义数组时误用变量。

int n;

scanf("%d",&n);

int a[n];

数组名后用方括号括起来的是常量表达式,可以包括常量和符号常量。即C不允许对数组的大小作动态定义。

15.在定义数组时,将定义的“元素个数”误认为是可使的最大下标值。

main()

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

printf("%d",a[10]);

}

C语言规定:定义时用a[10],表示a数组有10个元素。其下标值由0开始,所以数组元素a[10]是不存在的。

16.初始化数组时,未使用静态存储。

int a[3]={0,1,2};

这样初始化数组是不对的。C语言规定只有静态存储(static)数组和外部存储(exterm)数组才能初始化。应改为:

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

17.在不应加地址运算符&的位置加了地址运算符。

scanf("%s",&str);

C语言编译系统对数组名的处理是:数组名代表该数组的起始地址,且scanf函数中的输入项是字符数组名,不必要再加地址符&。应

改为:

scanf("%s",str);

18.同时定义了形参和函数中的局部变量。

int max(x,y)

int x,y,z;

{z=x>y?x:y;

return(z);

}

形参应该在函数体外定义,而局部变量应该在函数体内定义。应改为:

int max(x,y)

int x,y;

{int z;

z=x>y?x:y;

return(z);

}