亚洲鞋业资讯网,最新潮鞋资讯分享!

微信号:weixin888

详解C语言—预处理

时间:2024-04-01 21:03人气: 编辑:亚洲鞋业资讯网

目录

1、预处理

(1)预定义符号介绍

(2)预处理指令 #define

#define 定义标识符: 

#define 定义宏:

#define 替换规则

(3)预处理操作符#

(4)预处理操作符##

 (5)带副作用的宏参数

 (6)宏和函数对比

2、命名约定

3、预处理指令 #undef

4、命令行定义

5、条件编译 

(1)单分支#if:

(2)多分支#if:

(3)判断是否被定义

(4)嵌套指令

 6、文件包含

头文件被包含的方式:

嵌套文件包含:

小结


1、预处理

(1)预定义符号介绍

__FILE__      //进行编译的源文件

__LINE__     //文件当前的行号

__DATE__    //文件被编译的日期

__TIME__    //文件被编译的时间

__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义
这些预定义符号都是语言内置的,程序员可直接使用。

#include 

int main()
{
	printf("%s\n", __FILE__);
	printf("%d\n", __LINE__);
	printf("%s\n", __DATE__);
	printf("%s\n", __TIME__);
	//printf("%d\n", __STDC__);//当前VS是不支持ANSI C
	return 0;
}

(2)预处理指令 #define

#define 定义标识符: 

#define 宏名 值或代码片段

#define 是用来定义宏的预处理指令。宏是一种在源代码中定义的符号,可以用来代表一个常量、一个表达式或一段代码片段。宏的定义通常在源代码文件的顶部,以便在编译之前被预处理器处理。  

#define MAX 1000
#define reg register          //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)     //用更形象的符号来替换一种实现
#define CASE break;case        //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
                          __FILE__,__LINE__ ,  \
                          __DATE__,__TIME__ )  

#define 定义宏:

带参数的宏:宏也可以带有参数,允许你创建可重用的代码片段。例如:

#define SQUARE(x) ((x) * (x))

 SQUARE 宏接受一个参数x,并返回x的平方。在使用时,你可以这样调用宏:int result = SQUARE(5);,它会被展开为 int result = ((5) * (5));

 接下来我们看一个例子:

#define SQUARE(x)  x * x

如果我们写成这样不带括号, 对函数传入参数 5 + 1 :SQUARE( 5 + 1 );
得到的结果将是 11,而不是36,计算过程:5 + 1 * 5 + 1 得到结果为11。

#define SQUARE(x) (x) * (x)

当我们加上括号,计算过程:(5+1)*(5+1) 得到结果为36.  

这样定义看似没有问题了,我们再看一个例子: 

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

 这种情况下计算过程为 10 * (5) + (5)); 得到结果为55,并不是我们想要的100。

这时再对参数整体添加一对括号即可解决问题:

#define SQUARE(x) ((x) * (x))

通过这几个例子我们理解了为什么要加那些那些括号。 

所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。  

#define 替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

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

注意:

宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。

当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

  • 解释:当预处理器搜索由#define定义的符号时,它并不会搜索或替换字符串常量中的内容。这是因为字符串常量在C中是不可更改的文本序列,不应受到#define定义的符号的影响。
  • #include 
    
    #define VALUE 42
    
    int main() {
        int x = VALUE;
        printf("The value of x is: %d\n", x);
        
        printf("This is a string with VALUE: \"%d\"\n", VALUE);
        
        return 0;
    }

    在上面的示例中,我们定义了一个名为VALUE的符号,它被定义为整数42。然后,我们在程序中使用了这个符号。请注意以下两个地方:

  • 在第5行,我们使用了VALUE来初始化整数变量x。在这里,#define定义的符号VALUE被替换为42,因此x的值是42

  • 在第7行,我们在字符串常量中包含了VALUE。在这里,VALUE并不会被替换为42,而是保持不变。因此,字符串中的内容是"This is a string with VALUE: \"VALUE\""

  • 所以,尽管#define定义的符号VALUE在程序中的某些地方被替换为其定义的值,但它并不会影响字符串常量中的内容。字符串常量中的文本保持不变,不受符号替换的影响。这是为了确保字符串常量的内容始终保持不变。

