#include <stdio.h>
// NUM叫做宏名
// 6是用来替换宏名的字符串
#define NUM 6
#define mul(a, b) ((a)*(b))
void test() {
// 双引号中的NUM并不会被替换为6
//对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作
char *s = "NUMBER";
int a[NUM] = {1,2,3,4,5,6};
for (int i = 0; i<NUM; i++) {
printf("a[%d] = %d\n", i , a[i]);
}
}
int main(int argc, const char * argv[])
{
int a = mul(10, 10) / mul(2, 2);
// mul(a, b) (a)*(b)
// (10 * 10) / (2 * 2)
printf("%d", a);
return 0;
}
二、带参数的宏定义
1.一般形式
#define 宏名(参数列表) 字符串
2.作用
在编译预处理时,将源程序中所有宏名替换成字符串,并且将 字符串中的参数 用 宏名右边参数列表 中的参数替换
1> 宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串
2> 带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。
3> 计算结果最好也用括号括起来
下面定义一个宏P(a),作用是返回a的平方:
#include <stdio.h>
#define D(a) 2*a
int main ()
{
int b = D(3+4);
printf("%d", b);
return 0;
}
第7行将被替换成int b = 2*3+4;,输出结果:
#include <stdio.h>
#define Pow(a) (a) * (a)
int main(int argc, const char * argv[]) {
int b = Pow(10) / Pow(2);
printf("%d", b);
return 0;
}
注意第3行,没有用小括号扩住计算结果,只是括住了参数而已。第6行代码被替换为:
int b = (10) * (10) / (2) * (2);
简化之后:int b = 10 * (10 / 2) * 2;
5.与函数的区别
从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:
1> 宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
2> 函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率
===========
条件编译
============
在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译。
#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif
// main.c
// 条件编译
#include <stdio.h>
#define NUM -1
int main(int argc, const char * argv[])
{
#if NUM > 0
printf("NUM大于0");
#elif NUM == 0
printf("NUM等于0");
#else
printf("NUM小于0");
#endif
return 0;
}
===========
文件包含
============
1.第1种形式#include <文件名>
直接到C语言库函数头文件所在的目录中寻找文件
2.第2种形式 #include "文件名"
系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找
1.#include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,但是不允许递归包含,比如 a.h 包含 b.h,b.h 包含 a.h。
下面的做法是错误的
// #include "one.h"
#ifndef _ONE_H_
#define _ONE_H_
void one();
#endif
// #include "two.h"
#ifndef _TWO_H_
#define _TWO_H_
// #include "one.h"
#ifndef _ONE_H_
#define _ONE_H_
void one();
#endif
void two();
#endif