#7:C语言复习:编译预处理;

一、宏定义:


                            将一个标识符定义为一个替换文本,在编译过程中,将所有宏名都用相应的文本去替换。
 

一。无参宏定义:


                                1.    一般定义形式:

                  #define 标识符 替换文本        一般标识符用大写字母;   

                                                                 替换文本可以是任意东西各元素必须空格隔开;


例:

计算    s=3(y^2+3y)+4(y^2+3y)+5(y^2+3y);

#include<stdio.h>
#define M (y*y+3*y)
void main()
{
	int s,y;
	printf("请输入一个数(以便于计算方程):\n");
	scanf("%d",&y);
	s=3*M+4*M+5*M;
	printf("s=%d\n",s);
}

无参宏定义注意的问题:


                                1.    习惯上宏名用大写字母,

                                2.    用替换文本进行替换的时候,是直接的( 文本 )替换,所以一般需要加小括号;

                                3.    宏定义行末不加分号,若加上了分号,连同分号一起替换;

                                4.    一个#define只能定义一个宏,且只能在一行中定义一个宏;

                                若需要定义多个宏,就需要多个#define,并写在多行上;

                                5.    宏定义时若一行写不下,可用“\”续行:

                                #define PI 3.1415\
                                926;

                                6.    宏定义可以出现在源程序的任何地方,但通常写在函数之外,其作用域为从宏定义起到源文件结束;

                                7.    若要结束其作用域,可使用#undef,用法:
                    
                                #undef PI

                                8.    宏名在源程序中若用括号括起来,不替换:(纯文本)

例:

宏替换的选择性;

#include<stdio.h>
#define PI 3.1415926
void main()
{
	printf("PI 是 %9.7f.\n",PI);	//被括号里的引号扩住的PI,直接作为字符输出,后面的PI被替换;
}
                                9.    宏定义允许嵌套,在宏定义的替换文本中,可以使用已经定义过的宏名, 展开时层层替换

                                #define PI 3.14
                                #define S PI*r*r

例:

用无参宏定义表示常用的数据类型和输出格式:

#include<stdio.h>
#define INTEGER int
#define REAL float
#define P printf
#define D "%d\n"
#define F "%f\n"
void main()
{
	INTEGER a=5,c=8,e=11;
	REAL b=3.8,d=9.7, f=21.08;
	P(D F,a,b);
	P(D F,c,d);			//	宏替换后,printf会自动把"%d\n""%f\n"合并成"%d\n%f\n";

	P(D F,e,f);			//	D F 之间一定要有空格,否则无法识别DF;
}

二。带参宏定义:

                                宏定义中的参数称为形式参数,引用参数时给出的参数为实际参数

                                定义形式:

                            #define 宏名(形参表) 替换文本

                            展开时,先把宏引用替换为替换文本,再将替换文本中的形参用实参代替;

                            #define M(y) y*y+3*y

                            k=M(5);                            实际指的是    k=5*5+3*5;

例:

带参宏定义求两者中的大者:

#include<stdio.h>

#define MAX(a,b) (a>b)?a:b		//	宏名与其后的(不得有空格,(即不能写成MAX ()	)否则变为无参数定义;

					//	带参定义中,替换文本的形参通常用括号括起来,避免出错,即((a)>(b))?(a):(b);
void main()
{
	int x,y,max;
	printf("请输入两个数:\n");
	scanf("%d%d",&x,&y);
	max=MAX(x,y);
	printf("max=%d\n",max);
}

例:

一个宏定义代表多个语句:

#define SSSV(s1,s2,s3,v) s1=l*w;	s2=l*h;	s3=w*h;	v=w*l*h;			

#include <stdio.h>
void main()
{
	int l=3,w=4,h=5,sa,sb,sc,vv;
	SSSV(sa,sb,sc,vv);
	printf("sa=%d\nsb=%d\nsc=%d\nvv=%d\n",sa,sb,sc,vv);
}

带参宏定义和函数的区别:

                                    1.    参数性质不同,带参宏定义不必说明其性质,也不分配空间,函数需说明类型,也要分配空间

                                    2.    实现方式不同,宏展开是编译时预处理完成的,不占用运行时间,函数调用占用时间;

                                    3.    参数传递不同,若为实参传递,带参宏只简单的字符替换,不计算表达式的值函数调用先计算表达式,在代入,

                                    4.    返回值不同。  带参宏定义无返回值,函数有返回值;


例:

带参宏定义是实参是表达式:

