预处理:
预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。程序员在程序中用\\
预处理命令来调用这些功能。\\
______使用预处理功能便于程序的修改、阅读、移植和调试,也便于实现模块化程序设计。\\
===宏定义:===
1. 无参宏定义:(无参宏的宏名后不带参数)
定义形式:#define 标识符 字符串 -->字符串可以是常量,表达式,格式串
源程序在编译时,将先由预处理程序进行宏替换,然后再进行编译\\
宏定义注意事项:
1. 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,
字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查,如有错
误,只能在编译已被宏展开后的源程序时发现
2. 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。
3. 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束,如要终止其作用域可以使用#undef命令。
4. 宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换-->类似字符串
5. 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换。
6. 习惯上宏名用大写字母表示,以便于与变量区别,但也允许用小写字母
7. 可以宏定义表示数据类型,使书写方便。
8. 对输出格式作宏定义可以减少书写麻烦,便于调试程序。
2. 带参宏定义:
定义形式:#define 宏名(形参表) 字符串
调用形式: 宏名(实参)
例如:
#define M(y) y*y+3*y /*宏定义*/
……
k=M(5); /*宏调用*/
……
在宏调用时,用实参5去代替形参y,经预处理宏展开后的语句为:k=5*5+3*5
带参宏定义注意事项:
1. 带参宏定义中,宏名和形参表之间不能有空格出现。
2. 带参宏定义中,形参不分配内存单元,因此不必作类型定义,而实参需要做类型说明
3. 在宏定义中的形参是标识符,而在宏调用中的实参可以是表达式。
#define SQ(y) (y)*(y)
main(){
int a,sq;
printf("input a number: ");
scanf("%d",&a);
sq=SQ(a+1);
printf("sq=%d\n",sq);
}
上例中第一行为宏定义,形参为y。程序第七行宏调用中实参为a+1,是一个表达式,在宏展开时,
用a+1代换y,再用(y)*(y) 代换SQ,得到如下语句:
sq=(a+1)*(a+1);
这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再赋予形参。而宏代换中对实参表
达式不作计算直接地照原样代换。
4. 在宏定义中,字符串内的形参通常要用括号括起来以避免出错。
5. 带参的宏和带参函数很相似,但有本质上的不同,除上面已谈到的各点外,把同一表达式用函数处理与用宏处理两者的结果有可能是不同的。
6. 宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。看下面的例子。
#define SSSV(s1,s2,s3,v) s1=l*w;s2=l*h;s3=w*h;v=w*l*h;
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);
}
程序第一行为宏定义,用宏名SSSV表示4个赋值语句,4 个形参分别为4个赋值符左部的变量。
在宏调用时,把4个语句展开并用实参代替形参。使计算结果送入实参之中。
为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两
边也应加括号。
==宏定义表示数据类型与typedef定义数据说明符的区别:==
宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名,被命名的标识符具有类型定义说明的功能。
请看下面的例子:
#define PIN1 int *
typedef (int *) PIN2;
从形式上看这两者相似, 但在实际使用中却不相同。
下面用PIN1,PIN2说明变量时就可以看出它们的区别:
PIN1 a,b;在宏代换后变成:
int *a,b;
表示a是指向整型的指针变量,而b是整型变量。
然而:
PIN2 a,b;
表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。
由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是作字符代换。在使用时要分外小心,以避出错。
===文件包含:===
文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一\\
个目标文件。\\
1. 文件包含包括""和<>:\\
""号使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。用户编程时\\
可根据自己文件所在的目录来选择某一种命令形式。\\
<>号:使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文\\
件目录去查找;\\
文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。\\
===条件编译:===
条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。
#define NUM ok
#ifdef NUM
printf.....1
#else
printf......2
#endif
如果ifdef指定位置数据被#define定义过,则执行程序段1否则执行程序段2\\
如果ifndef指定位置没有被#define定义过,则执行程序段1否则执行程序段2\\
===例子:===
经常会在cpp文件中见到类似于下面的定义:
#ifndef A
#define A
#ifdef /_/_cpluspluss //此处为两个下划线
extern "C"{
#endif
void cpp_fun();
#ifdef /_/_cplusplus //此处为两个下划线
}
#endif
#endif
其中/_/_cplusplus(此处为两个下划线)是C++编译器的保留宏定义,extern“C”告诉C++编译器括弧里边的\\
东西按照C的obj文件格式编译,链接的话按照C的命名规则去找。
总结:C和C++对函数的处理方式是不同的,extern "C"是使C++能够调用C写的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。
阅读(1509) | 评论(0) | 转发(0) |