内容正文:
(清华大学出版)
《C语言程序设计》
同步教学
*
第八章 编译预处理
第8章 编译预处理
8.1 宏定义
8.2 文件包含
8.3 条件编译
*
C语言提供的编译预处理命令主要有以下三种:
(1) 宏定义。
(2) 文件包含。
(3) 条件编译。
第八章 编译预处理
*
8.1.1 不带参数的宏定义
8.1 宏定义
#define 宏名 字符串
标识符
例如:
#define PI 3.1415926
宏展开: 将宏名替换成字符串的过程
第八章 编译预处理
*
(1) 宏名一般习惯用大写字母表示。
(2) 一个宏名只能被定义一次。
(3) 在进行宏定义时,可以引用已定义的宏名。
(4) 对在字符串常量及用户标识符中,与宏名相同的部分不作
替换。
例如:已定义
#define L 1234
在程序中有变量 Length,其中 L 不作替换。同样在
printf("L= %f",…)中的L不作替换。
(5) 宏定义的作用域:从定义开始到程序结束。
(6) 当宏定义在一行写不下,换行时需在行尾加换行字符"\"。
8.1.1 不带参数的宏定义
第八章 编译预处理
*
【 例8.1】使用宏定义的例子。
#include<stdio.h>
#define R 3.0
#define PI 3.1415926
#define L 2*PI*R
#define S PI*R*R
void main()
{
printf("L=%.2f
S=%.2f
",L,S);
}
第八章 编译预处理
*
#define 宏名(形参表) 字符串
例如:
#define M(a,b) a*b
area=M(3,7);
宏展开:area=3*7;
【例8.2】 分析下面程序的执行结 果。
#include <stdio.h>
#define M(x,y) (x*y)
void main()
{ int a = 2, b = 3, c, d;
c = M(a,b);
/* c = (a*b) */
d = M(a+1,b+1);
/* d = (a+1*b+1) */
printf("c=%d, d=%d
",c,d);
}
程序运行结果:
c=6, d=6 (为什么?)
8.1.2 带参数的宏定义
第八章 编译预处理
*
宏调用:6/MU(2+3,4+5)
宏展开:6/((2+3)*(4+5))
宏调用:6/MU(2+3,4+5)
宏展开:6/(2+3)*(4+5)
宏调用:6/MU(2+3,4+5)
宏展开:6/(2+3*4+5)
宏调用:6/MU(2+3,4+5)
宏展开:6/2+3*4+5
(2) 宏定义:#define MU(x,y) (x*y)
(3) 宏定义:#define MU(x,y) (x)*(y)
(4) 宏定义:#define MU(x,y) ((x)*(y))
在使用带参数的宏时,要注意 括号 的使用!!!
(1) 宏定义:#define MU(x,y) x*y
第八章 编译预处理
*
8.1.3 终止宏定义
#undef 宏名
例如:
#define PI 3.14
void main()
…
#undef PI
…
第八章 编译预处理
*
8.2 文件包含
文件包含(file inclusion)
预处理命令有两种格式:
#include“文件名”
#include<文件名>
如:
#include<stdio.h>
在stdio.h文件中有:
#define EOF -l
#define NULL 0
当前目录
系统指定目录
系统指定目录
第八章 编译预处理
*
【例8.3】一个程序写在多个源文件中的应用举例。
经过编译预处理后:
… …
int fun1(int a,int b)
{ return a+b;
}
int fun2(int a,int b)
{ return a*b;
}
void main()
{ printf("%d
",fun1(3,5));
printf("%d
",fun2(3,5));
}
源文件ex803.c中的内容:
#include<stdio.h>
#include"ex803_1.c"
#include"ex803_2.c"
void main()
{printf("%d
",fun1(3,5));
printf("%d
",fun2(3,5));
}
源文件ex803_1.c中的内容:
int fun1(int a,int b)
{ return a+b;}
源文件ex803_2.c中的内容:
int fun2(int a,int b)
{ return a*b;}
第八章 编译预处理
*
8.3 条件编译
源程序中的一部分内容只在满足一定条件才进行编译,或者,当条件成立时去编译一组语句,而当条件不成立时编译另一组语句,这就是“条件编译”。
条件编译命令有以下几种形式:
#ifdef 标识符
程序段l
#else
程序段2
#endif
第八章 编译预处理
*
第九章 指 针
第9章 指 针(Pointer)
9.1 指针概述
9.2 指针与一维数组
9.3 指针与二维数组
9.4 指针与字符串
9.5 指针与函数
9.6 指针数组与多级指针
*
第九章 指 针
#include <stdio.h>
void main()
{
int m=0;
int n=0;
m=1;
printf("Address of m and n
is: %#xd,%#xd
",&m, &n);
}
9.1 指针概述
内存
地址
地址
对内存单元的访问是通过地址实现的,地址“指向”要操作的内存单元。
内存单元的地址—指针 !
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
0xFAC2AB2C
00000000
00000000
00000000
00000000
m
0xFAC2AB28
00000000
00000000
00000000
00000000
n
00000001
00000000
00000000
00000000
*
第九章 指 针
9.1 指针概述
指针变量(pointer):
一种特殊类型的变量,用于存放变量的地址,通常称为指针。
指针 是 一种 变量,
指针 是 一种 特殊类型 的 变量,
一个指针变量的值 是
另一个变量的地址。
*
第九章 指 针
如何声明 一个 指针变量?
int *ptr;
指针变量的名称
指针变量的类型
ptr 是一个指向整型变量的指针
*
第九章 指 针
int x,*ptr;
ptr = &x;
ptr 是一个指向 整型变量x 的指针
如何对 一个指针变量 赋值?
*
第九章 指 针
如何读写内存中的数据?
通过变量的地址访问变量所在的存储单元
两种寻址方式
直接(寻址)访问
直接按变量地址来存取变量内容的访问方式
间接(寻址)访问
通过指针变量来间接存取它所指向的变量的访问方式
寻址方式
*
&:求地址运算符
*:指针运算符
若执行了p=&m,则
*p 与 m 等价,
都代表变量 m 的
存储单元
#include <stdio.h>
void main()
{ int m = 0;
int n = 0;
int *p;
/*求m的地址赋值给变量p */
p = &m;
/* 给p所指向的变量赋值1*/
*p = 1;
p = &n;
*p = 2;
/* 读取p所指向的变量的值*/
m = *p;
printf("m=%d,n=%d",m, n);
}
第九章 指 针
*
#include <stdio.h>
void main()
{ int m = 0;
int n = 0;
int *p;
/*求m的地址赋值给变量p */
p = &m;
/* 给p所指向的变量赋值1*/
*p = 1;
p = &n;
*p = 2;
/* 读取p所指向的变量的值*/
m = *p;
}
第九章 指 针
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
11001100
0xFAC2AB24
p
0xFAC2AB2C
00000000
00000000
00000000
00000000
m
0xFAC2AB28
00000000
00000000
00000000
00000000
n
00101100
10101011
11000010
11111010
00000001
00000000
00000000
00000000
00000010
00000000
00000000
00000000
00101000
10101011
11000010
11111010
00000010
00000000
00000000
00000000
*
注意事项:
(1)* 用于定义指针变量,但指针变量名不带 *。
如 int *point_i;
定义的指针变量为 point_ i ,不是 *point_ i
(2)一个指针变量只能指向同一类型的变量
如 int *p1,*p2;
类型标识符 int 是说明:
指针变量 p1 和 p2 所指向的变量类型为 int 型。
第九章 指 针
*
(3)变量前面的 “*” 不能省略。
如: int *ptr1,ptr2;
则 ptr2 被定义为整型变量而非指针变量。
(4)定义指针变量后,并未确定该变量指向何处。
在引用指针变量前,必须让它指向一个变量。
int a=3; float x=2.5 ; int *p1,*p2;
p1=&a;
p2=&x;
×
√
第九章 指 针
注意事项:
*
在定义指针变量的同时, 可对其初始化。
int i, *ip=&i;
注意:i 的定义一定在 ip 定义之前。
假设 i 为整型变量,且有 int *p1,*p2;
则可通过下面四种方式为指针变量 p1 或 p2 赋值:
第九章 指 针
如何引用 一个指针变量?
*
2. 使用取地址运算符,把地址值赋给指针变量。
p1=&i;(求出变量 i 的内存地址赋给p1)。
i 的地址赋予 p1 之后,*p1 总是与 i 等价。
3. 把指针变量的值直接赋给指针变量。
p1=&i,p2=p1;将使 p1 和 p2 均指向变量 i。
4. 给指针变量赋 NULL 值。
P1=NULL; 表示为指针 p1 赋了“空值”。
NULL 表示空指针,即无效指针;但它只是逻辑上无效,并不是真正地无效。
如何引用 一个指针变量?
第九章 指 针
*
5. 把数组的首地址赋予指向数组的指针变量。
int a[5], *pa; pa=a; ( 或pa=&a[0] )。
6. 把字符串首地址赋予指向字符类型的指针变量。
char *pc; pc= "c language " ;
或:char *pc = "c language";
注意:
仅将首地址赋入,而不是将字符串"c language"赋予 pc
7. 将函数的入口地址赋予指向函数的指针变量。
int (*pf)( ); pf = f ; (f为函数名)
如何引用 一个指针变量?
第九章 指 针
*
#include <stdio.h>
main()
{ char ch='A';
char * ch_ptr ;
ch_ptr=&ch; /*使ch_ptr指向ch*/
printf("ch=%c
",ch);
printf("*ch_ptr=%c
",*ch_ptr);
*ch_ptr='X'; /*间接改变ch的值*/
printf("ch=%c
",ch);
printf("*ch_ptr=%c
",*ch_ptr);
printf("address of ch is: %x
",ch_ptr); } /*输出ch的地址*/
第九章 指 针
【例9.1】
*
将两个整型数a,b按由大到小次序输出。
#include <stdio.h>
main( )
{ int *p1, *p2, *p, a, b;
scanf("%d,%d",&a,&b);
p1=&a; p2=&b;
if(a<b)
{ p=p1; p1=p2; p2=p; }
printf("a=%d,b=%d
",a,b);
printf("larger=%d,little=%d
",*p1,*p2);
}
交换地址
按原序输出
由大到小
注意:a 和 b 并未交换,但 p1 和 p2 的值交换了。
【例9.2】
第九章 指 针
*
第九章 指 针
对 * 及 & 的说明:(同级运算,由右向左)
若pa=&a (将 a 的地址赋给指针变量pa), 则
&*pa &(*pa) &(a) & a
*&a *(&a) *pa a
(*pa)++ a++
*pa++ *(pa++)
注意:此时pa不再指向a
*
void Swap(int *x,int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
main()
{
int a, b;
a = 15;
b = 8;
Swap( &a, &b );
printf("a=%d,b=%d",a,b);
}
void Swap(int x,int y)
{
int temp;
temp = x;
x = y;
y = temp;
}
main()
{
int a, b;
a = 15;
b = 8;
Swap(a, b);
printf("a=%d,b=%d",a,b);
}
程序 1
程序 2
【例9.3】编写函数实现两数的互换。
实 参
形 参
结果有何不同?
第九章 指 针
主调函数
被调
函数
*
简单变量作函数参数
图 Swap函数调用前后参数变化的示意图
第九章 指 针
15
8
15
8
15
8
15
15
8
8
15
8
15
15
a) 调用Swap函数
(b)执行Swap函数
c) 从Swap函数返回
temp
x
y
b
main
函数
Swap
函数
①
②
③
a
a
a
temp
temp
x
x
y
y
b
b
*
指针变量作函数参数
第九章 指 针
15
8
8
15
&a
&b
&a
&b
15
(a) 调用Swap函数
(b) 执行Swap函数
a
a
b
b
*x
*x
*y
*y
y
y
x
x
&a
&b
main
函数
Swap
函数
temp
temp
②
①
③
图 用指针变量作函数参数实现两数互换函数的示意图
*
swap函数的几种错误形式(1/3)
参数单向传递
void Swap(int x, int y)
{
int temp;
temp = x; /*x,y为内部变量*/
x = y;
y = temp;
}
第九章 指 针
*
swap函数的几种错误形式(2/3)
参数单向传递
void Swap(int *p1, int *p2)
{
int *p;
p = p1; /*p1,p2为内部变量*/
p1 = p2;
p2 = p;
}
第九章 指 针
*
swap函数的几种错误形式(3/3)
指针 p 没有确切地址
void Swap(int *p1, int *p2)
{
int *p; /*指针p未初始化*/
*p = *p1;
*p1 = *p2;
*p2 = *p;
}
第九章 指 针
*
谢谢观看
$$