一、宏定义:
将一个标识符定义为一个替换文本,在编译过程中,将所有宏名都用相应的文本去替换。
一。无参宏定义:
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. 当某个头文件变化的时候,包含该头文件的源程序也发生变化,需要重新编译。