#define		SQ(y)	(y)*(y)
#include <stdio.h>
void main()
{
	int a,sq;
	printf("请输入一个数:\n");
	scanf("%d",&a);

	sq=SQ(a+1);				//	在宏展开时,先用(y)*(y)替换SQ(a+1),再用 a+1 代替形参y;
	
	printf("sq=%d\n",sq);			//	完全展开,直接替换;
}


例:

函数与带参宏定义的进一步比较:

#include<stdio.h>


#define SQ_MACRO(y) ((y)*(y))
int SQ_fun(int n); 

int main()
{
	int i=1;
	printf("SQ_fun:\n");

	while(i<=5)
	{
		printf("%d\n",SQ_fun(i++));	//	函数在执行的时候,把i的值给形参,自加一后,循环五次;
	}

	i=1;					//	循环归零;

	printf("SQ_MACRO:\n");

	while(i<=5)
		printf("%d\n",SQ_MACRO(i++));	//	宏引用的时候,SQ_MACRO(i++)被替换为,((i++)*(i++)),取出i后,i自加两次;

	return 0;
}

int SQ_fun(int y)
{
	return((y)*(y));
}

条件编译:


                            在一定条件下,仅仅编译一部分语句,不满足条件编译另一部分;

                            一套程序要产生不同的版本,(演示版本,实际版本)避免重复定义时用;


条件编译的三种形式:

                                    1.    #ifdef    标识符
                                              程序段1
                                           #else
                                              程序段2
                                           #endif
                                       
                                或者:
                                       
                                           #ifdef 标识符
                                              程序段
                                           #endif

         说明:    如果标识符是已被#define命令定义过的宏名,就对程序段1进行编译;否则对程序段2进行编译。

例:

根据需要设置条件编译,使之能控制对一些提示信息的输出:

#include<stdio.h>
#define DEBUG
void main()
{
	int a=4;
	#ifdef DEBUG				//	因为编译了 DEBUG 所以输出下面语句	
	printf("现在是在编译……\n");
	#else					//	若没有第一行的宏定义 DEBUG ,程序输出 a=4.
		printf("a=%d.",a);
	#endif					//	条件编译语句没有分号;
}

                                        2.    #ifndef 标识符
                                                  程序段1
                                               #else
                                                  程序段2
                                              #endif

         若标识符 未被定义,对程序段1进行编译,否则对程序段2进行编译。 与第一种形式正好相反。

                                        3.    #if 常量表达式
                                                  程序段1    
                                               #else
                                                  程序段2
                                              #endif
 
           如果 常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。

例:

                   设置一个"开关"R ,用于识别输入值是半径还是边长,实现求圆或者正方形的面积

#include<stdio.h>
#define R 0
#define S 2
void main()
{
	float c,r,s;
	printf("请输入一个数:\n");
	scanf("%f",&c);

	#if R					//	若R未进行编译,随机数一样不为零,继续执行程序段1;只有定义R=0,才执行语句2;
		r=3.1415926*c*c;
		printf("圆的面积为: %f\n",r);

	#else					//	当且仅当定义R=0.执行。
		s=c*c;
		printf("正方形的面积为: %f\n",s);

	#endif					//	不可缺少。
}


注意:

                       1.    条件编译语句后面的#if后面的条件必须是常量表达式,大多数由#define定义的符号常量,便于切换不同的版本;

                        2.    条件编译检查某些变量的中间结果:

                        #define FLAG 1
                        #if FLAG    
                            printf("a=%d",a);
                        #endif


文件包含:


                            1.    #include <....>        只在系统指定的目录查找制定的文件;

                                 一般调用系统提供的使用各式一,节省时间。

                            2.    #include "...."        先在当前工作目录中寻找制定的头文件,找不到再按标准去找;

                                要包含用户自己写的头文件。若头文件不在当前文件,可加路径,如:
                                #include "D:\boli\AppFace.h"


说明几点:

                            1.    不要在头文件中定义全局变量和函数;

                            2.    一个#include 只能包含一个头文件,若有多个文件,需要用多个#include

                            3.    文件包允许嵌套,即在一个头文件中可以包含另一个头文件。但不能重复包含的情况

                                 #include"a.h"
                                 #include"b.h"

      若a.h和b.h都又包含了一个头文件x.h,即x.h被包含了两次。如果x.h中定义了数据类型,就会出现重复定义错误。

                            4.    当某个头文件变化的时候,包含该头文件的源程序也发生变化,需要重新编译。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值