C程序设计
第 9章 ----第 13章
第 9章 预处理命令
区别于其它高级语言:
三种预处理功能 宏定义
文件包含
条件编译
用命令来实现
用命令来实现并用, #”开头
9.1 宏定义
用一个指定的标识符来代表一个字符串
1、不带参数的宏定义
命令的一般形式
#define 标识符(宏名) 字符串
如 #define PI 3.1415929
在预编译时,将程序中出现的宏名进行宏展开。
说明,P188
2、带参数的宏定义
( 1)命令的一般形式
#define 宏名(形参表) 字符串
( 2)带参数的宏展开
预编译时遇到带实参的宏名时,则按命令行指定的字
符串从左到右进行置换,原则是,遇形参则以实参代
替,非形参字符原样保留,从而形成展开后的内容。
说明 P190
2、带参数的宏定义
( 1)命令的一般形式
#define 宏名(形参表) 字符串
( 2)带参数的宏展开
预编译时遇到带实参的宏名时,则按命令行指定的字
符串从左到右进行置换,原则是,遇形参则以实参代
替,非形参字符原样保留,从而形成展开后的内容。
说明 P190
( 3)宏名的作用域是从宏名定义命令后到本文件结束,
可用 #undef 终止宏名的作用域
如 #undef 宏名
( 4)宏定义中可以引用已定义过的宏名
( 5)程序中双引号括起的字符串的内容若有与宏名相
同的子串时,一般不进行宏展开。
9.2 文件包含
1、命令的一般形式
#include,文件名,
或 #include <文件名 >
说明 ( 1)文件名应是 C语言的源文件名,该源文件可
以是系统提供,也可以是用户编写。头文件、标题文
件,h 或,c
9.2 文件包含
( 2),” 和 < >的区别
( 3)一条 include 命令只能指定一个被包含的文件
2、文件包含的处理
预编译时,将被包含文件的内容全部复制到包含文件
中,文件包含可以是嵌套的。
9.3 条件编译
1,#ifdef 标识符
程序段 1
#else
程序段 2
#endif
当指定标识符已经被定义过,则对程序段 1进
行编译,否则编译程序段 2
9.3 条件编译
2,#ifndef 标识符
程序段 1
#else
程序段 2
#endif
当指定标识符未被定义过,则对程序段 1进行
编译,否则编译程序段 2
9.3 条件编译
3,#if 表达式
程序段 1
#else
程序段 2
#endif
当表达式的值为, 真, (非零)时,编译程序
段 1,否则编译程序段 2
说明
( 1)以上的, 程序段, 可以含语句和命令行
( 2), 标识符, 一般是用 #define命令定义
(3)其中的 #else部分可以没有
第 10章 指 针
有利于设计系统软件
特点:表示复杂的数据结构;能动态分配内存;能
方便地使用字符串;有效方便地使用数组;调用函
数时得到多于 1个的值;能直接处理内存地址。
10.1 指针的概念
一个变量的 地址 称为该变量的, 指针,
一个变量存放另一个变量的地址(指针)则它是 指
针变量
10.2 变量的指针和指向变量的指针变量
变量的, 指针, 就是变量的 地址
,*, --指向
p是指针变量 * p是 p指向的变量
如 i=3;
p=&i;
一、指针变量的定义
int i,j;
int *p1,*p2;
指针变量定义的一般形式
类型标识符 *标识符;
如,float *p3;
char *p4;
二、指针变量的引用
( 1)& 取地址运算符
( 2)* 指针运算符(间接访问运算符)
& a 变量 a的地址
* p 指针变量 p所指向的变量
三、指针变量作为函数参数
运用指针变量作参数,可以得到多个变化的值
10.3 数组的指针和指向数组的指针变量
数组的, 指针, 就是数组的 起始地址。
数组元素的指针是指数组元素的地址,引用数
组元素可以用下标法(如 a[3])也可以用指针法,即
通过指向数组元素的指针找到所需的元素。使用指
针法能使目标程序质量高(占内存少,运行速度快)
一、一维数组和指针
指向数组元素的指针变量的定义与赋值
int a[10];
int *p;
p=&a[0];或 p=a;
在定义变量时可以赋给初值
int *p=& a[0];或 *p=a;
说明:
1,一维数组和数组元素的地址
数组名代表该数组的首地址,(数组中第一个元素的地址)
例如 int a[10],*p;
则 p=a 和 p=&a[0]是等价的
p=a+1和 p=&a[1]等价
……
p=a+i和 p=&a[i]等价
2、通过一维数组名所代表的地址引用数组元素
数组名代表该数组的首地址,(数组中第一个元素的地址)
*(a+i)代表第 i个元素,即 a[i]
3、通过指针引用数组元素
例 int a[10],*p;
若 p=a;或 p=&a[0];
则 p+i代表 a数组第 i个元素的地址,*(p+i)代表第 i个元素 a[i]
4、通过带下标的指针引用数组元素
*(p+i)可以写成 p[i]
综上所述:在 p=a的条件下,对 a[i]数组元素的引用方式可以是:
*(a+i) ; *(p+i) ; p[i];
注意:
1,a和 a[0]具有不同含义,前者是一个地址常量,是存贮
单元 a[0]的地址;而后者是一个变量名,代表一个存放数据的
存贮单元。
2,p是指针变量,可以对其进行加、减和赋值运算,如
p++,p=a,p=&a[i]等运算都是合法的,而由于数组名 a代表一个
地址常量,即一个常量指针,因此 a++,a=p,a+=i是非法的。
二、二维数组和指针
1、二维数组和数组元素的地址
如 int a[2][3];
二维数组 a中任一元素 a[i][j]的地址与 a[i]的关系是:
&a[i][j]==a[i]+j
注,a+1和 a[0]+1的含义不同
a和 a[i]是两个基类不同的常量指针,*(a+1)代表 a[1]
而 *(a[0]+1)则代表 a[0][1]
2、通过地址引用二维数组元素
设二维数组 a的定义同上,则 a中任一元素 a[i][j]的引
用可以用以下表达式之一来表示
(1)*(a[i]+j)
(2)*(*(a+i)+j)
(3)(*(a+i))[j]
3、通过一个指针数组引用二维数组元素
设 int a[2][3],*p[2]
p是一个一维指针数组,其中每个元素都是一个指向
整型变量的指针。
若 for(i=0;i<2;i++)
p[i]=a[i];
使 p数组中每个元素依次指向 a数组中每行的起始元
素,则二维数组元素 a[i][j]的引用形式可以写成
(1) *(p[i]+j)
(2) *(*(p+i)+j)
(3) (*(p+i))[j]
(4) p[i][j]
4、通过一个行指针引用二维数组元素
设 int a[2][3],( *prt)[3];
其中 prt是一个指针变量,它指向包含三个整数元素
的一维数组。注意,*prt两侧的园括号不可缺少,
如果写成 *prt[3] 则 prt将成为一个指针数组名
若 prt=a;则 a[i][j]可以写成
*(prt[i]+j);*(*(prt+i)+j);(*(prt+i))[j];prt[i][j]
10.4 字符串的指针和指向字符串的指针变量
1、字符串的表示形式
(1)用字符数组实现
static char string[]=“I love China”;
(2)用字符指针实现
char *string;
string=“I Love China”;
10.4 字符串的指针和指向字符串的指针变量
2、字符串指针作函数参数
如,P235 例 10.20
3、字符指针变量与字符数组的区别
( 1)字符数组由若干个元素组成,每个元素中放一
个字符;而字符指针变量中存放的是地址,决不是
将字符串放到字符指针变量中。
3、字符指针变量与字符数组的区别
( 2)赋初值的方式不同
对数组 static char str[]=“I Love China!”;
对字符指针 char *a=“I Love China!”;
3、字符指针变量与字符数组的区别
( 3)赋值方式不同
不能用 char str[14];
str=“I Love China!”;
而字符指针变量可以
char *a;
a=“I Love China!”;
3、字符指针变量与字符数组的区别
( 4)指针变量的值是可以改变的
char *a=“I Love China!”;
a+=7;
3、字符指针变量与字符数组的区别
( 5)可以表示格式字符串
char *format=“a=%d,b=%f\n”;
printf(format,a,b);
或,static char format[]=“a=%d,b=%f\n”;
printf(format,a,b);
10.5 函数的指针和指向函数的指针变量
1、指向函数的指针变量的一般定义形式
数据类型标识符 (*指针变量名)()
返值类型
10.5 函数的指针和指向函数的指针变量
2、函数的调用可以通过函数名调用,也可以通过函
数指针调用。如
c=max(a,b);
int max();
int (*p)();
p=max;c=(*p)(a,b);
10.5 函数的指针和指向函数的指针变量
3、函数的指针变量可以改变 p=min;
4、在给函数指针赋值时,只用函数名
5、用函数指针调用函数时,只需要将 (*p)代替函数
名即可,如 c=(*p)(a,b);
6、函数指针变量 p+n,p++,p--;运算是无意义的
7、可以用指向函数的指针变量作函数的参数
如,P244页
10.6 返回指针值的函数
定义的一般形式:
类型标识符 *函数名(参数表)
如 int *a(int x,int y)
例,P246 例 10.25
仔细读!
10.7 指针数组和指向指针的指针
1 指针数组的定义形式
类型标识 *数组名 [数组长度说明 ];
如 int *p[4];
2、指向指针的指针
定义形式 char **p;
3、指针数组作 main()函数的形参
main(int argc,char *argv[])
10.8 有关指针的数据类型和指针运算的小结
1 有关指针的数据类型的小结
2、指针运算的小结
3,void指针类型
本章的例题需要同学们仔细阅读!
第 11章 结构体与共用体
11.1 使用关键字 typedef定义新的类型标识符
一般形式 typedef 类型名 标识符;
说明 ( 1), 类型名, 为已有定义的类型标识符;, 标识符,
为用户定义的标识符。
如,typedef int INTEGER;
typedef float REAL;
(2) 经此定义后的标识符可作为原数据类型名使用
如,INTEGER i,j; REAL x,y;
11.2 结构体
一、结构体类型定义的一般形式
Struct 结构体名
{类型名 结构成员名;
??
} ;
说明( 1)结构体成员的命名规则与变量相同,并且允许与变量或
其它结构体中的成员重名。
( 2)结构体成员可以是简单类型,数组,指针或已定义过的
结构等。
如,struct student
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
二、结构体变量、数组、指针的四种说明方式
struct student 是自定义的类型名,是结构体类型,用其可
以定义变量、数组、指针。
( 1)紧跟在类型定义之后进行说明
如 struct student
{ ……
} student1,student2;
( 2)先定义结构体类型,再单独进行说明
如 struct student
{ ……
} ;
struct student student1,student2;
( 3)定义一个无名结构体类型,直接进行变量说明
如 struct
{ ……
} student1,student2;
( 4)用 typedef定义一个结构体类型名,再用类型名进行说
明。如 typedef struct
{int month;
……
}DATE;
DATE birthday;说明,P262
三、对结构体成员的三种引用方式
( 1)结构体变量名,成员名 如 student1.num=10010;
( 2)指针变量名->成员名 如 p->num=10010;
( 3)(*指针变量名),成员名 如 (*p).num=10010;
(2)和 (3) 的前提是,struct student student1,*p;
p=&student1;
四、给结构体变量、数组赋初值
( 1)在定义结构时,可以给结构变量、数组赋初值。
如 P264 例 11.1 P266 例 11.2
( 2)给结构体变量赋初值不能跨越前边的成员而只给后面
的成员赋值。
( 3)结构体数组成员赋初值的规则与数组元素赋初值的规
则相同。如 例 11.2
五、引用自身的结构体
当在一个结构体中有一个成员是指向本结构体类型的
指针时,那么通过这样的指针可以把若干个相同的结构体
存贮单元连在一起,用以建立如链表、树、图等各种数据
结构。
如链表结构:
如 P273 图 11.10
Struct c_table
{char ch;
Struct c_table *next;
};
C语言的内存动态分配函数
( 1) malloc(表达式 )
该函数用来在内存中分配一个指定长度(以字节为单
位)的存贮空间,其中表达式的值表示待分配的存贮空间
的字节数,返值是指向分配域起始地址的指针,不成功为
NULL(空值)
其函数原型为
void *malloc(unsigned int size);
( 2) void *calloc(unsigned num,nusigned size)
该函数用来在内存中分配一块连续的存贮空间,其中
num指定需要分配的元素个数; size用来指定每个元素所占
的内存空间(以字节为单位),返值是指向分配域起始地
址的指针,不成功为 NULL(空值)
( 3) void free(void *p)
该函数用来释放由 malloc或 calloc函数分配的存贮空间,其
中指针应指向最近一次调用 malloc或 calloc时所分配存贮区
的首地址。
对链表的操作:
( 1)建立链表
( 2)输出链表
( 3)链表的插入及删除
11.3 共用体(联合体)
一、共用体定义的一般形式
union [共用体名 ]
{类型名 共用体成员名;
……
}[变量名表 ];
说明 ( 1)共用体变量的定义与结构类似,可以把类型定义
和变量说明放在一起,也可以分开,还可以直接定义共用
体变量而不要共用体名。
( 2)共用体的含义与结构体不同,共用体中所有成员
均放在以同一地址开始的存贮空间中,使用覆盖的方式共
享存贮单元,共用体所占空间大小取决于占存贮空间最大
的那个成员。
( 3)共用体变量不能在定义时赋初值。
11.3 共用体(联合体)
二、共用体类型数据的使用
( 1)不能对共用体变量进行整体操作,只能单独引用其成员
( 2)共用体成员的引用方式与结构体完全相同
( 3)共用体变量不能作为函数的参数或函数值,但可以使用
指向共用体的指针变量 P289
( 4)共用体可以作为结构成员,结构也可以作为共用体的成

