【零基础C语言】预处理解析

本文详细解释了C语言中预定义符号的使用,#define宏的定义、参数传递、字符串字面量转换以及##运算符的应用,还讨论了命名约定和条件编译的概念。
摘要由CSDN通过智能技术生成

 

预定义符号

 c语言中设置一些预定的符号,我们可以直接使用

 //列:
__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义

 / 续行符号

int main()
{
	printf("file = %s\nline = %d\ndate = %s\ntime = %s\n", \
		__FILE__, __LINE__, \
		__DATE__, __TIME__);

	return 0;
}

#define 用法

 //定义常量
#define max 100
int main()
{
	printf("%d\n", max);
	return 0;
}
 //给关键字创建一个别的名称
#define i int
i main()
{
	i a = 10;
	printf("%d\n", a);
	return 0;
}

我们平时使用代码时结尾都是以;,那么#define后面需不需要加上呢?

#define min 1;
#define max 1
int main()
{
	printf("%d\n", min); //err
    printf("%d\n", max);
	return 0;
}

 答案是不能因为在预处理阶段,编译器会删除所有的#define,并且替换定义的变量
 拿上面的代码举例
 下面是预处理替换后的代码

int main()
{
	printf("%d\n", 1;);
	printf("%d\n", 1);
	return 0;
}

#define 定义宏 - 可以传入参数

 //比如: 
#define add(x) x * x
int main()
{
	printf("%d\n", add(2 + 1));
  return 0;
}

这段代码会输出多少?有木有小伙伴算的是9,毕竟3*3是9,那么这样就错了
下面我们一起来看看

首先我们前面了解到#define的内容会直接被替换
替换后

int main()
{
	printf("%d\n", 2 + 1 * 2 + 1);
	return 0;
}
// 根据优先级, 2+(1*2)+1 == 5
// 这种直接的替换往往也会带来一些小麻烦,要想解决也很容易,只要勤加()就行了
#define add(x) (x)*(x)
int main()
{
	printf("%d\n", add(2 + 1));
	return 0;
}
#endif
 //这样就不用担心优先级带来的麻烦了

 //这样就真的改好了吗,我们来看下面一段代码
#define add(x) (x)+(x)
int main()
{
	printf("%d\n", 10 * add(5)); //我们想要的结果是100
	return 0;
}

// 输出的确实55,这还是犯了与上面同样的问题,我们的()加少了
// 最好的方案是在外面一层也加上

#define add(x) ((x)+(x))
int main()
{
	printf("%d\n", 10 * add(5));
	return 0;
}

 带有副作用的宏参数

 举例:

#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main()
{
	int a = 3;
	int b = 10;
	int z = MAX(a++, b++);
	printf("%d\n %d\n %d\n", a, b, z);
	return 0;
}

 //由上面的函数我们来进行替换
int main()
{
	int a = 3;
	int b = 10;
	int z = ((a++) > (b++) ? (a++) : (b++));
	// 先执行(a++)>(b++)  -  a=4, b=11;
	// 因为a<b, 所以执行表达式2(b++) - a=4,z=11,b=12
	printf("%d\n%d\n%d\n", a, z, b);
	return 0;
}
 //这就是宏参数的副作用,如果换成函数又如何呢?
int fution(int a, int b)
{
	return a > b ? a : b;
}

int main()
{
	int a = 3;
	int b = 10;
	int z = fution(a++, b++);
	printf("%d\n%d\n%d\n", a, z, b);
	return 0;
}
 //我们可以看见z的结果是10,而a,b的结果分别加了1,函数在传参后函数中a与b不再改变。

宏替换的规则

  • 1.在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先被替换。
  • 2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。
  • 3. 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:

  • 1. 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
  • 2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
     

 宏函数的对比

// 宏函数常常应用于比较简单的运算

// 如:比较大小

// 宏写

#define MAX(a, b)  ((a)>(b)?(a):(b))
int main()
{
	printf("%d\n", MAX(2, 3));
	return 0;
}

// 函数写

int MAX(int a, int b)
{
	return a > b ? a : b;
}
int main()
{
	printf("%d\n", MAX(2, 3));
	return 0;
}

为什么使用宏来运算而不使用函数
1. 相比较函数的调用,直接替换的计算工作时间更快
2. 宏与类型无关,你可以比较任意类型,然而函数却需要更改返回类型

宏函数的劣势
1.使用宏的时候,就相当于插入一段定义的代码,如果宏过长会导致代码太多,不好观察
2.宏无法调试
3.宏可能会带来优先级的问题,与类型也无关,不够严谨

 #和##

 #运算符

 //#运算符
// #运算符所执行的操作可以将宏中的一个参数转换为字符串字面量

//如:我们有一个变量 int a = 20;
//我们想要使用宏然后打印出 the num of a is 20.

#define PRINT(n) printf("the num of "#n " is %d",n);
int main()
{
	int a = 20;
	PRINT(a);
	return 0;
}

当我们按照下⾯的⽅式调⽤的时候:
PRINT(a);//当我们把a替换到宏的体内时,就出现了#a,⽽#a就是转换为"a",这时⼀个字符串
代码就会被预处理为:
printf("the num of "a " is %d",a);

 ##运算符

#define FUNTION_MAX(type) \
type type##_max(type a, tpye b)\
{                              \
      return (a > b ? a : b); \
}

// 这样使用后我们可以传入不同类型的参数了

 命名约定

 我们一般宏的使用命名时都全部大写,对于函数的应用却不全部大写

#undef

这条命令可以移除一个宏定义 

#define MAX 100
int main()
{
	printf("%d\n", MAX);
#undef MAX
	printf("%d\n", MAX); //err
	return 0;
}

条件编译
有时我们需要用一段代码,但用后后面可能还要使用,我们希望保留,并且设定一个开关,这就是条件编译

#define __DEBUG__
int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		arr[i] = i + 1;
#ifdef __DEBUG__
		printf("%d ", arr[i]);
#endif
	}
	return 0;
}
// 这样之后我们只有在前面定义 __DEBUG__ 我们才可以使用中间的代码

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值