C语言预处理器

C预处理器在程序执行之前查看程序,根据程序中的预处理指令,预处理器把符号缩写替换成其表示的内容。基本上他的工作就是文本替换

翻译程序的第一步

  • 首先,编译器把源代码中出现的字符映射到源字符集。
  • 第二,定位每个反斜杠后面跟着的换行符实例,并删除它们。将多个物理行替换成一个逻辑行
//两个物理行
printf("That is wond\
		erful!\n");
//转换成一个逻辑行:
printf("That is wonderful!\n");
  • 第三,编译器将文本划分成预处理记号序列、空白序列和注释序列,编译器将用一个空格字符替换每一条注释。
int/*这看起来并不是空格*/fox;
//会变成:
int fox;

明示常量:#define

使用#define来定义明示常量,也叫符号常量
预处理器指令从#开始运行,到后面的第一个换行符为止,也就是说指令的长度仅限于一行(逻辑行)。

#include<stdio.h>
#define TWO 2
#define OW "This is a\
	test.have a try" //反斜杠把该定义延续到下一行
#define FOUR TWO*TWO //预处理不做计算,只是简单替换 
#define PX printf("X is %d.\n",x)
#define FMT "X is %d.\n"
 
//在#define中使用参数--类函数宏,圆括号中可以有一个或多个参数。
#define SQUARE(X) X*X
#define PR(X) printf("The result is %d.\n",X);
 
//预处理粘合剂:##运算符
#define XNAME(n) x ## n
#define PRINT_XN(n) printf("x" #n " = %d\n", x ## n); 

int main(void)
{
	int x = TWO;
	
	PX;
	x = FOUR;
	printf(FMT,x);
	printf("%s\n",OW);
	printf("TWO:OW\n");
	
	x = SQUARE(x);
	PR(x);
	
	//宏定义只是简单替换,
	//这里变成:printf("The result is %d.\n", x + 2 * x + 2);  16+2*16+2==50	
	PR(SQUARE(x + 2));
	
	//这里变成:printf("The result is %d.\n", x++ * x++);  16*17==272	
	PR(SQUARE(x++));
	
	//##运算符
	int XNAME(1) = 14; //变成 int x1 = 14;
	int XNAME(2) = 20; //变成 int x2 = 20;
	int x3 = 30;
	PRINT_XN(1);       //变成 printf("x1 = %d\n", x1); 
	PRINT_XN(2);       //变成 printf("x2 = %d\n", x2); 
	PRINT_XN(3);       //变成 printf("x3 = %d\n", x3); 
	return 0;	
} 
/*
结果: 
	X is 2.
	X is 4.
	This is a       test.have a try
	TWO:OW
	The result is 16.
	The result is 50. 
	The result is 272.
	x1 = 14
	x2 = 20
	x3 = 30
*/

文件包含:#include

当预处理器发现#include时,会查看后面的文件名,并把文件的内容包含到当前文件中,即替换源文件中的#include指令。

#undef指令

#undef指令用于取消已定义的#define指令
假如有如下定义:

#define LIMIT 100

然后又有如下指令:

#undef LIMIT

将移除上面的定义,可以将LIMIT重新定义一个新值,即使原来没有定义LIMIT,取消LIMIT定义仍然有效,如果想使用一个名称,又不确定之前是否用过,为了安全起见,可以使用#undef指令取消改名字的定义。

条件编译

可以使用这些指令告诉编译器根据编译时的条件执行或忽略代码块。

#include<stdio.h>
#define TEST 
#ifdef TEST        //如果已经用#define定义了MAVIS,则执行下面指令 
	#include "string.h"
	#define NUM 999
#else              //否则 
	#include "math.h"
	#define NUM 666
#endif             //条件结束,必须以#endif结尾 