11.4 枚举类型
一个变量只有几种可能的值,可以定义为枚举类型
一、枚举类型的定义和变量说明的两种方式
1、定义和说明分开
类型定义形式
enum 枚举类型名 {枚举值 1,枚举值 2,……,枚举值 n} ;
变量说明形式
enum 枚举类型名 变量名表;
2、直接定义枚举变量
一般形式
enum {枚举值 1,枚举值 2,……,枚举值 n}变量名表 ;
说明 ( 1)枚举类型名为用户定义标识符
( 2)枚举值又称枚举元素、枚举常量,也是用户定
义的标识符
二、枚举元素和它的序号
1、在定义枚举类型的同时,编译程序按顺序给每个枚举元素
一个对应的序号,序号的值从 0开始,后续元素顺序加 1
2、可以在定义时人为指定枚举元素的序号值,如
enum {sun=7,mon=1,tue,ued,thu,fri,sat}day;
没有指定序号值的元素则在前一元素序号值基础上顺序加 1
三、枚举类型数据的使用
1、枚举类型变量的取值范围只限于类型定义时所列出的值
2、只能给枚举变量赋枚举值,若赋序号值必须进行强制类型
转换,如 P292
3、枚举值可以进行加(减)一个整数 n的运算,用以得到其后
(前) 第 n个元素的值
4、枚举值可以按定义时的序号进行关系比较
5、枚举值可以按整型输出其序号值
第 12章 位运算
12.1 位运算符
位运算:指进行二进制位的运算
运算符 功能 优先级 结合性
~ 按位取反 高 左 右
<< >> 左移 右移 左 右
& 按位与 左 右
^ 按位异或 左 右
| 按位或 低 左 右
说明:( 1)只有 ~为单目运算符
( 2)位运算只能用于整型或字符型数据
( 3)位运算符可以与赋值运算符结合组成扩展的赋值
运算,即 ~=,<<=,>>=,&=,^=,!=
( 4)两个长度不同的数据进行位运算时,系统先将二
者右端对齐,然后将短的一方按符号位扩充,无符号数则以 0
扩充。
12.2 位运算符的运算规则
1、按位取反运算符 ~
单目运算符 如 ~x
~x不是求 x的负数 ~1的结果不是- 1
如 ~1=? ~8=?
2、移位运算符 >> 和 <<
左边是移位对象,右边是整型表达式,表示移位的
位数,左移时低位补 0,右移时补符号位,若为无符
号整数则补 0
3、按位与运算符&
&和&&不同
例如,a=10,b=5;
可用于, 高字节清零,低字节清零,
a&0xff a&0xff00
4、按位或运算符|
如果想使 a的低字节全置 1 则 a|0xff
如果想使 a的高字节全置 1 则 a|0xff00
5、异或运算符^
相同为 0不同为 1,如果 a和 b的值相等,则 a^b=0,如果
想取 a中的值并使低位翻转则 a^0xff
如果使高位翻转 则 a^0xff00
12.3 有多个位运算符的表达式
如果一个表达式中出现多个运算符时,应掌握各运算
符之间的优先关系才能进行正确运算。
~1+1=? ~4+1=?
例 1:若有以下的说明和语句,则以下的输出结果为:
char a=010,b=10;
printf(%d\n”,a^b>>2);
例 2:若有以下说明语句,则以下的输出结果为:
char a=9,b=020;
printf(“%o\n”,~a&b<<1);
第 13章 文 件
12.1 C文件的概念
1,C文件不是由记录组成,而是字符(字节)的序列,称为流式文件。
2,C文件根据数据的组织形式可分为 ASCII文件和二进制文件。
3,C语言对文件的处理方法分为缓冲文件系统和非缓冲文件系统 ANSIC
(美国国家标准化协会)新标准,采用的是缓冲文件系统。
4、在缓冲文件系统中是靠文件指针与相应文件建立起了联系的,一般
是有几个文件,就有几个文件指针。
5、文件指针的定义形式为 FILE *指针变量名;说明是文件型指针
变量
12.2 有关文件的操作
是由库函数来实现的,掌握函数的 调用方式;函数的功能;函数的
返值。
1、文件的打开( fopen)与关闭( fclose)函数
调用形式 FILE *fp;
fp=fopen(文件名,使用方式)
……
fclose(fp);
文件名:是用双引号括起来的字符串, c:\\user\\file.dat”,也可以是字符数
组名或指向字符串的指针变量;使用方式,见 P312表 13.1
2、文件的读写
( 1)单个字符的读( fgetc或 getc)和写( fputc或 putc)函数
fgetc函数从指定文件读一个字符,fputc函数把一个字符写到一个磁盘文
件中去,调用形式:
ch=fgetc(fp); fputc(ch,fp);
( 2)字符串的读( fgets)写( fputs)函数
fgets的调用形式为:
fgets(str,n,fp);
作用是从指定文件读 n-1个字符,并把它放入 str为起始地址的存贮空
间内,如果在读 n-1个字符前遇到了换行或 EOF,读入即结束,字符串
读入结束后,在最后加一个 ’ \0’字符,fgets函数返回 str的地址
2、文件的读写
( 2)字符串的读( fgets)写( fputs)函数
fgets的调用形式为:
fgets(str,n,fp);
作用是从指定文件读 n-1个字符,并把它放入 str为起始地址的存贮空
间内,如果在读 n-1个字符前遇到了换行或 EOF,读入即结束,字符串
读入结束后,在最后加一个 ’ \0’字符,fgets函数返回 str的地址。
fputs调用形式为
fputs(str,fp);
其中 str可以是字符串常量,字符数组名或指向某字符串
的指针变量,函数把 str为起始地址的字符串输出到 fp指定的
文件中,最后的 ’ \0’不输出,也不会在字符串的末尾加 ’ \n’,
输出成功函数值为 0,不成功为非 0
2、文件的读写
( 3)格式化读( fscanf)写( fprintf)函数
函数的调用形式为:
fprintf(文件指针,格式字符串,输出列表 );
fscanf(文件指针,格式字符串,输入列表 );
这两个函数与 printf和 scanf作用相同,只是输入输出对象由终端改
为磁盘文件。
2、文件的读写
( 4)数据块读( fread)写( fwrite)函数
函数的调用形式为:
fread(fuffer,size,count,fp);
fwrite(buffer,size,count,fp);
其中 buffer是一个地址值,对于 fread来说,它是读入数据存贮区的
起始地址,对于 fwrite来说,它是存放输出数据的存贮区的起始地址。
2、文件的读写
( 4)数据块读( fread)写( fwrite)函数
函数的调用形式为:
fread(fuffer,size,count,fp);
fwrite(buffer,size,count,fp);
其中 buffer是一个地址值,对于 fread来说,它是读入数据存贮区的
起始地址,对于 fwrite来说,它是存放输出数据的存贮区的起始地址。
3、用于文件定位的函数
( 1)反绕( rewind)函数
( 2)移动文件位置指针函数( fseek)
( 3)测定位置指针当前指向的函数( ftell)
4、检测文件是否结束的函数( feof)
第 14章 C的扩充内容
区别于其它高级语言:
三种预处理功能 宏定义
文件包含
条件编译
用命令来实现
用命令来实现并用, #”开头