第一章到第六章重点内容详见
第七章 用函数实现模块化程序设计
1.函数的定义
(1)带返回值的函数
//格式如下:
类型名 函数名(参数列表)
{
语句;
return 返回值;
}
(2)不带返回值的函数
//格式如下:
void 函数名(参数列表)
{
语句;
}
2. 函数的调用
函数的调用一般形式:
函数名(实参列表)
具体有3中主要的函数调用方式:
-
函数调用语句
-
函数表达式
-
函数参数
函数调用时的数据传递
-
形式参数和实际参数
-
是参合形参之间的数据传递
注意:函数调用过程中的传值和传地址
传值是指子程序变量的变化不会影响主程序变量。
传地址是指子程序变量的变化会影响主程序变量。
3. 函数的类型
表示函数返回值的类型,是函数定义时,函数名前面的标识符,若缺省,则系统默认为int型。
注意:1. 在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。
2. 函数类型决定返回值的类型。
4. 函数的嵌套调用
C语言的函数定义是互相平行、独立的,也就是说,在定义函数时,一个函数内不能再定义另一个函数,即不能嵌套定义,但可以嵌套调用函数,即在调用一个函数的过程中,又调用另一个函数。
5. 函数的递归调用
调用一个函数的过程中,又出现直接或者间接地调用该函数本身,称为函数的递归调用。
6. 数组作为函数参数
-
数组元素作为函数实参时,把实参的值传给形参,是“值传递”的。数据传递的方向是从实参传到形参,单向传递。
-
数组名作为函数参数,把实参的地址传给形参,是“地址传递”。
7. 局部变量和全局变量
在函数内定义的变量是局部变量,在函数外定义的变量是全局变量。
注意:建议不在必要时不要使用全局变量。
第八章 善于利用指针
1. 指针
如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。编译系统根据程序中定义的变量类型,分配一定长度的空间。内存区的每一个字节有一个编号,这就是“地址”。
由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元,将地址形象化地称为“指针”。
2. 定义指针变量
类型名* 指针变量名
3. 引用指针变量
① 给指针变量赋值。
② 引用指针变量指向的变量。
③引用指针变量的值。
//指针变量初始化方法:
int a = 0;
int* p = &a;
//赋值语句的方法
int a = 0;
int *p;
p = &a;
4. 指针变量作为函数参数
函数的调用可以(而且只可以)得到一个返回值(即函数值),而使用指针变量作参数,可以得到多个变化了的值。
5. 数组元素的指针
所谓数组元素的指针就是数组元素的地址。
可以用一个指针变量指向一个数组元素。
6. 引用数组元素时指针的运算
在指针已指向一个数组元素时,可以对指针进行以下运算:
•加一个整数(用+或+=),如p+1,表示指向同一数组中的下一个元素;
•减一个整数(用-或-=),如p-1,表示指向同一数组中的上一个元素;
•自加运算,如p++,++p;
•自减运算,如p--,--p。
两个指针相减,如p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义),结果是两个地址之差除以数组元素的长度。
7. 通过指针引用数组元素
利用指针引用数组元素,比较方便灵活。
设p开始时指向数组a的首元素(即p=a):
//情况1
p++; //使p指向下一元素a[1]
*p; //得到下一个元素a[1]的值
//情况2
*p++; /*由于++和*同优先级,结合方向自右而左,因此它等价于*(p++)。先引用p的值,实现*p的运算,然后再使p自 增1*/
//情况3
*(p++); //先取*p值,然后使p加1
*(++p); //先使p加1,再取*p
//情况4
++(*p); /*表示p所指向的元素值加1,如果p=a, 则相当于++a[0],若a[0]的值为3,则a[0]的值为4。注意: 是元素 a[0]的值加1,而不是指针p的值加1*/
//情况5: 如果p当前指向a数组中第i个元素a[i],则:
*(p--) //相当于a[i--],先对p进行“*”运算,再使p自减
*(++p) //相当于a[++i],先使p自加,再进行“*”运算
*(--p) //相当于a[--i],先使p自减,再进行“*”运算
8. 数组名做函数参数
当用数组名作参数时,如果形参数组中各元素的值发生变化,实参数组元素的值随之变化。
以变量名和数组名作为函数参数的比较:
实参类型 | 变量名 | 数组名 |
---|---|---|
要求形参的类型 | 变量名 | 数组名或指针变量 |
传递的信息 | 变量的值 | 实参数组首元素的地址 |
通过函数调用能否改变实参的值 | 不能改变实参变量的值 | 能改变实参数组的值 |
注意:实参数组名代表一个固定的地址,或者说是指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理。
有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况。
① 形参和实参都用数组名
int main()
{ int a[10];
⋮
f(a,10);
⋮
}
int f(int x[], int n)
{
⋮
}
② 实参用数组名,形参用指针变量。
int main()
{ int a[10];
⋮
f(a,10);
⋮
}
int f(int *x, int n)
{
⋮
}
③ 实参形参都用指针变量。
int main()
{ int a[10];*p=a;
⋮
f(p,10);
⋮
}
int f(int *x, int n)
{
⋮
}
④ 实参为指针变量,形参为数组名。
int main()
{ int a[10];*p=a;
⋮
f(p,10);
⋮
}
int f(int x[], int n)
{
⋮
}
9. 通过指针引用多维数组
如果用一个指针变量pt来指向此一维数组,应当这样定义:
int (*pt)[4];
//表示pt指向由4个整型元素组成的一维数组,此时指针变量pt的基类型是由4个整型元素组成的一维数组
10. 通过指针引用字符串
-
字符串的复制
char *string="I love China!";
//等价于
char *string; //定义一个char *型变量
string=″I love China!″; //把字符串第1个元素的地址赋给字符指针变量string
2. 字符串的复制
char str[] = {'s','t','r','i','n','g','!','\0'};
//数组str共10个元素,最后2个元素也是'\0',此字符共占10个空间,实际长度为7
char str[] = {'s','t','r','i','n','g','!','\0'};
//定义一个包含7个字符的字符串,最后一个'\0'不能省略
char str[] = {'s','t','r','i','n','g','!'};
//只是定义了一个7个字符的数组,但由于没有'\0',str不能作为字符串使用
3. 字符数组的整体赋值
char str[10] = {"string!"};
//或者
char str[10] = {"string!"};
//或者
char str[] = "string!";
-
字符数组的输入输出
-
使用scanf和printf,格式符均为%s
-
使用gets和puts,传入的实参为数组名或者指向字符数组的指针
11. 字符指针作函数参数
字符指针作为函数参数时,实参与形参的类型有以下几种对应关系:
实参 | 形参 |
---|---|
字符数组名 | 字符数组名 |
字符数组名 | 字符指针变量 |
字符指针变量 | 字符指针变量 |
字符指针变量 | 字符数组名 |
第九章 用户自己建立数据类型
1. 用户自己建立结构体类型
C语言允许用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体(structure) 。
结构体类型的名字是由一个关键字struct和结构体名组合而成的。
花括号内是该结构体所包括的子项,称为结构体的成员(member)。对各成员都应进行类型声明,即
类型名 成员名;
“成员表列”(member list)也称为“域表”(field list),每一个成员是结构体中的一个域。成员名命名规则与变量名相同。
在程序中建立如下表所示的一个结构体类型:
num | name | sex | age | score | addr |
---|---|---|---|---|---|
10010 | Li Fang | M | 18 | 87.5 | Beijing |
struct Student
{ int num; //学号为整型
char name[20]; //姓名为字符串
char sex; //性别为字符型
int age; //年龄为整型
float score; //成绩为实型
char addr[30]; //地址为字符串
}; //注意最后有一个分号
2. 定义结构体数组
定义结构体数组一般形式是:
//情况1
struct 结构体名
{成员表列} 数组名[数组长度];
//情况2
//先声明一个结构体类型,然后再用此类型定义结构体数组
结构体类型数组名[数组长度];
3. 指向结构体变量的指针
如果p指向一个结构体变量stu,以下3种用法等价:
//方法1: stu.成员名
stu.num
//方法2: stu.成员名
(*p).num
//方法3: p->成员名
p->num
4. 用结构体变量和结构体变量的指针作函数参数
将一个结构体变量的值传递给另一个函数,有3个方法:
-
用结构体变量的成员作参数
-
用结构体变量作实参
-
用指向结构体变量(或数组元素)的指针作实参,将结构体变量(或数组元素)的地址传给形参
第十章 对文件的输入输出
文件(file)一般指存储在外部介质上数据的集合。操作系统是以文件为单位对数据进行管理的。
输入输出是数据传送的过程,数据如流水一样从一处流向另一处,因此常将输入输出形象地称为流,即数据流。
文件标识包括3部分: (1)文件路径; (2)文件名主干; (3)文件后缀。
-
文件路径表示文件在外部存储设备中的位置
-
文件名主干的命名规则遵循标识符的命名规则
-
文件后缀用来表示文件的性质
1. 文件类型指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
typedef struct
{ short level; //缓冲区“满”或“空”的程度
unsigned flags; //文件状态标志
char fd; //文件描述符
unsigned char hold; //如缓冲区无内容不读取字符
short bsize; //缓冲区的大小
unsigned char*buffer; //数据缓冲区的位置
unsigned char*curp; //文件位置标记指针当前的指向
unsigned istemp; //临时文件指示器
short token; //用于有效性检查
}FILE;
FILE *fp;
//定义一个指向FILE类型数据的指针变量
2. 打开与关闭文件
-
用fopen函数打开数据文件
fopen(文件名,使用文件方式);
FILE *fp; //定义一个指向文件的指针变量fp fp=fopen(″a1″,″r″); //将fopen函数的返回值赋给指针变量fp
文件使用方式 | 含义 | 如果指定的文件不存在 |
---|---|---|
“r”(只读) | 为了输入数据,打开一个已存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据,打开一个文本文件 | 建立新文件 |
“a”(追加) | 向文本文件尾添加数据 | 出错 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立新文件 |
“ab”(追加) | 向二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建立一个新的文本文件 | 建立新文件 |
“a+” (读写) | 为了读和写,打开一个文本文件 | 出错 |
“rb+”(读写) | 为了读和写,打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,建立一个新的二进制文件 | 建立新文件 |
“ab+”(读写) | 为读写打开一个二进制文件 | 出错 |
-
用fclose函数关闭数据文件
fclose(文件指针);
fclose(fp);
3. 向文件读写字符
读写一个字符的函数:
函数 名 | 调用形式 | 功能 | 返回值 |
---|---|---|---|
fgetc | fgetc(fp) | 从fp指向的文件读入一个字符 | 读成功,带回所读的字符,失败则返回文件结束标志EOF(即-1) |
fputc | fputc(ch,fp) | 把字符ch写到文件指针变量fp所指向的文件中 | 输出成功,返回值就是输出的字符;输出失败,则返回EOF(即-1) |
fgets函数的函数原型为:
char *fgets(char *str, int n, FILE *fp);
fputs函数的函数原型为:
int fputs (char *str, FILE *fp);
4. 用格式化的方式读写文本文件
fprintf(文件指针, 格式字符串, 输出表列);
fscanf(文件指针, 格式字符串, 输入表列);
5. 二进制方式向文件读写数据
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
/*
buffer: 是一个地址。对fread,它是用来存放从文件读入的数据的存储区的地址。对fwrite,是要把此地址开始的存储区中的数据向文件输出(以上指的是起始地址)。
size: 要读写的字节数。
count: 要读写多少个数据项(每个数据项长度为size)。
fp: FILE类型指针。
*/