关于预处理指令的讲解

一、前言

我们写的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;
}

如果定义了,就不会报错,如果没有,那就报错。

以上就是关于预处理指令的讲解,如有错误,望各位指出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值