一、前言
我们写的C文件,会经过预编译,编译,汇编,链接,最后生成我们的可执行程序,而在预编译期间,就会把#define定义的标识符全部替换成定义的内容。
二、#define定义标识符
#define MAX 2
int main()
{
int x = 3;
printf("%d\n", x + MAX);
return 0;
}
在预编译期间,系统会自动把代码里的MAX替换成#define定义的MAX的值,就变成了:x + 2。
这里要注意一点:定义的标识符后面最后不要写分号,可能会有bug
举个栗子:
#define MAX 2;
int main()
{
int x = 3;
if (x >= 0)
x += MAX;
else
x -= MAX;
printf("%d\n", x);
return 0;
}
这个代码走进if语句,执行x += MAX,而(MAX == 2;),替换之后就变成了(x += 2;;),一个分号表示一条语句,后面再加一个分号,则表示另一条语句了,这时,else语句就不和if语句是一对了,所以就会报错,,如果加上括号,就不会报错,当然,最好还是不要加上分号。
三、#define定义宏
#define还可以把参数替换到文本中去,这种实现通常叫做宏。
举个栗子:
#define PRINT(x) x + x
int main()
{
int x = 5;
printf("%d\n", PRINT(x));
return 0;
}
在经过预编译后,代码就变成:
#define PRINT(x) x + x
int main()
{
int x = 5;
printf("%d\n", x + x);
return 0;
}
定义宏的时候要注意几个点:
1、参数左边的括号必须和宏名字紧邻,不能有空格
2、注意运算顺序,多加括号
#define PRINT(x) x * x
int main()
{
int x = 5;
printf("%d\n", PRINT(x + 1));
return 0;
}
例如这个代码,它执行的结果是11,因为,经过预编译后,代码就变成:
#define PRINT(x) x * x
int main()
{
int x = 5;
//printf("%d\n", PRINT(x + 1));
printf("%d\n", x + 1 * x + 1);
return 0;
}
当然,也不是说只要把每个参数括起来就没问题了,再举一个例子:
#define MULTIPLICATION(x) (x) + (x)
int main()
{
int x = 3;
printf("%d\n", 10 * MULTIPLICATION(x));
return 0;
}
替换之后就成了:
#define MULTIPLICATION(x) (x) + (x)
int main()
{
int x = 3;
//printf("%d\n", 10 * MULTIPLICATION(x));
printf("%d\n", 10 * 3 + 3);
return 0;
}
所以,还需要在外层再加一层括号,当然,这些问题最主要是自己注意运算顺序就可以.。
四、#与##
一、#
一个#右边加一个宏参数,则是把这个参数变成字符串,而不会被替换成值
#define PRINT(str,format) printf(""#str" is " format " \n ",str)
int main()
{
char str[20] = "hello world";
PRINT(str,"%s");
return 0;
}
首先,前面一对引号中间什么没有就不打印,再就是#str会把它变成字符串"str",之后就是" is ",接下来就是format被替换成"%s",用来打印str字符串,最后就是遇到字符串"\n",输出结果为str is hello world
二、##
##是将两端的部分合在一起
#define PRINT(string1,string2) string1##string2
int main()
{
int classmate = 20;
printf("%d\n", PRINT(class, mate));
return 0;
}
把class和mate合在一起就变成classmate,就是一个局部变量,结果就是20。
五、#undef取消宏定义
#define PRINT(string1,string2) string1##string2
int main()
{
int classmate = 20;
printf("%d\n", PRINT(class, mate));
#undef PRINT
printf("%d\n", PRINT(class, mate));//该行输出不了
return 0;
}
只需要在#undef后面加上你想取消的宏定义就行,后面要想再使用这个宏就不行了。
六、条件编译
这个其实和if,else if,else语句差不多
#define TRUE 1
#define FALSE 0
int main()
{
int x = 10;
#if TRUE
x += 2;
printf("%d\n", x);
#elif FLASE
x -= 2;
printf("%d\n", x);
#else
printf("%d\n", x);
#endif
return 0;
}
如果把第一行注释掉。那么只执行#else语句,因为FLASE的值为0,0为假,不执行。
#if使用后,必须在加上#endif,否则报错
还可以用来判断是否被定义,可以用来防止头文件被重复定义
#define COMPARE
int main()
{
int x = 10;
int y = 20;
#if defined(COMPARE)
printf("%d\n", x > y ? x : y);
#endif
return 0;
}
判断该标识符是否被定义,可以不定义标识符的值
还有另一种写法:
#define COMPARE
int main()
{
int x = 10;
int y = 20;
#ifdef COMPARE
printf("%d\n", x > y ? x : y);
#endif
return 0;
}
这些是定义了就执行,那没定义就执行的语句怎么写呢?
#define COMPARE
int main()
{
int x = 10;
int y = 20;
#if !defined(COMPARE)
printf("%d\n", x > y ? x : y);
#endif
return 0;
}
另一种写法就是:
#define COMPARE
int main()
{
int x = 10;
int y = 20;
#ifndef COMPARE
printf("%d\n", x > y ? x : y);
#endif
return 0;
}
所以,为了防止头文件被重复使用,我们可以这么写:
#ifndef _MAIN_
#define _MAIN_
#include<stdio.h>
#endif
也可以写成:
#pragma once
七、补充
其实,有些预定义符号是语言内置的
__FILE__ 表示进行编译的源文件
__DATE__ 表示文件被编译的日期
__TIME__ 表示文件被编译的时间
__LINE__ 表示文件当前的行号
__STDC__ 表示如果编译器遵循ANSIC标准,其值为1,否则未定义
int main()
{
printf("%s\n", __FILE__);
printf("%s\n", __DATE__);
printf("%s\n", __TIME__);
printf("%d\n", __LINE__);
printf("%d\n", __STDC__);//vs2019编译器未定义,
return 0;
}
#line 是强制定义新的行号和被编译的文件名
int main()
{
#line 2 "file.c"
printf("%d\n", __LINE__);
printf("%s\n", __FILE__);
printf("%d\n", __LINE__);
return 0;
}
#line可以用在函数里面,也可以用在外面。
#error是用于生成一个编译错误信息消息,只需要在后面加上你想报错的信息即可,那肯定会有人问了,既然用这个东西就是报错,那还用它干嘛?其目的当然就是保证程序是按照你所设想的那样进行编译的。
如果程序比较大,有些宏是系统定义的还是外部指定的不确定,那就可以用这个进行编译:
//#define COMPARE
int main()
{
#ifndef COMPARE
#error COMPARE Undefined
#endif
return 0;
}
如果定义了,就不会报错,如果没有,那就报错。
以上就是关于预处理指令的讲解,如有错误,望各位指出!