(3)预处理操作符#

我们由一个例子来引入讲解: 

int main()
{
	int a = 20;
	printf("the value of a is %d\n", a);
	
	return 0;
}

如何通过#define实现上述代码中printf语句的相同功能呢?

通过前面的学习可以很轻松实现#define定义宏,代码如下: 

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

但我们的输出语句是the value of n is 20,并不是我们传入的参数 a ,

那怎么修改 #define 把参数插入到字符串?

我们通过添加双引号将“参数前后字符串”分隔成“独立的两个字符串”。再在参数前添加 # 使其在宏展开时被替换为参数 n

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

如果我们要输出不同格式,那怎么修改#define呢?

添加一个参数代替格式化字符即可解决。

#define Print(n,format) printf("the value of "#n" is " format "\n",n)
int main()
{
	float f = 4.5f;
	printf("the value of a is %f\n", f);
	Print(f, "%f");
	return 0;
}

(4)预处理操作符##

##可以把位于它两边的符号合成一个符号。

它允许宏定义从分离的文本片段创建标识符。

看下面的例子就明白了: 

#define CAT(x,y) x##y
int main()
{
	int Class110 = 110;
	printf("%d\n", Class110);
	printf("%d\n", CAT(Class, 110));
	return 0;
}

两种输出形式结果一样:  

 (5)带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能 出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

例如:

x+1;//不带副作用
x++;//带有副作用

我们看下面的例子:

#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{
	int a = 5;
	int b = 6;
	int c = MAX(a++, b++);
	printf("a=%d\nb=%d\nc=%d\n", a, b, c);
	return 0;
}

 替换宏的文本:c=MAX( (a++)>(b++) ? (a++):(b++) ) 首先判断 ab 的大小,之后a与b自增:a=6 b=7b 大于 a 则 c 被赋值为MAX的返回值 b的值7 即( c=7 ), 赋值之后b进行自增,b=8

输出结果: 

(6)宏和函数对比

宏通常被应用于执行简单的运算。

比如:在两个数中找出较大的一个

#define MAX(a,b) ((a)>(b)?(a):(b))

int Max(int x, int y)
{
	return (x > y ? x : y);
}

那为什么不用函数来完成这个任务?

原因有二:

1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。

所以宏比函数在程序的规模速度方面更胜一筹

2. 更为重要的是函数的参数必须声明为特定的类型。

所以函数只能在类型合适的表达式上使用。反之这个宏可以适用于整形、长整型、浮点型等可以用于>来比较的类型。

宏是类型无关的

宏的缺点:当然和函数相比宏也有劣势的地方:

  1. 宏由于类型无关,也就不够严谨。
  2. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  3. 宏是没法调试的。
  4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
  5. 宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。比如下面的例子:
#define Malloc(num,type) (type*)malloc(num*sizeof(type))

int main()
{
	int* p = (int*)malloc(126 * sizeof(int));
	int* p = Malloc(126, int);
	return 0;
}

 宏和函数详细对比: 

2、命名约定

一般来讲函数和宏的使用语法很相似,所以语言本身没法帮我们区分二者 

一般习惯宏名全部大写,函数名不要全部大写。

#define MAX(x,y) ((x)>(y)?(x):(y))

int Max(int x, int y)
{
	return x > y ? x : y;
}

3、预处理指令 #undef

这条指令用于移除一个宏定义。  

#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。

 示例:

#define MAX(x,y) ((x)>(y)?(x):(y))

int main()
{
	int c = MAX(3, 5);
	printf("%d\n", c);
#undef MAX
	c = MAX(5, -5);
	printf("%d\n", c);
	return 0;
}

 当我们运行时,程序报错

我们想要修改 c 的值,宏MAX已经失效了,所以程序报错“MAX”未定义。 

4、命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。
标签: [db:TAGS]  
相关资讯
热门频道

热门标签

官方微信官方微博百家号

网站简介 | 意见反馈 | 联系我们 | 法律声明 | 广告服务

Copyright © 2002-2024 欧亿●平台●官网-OE SPORTS 版权所有 网站地图 备案号:粤06094508号