int main(void)
{
	#ifdef TEST
		printf("this is a test: %d\n",NUM);
	#endif
	
	#ifndef SIZE   //#ifndef 和#ifdef指令的用法类似,不过它的意思相反,如果未定义SIZE,则执行下面代码段 
		#define SIZE 2
		puts("size is not defined!");
		printf("size = %d\n", SIZE);
	#endif 
	
	// #if指令很像C语言中的if
	#if SIZE == 1
		puts("size == 1");
	#elif SIZE == 2
		puts("size == 2");
	#else
		puts("size == 0");	
	#endif 
	
	/*较新的编译器提供另一种方法测试名称是否定义
		即,用#if defined (SIZE) 来代替 #ifdef VAX 
	*/
	#if defined (SIZE)
		puts("size!!!");
	#endif 
	return 0;
	/*
	结果: 
		this is a test: 999
		size is not defined!
		size = 2
		size == 2
		size!!!
	*/
}

预定义宏

#include<stdio.h>
void why_me();

int main(void)
{
	printf("The file is %s.\n", __FILE__);//当前源代码文件名的字符串字面量 
	printf("The date is %s.\n", __DATE__);//预处理的日期 
	printf("The time is %s.\n", __TIME__);//翻译代码的时间 
	//printf("The version is %ld.\n", __STDC_VERSION__);
	printf("This is line %d.\n", __LINE__);// 当前源代码所在的行号 
	printf("This function is %s,\n", __func__);//预定义标识符,不是预定义宏,当前所在函数的函数名称 
	
	why_me();
	
	return 0;	
} 

void why_me(){
	printf("This function is %s\n", __func__);
	printf("This is line %d\n", __LINE__);
}
/*
结果: 
	The file is C:\Users\LUO\Desktop\C\code-train\predef.c.
	The date is Sep 24 2020.
	The time is 20:14:14.
	This is line 10.
	This function is main,
	This function is why_me
	This is line 20
*/

泛型选择

在程序设计中,泛型编程指那些没有特定类型,但一旦指定一种类型,既可以转换成指定类型的代码。C++在模板中可以创建泛型算法,然后编译器根据指定的类型自动使用实例化代码。C11新增了一种表达式,叫作泛型选择表达式,可根据表达式的类型选择一个值。

-Generic(x, int: 0,float: 1, double: 2, default: 3)

_Generic是C11的关键字,第一个项是一个表达式,后面每个项都由一个类型、一个冒号和一个值组成。

#include<stdio.h>

#define MYTYPE(X) _Generic((X),int:"data is int",float:"data is float",double:"data is double",default:"other")

int main(void)
{
	int d = 4;
	
	printf("%s\n", MYTYPE(d)); //d是int类型, MYTYPE(4)得”int” 
	printf("%s\n", MYTYPE(d*2.0)); //d*2.0是double类型,, MYTYPE(8.0)得”double”  
	printf("%s\n", MYTYPE(3L)); //3L是long类型, MYTYPE(3L)得”other”
	printf("%s\n", MYTYPE(&d)); //&d是int *类型, MYTYPE(&d)得”other”
	
	return 0;
}
/*
结果: 
	data is int
	data is double
	other
	other
	
*/

内联函数(C99)

通常函数的调用都有一定的开销,因为函数的调用包括建立调用、传递参数、跳转到函数代码并返回。内联函数就是以空间换时间,使函数的调用更加快捷。
标准规定:具有内部链接的函数可以成为内联函数,还规定了内联函数的定义与调用该函数的代码必须在同一个文件中。因此最简单的办法是使用inlinestatic修饰。通常,内联函数应定义在首次使用它的文件中,所以内联函数也相当于函数原型。

#include<stdio.h>
inline static void test()
{
	puts("test!");
}
int main()
{
	...
	test();
	...
}
  • 由于并未给内联函数预留单独的代码块,所以无法获得内联函数的地址,另外,内联函数无法在调试器中显示。
  • 由于内联函数具有内部链接,所以在多个文件中定义同一个内联函数不会产生什么影响。
  • 如果多个文件都需要使用同一个内联函数,可以将它定义在h头文件中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SOC罗三炮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值