c语言写预处理器,C语言预处理器详解

1.预定义符号

下面这些是C语言的预处理器定义的符号,他们都是常量十进制数,或者是常量字符串。他们用来指示调试输出来源以及为编译程序加如时间信息。而STDC用来进行条件编译。其详细意义如下:

FILE:表示进行编译的源文件名

LINE:当前行在源文件中的行数

DATE:编译时候的日期

TIME:编译时候的时间

STDC:如果编译器遵循ANSI C 那么这个值就是1,否则未定义。

2. #define

2.1 #define 替换

在程序中我们可以使用#define来进行对源程序在预处理时期的替换。

最最常见的有:

#define uint unsigned int

#define uchar unsigned char

这样可以免去敲冗长的类型名,在预处理阶段所有的 uint 和 uchar 都会被分别替换为 unsigned int 和 unsigned char 。

注意:遇到上面这样的情况,最佳的选择是使用typedef来创建别名,而不是使用宏替换。

typedef unsigned int uint;

typedef unsigned char uchar;

2.2 #define 与 函数

有时候我们采用宏来充当函数,采用#define的一个好处是,对于简短的函数段,可以直接插入在程序中,而若使用函数的话会在调用函数时产生堆栈上的开销。其次有些时候,参数类型不明确所以用函数实现不够方便。

如:

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

这个定义能够求出最大值,作用类型可以使任何能够用>比较的类型。

##用来连接两个符号,举例如下:

#define ADD_NO(number) NO##number

这样 ADD_NO(1),将得到NO1,需要注意的是,你一定要确保在程序中,你定义了NO1这个变量。

#argument 被用来指代宏参数对应的字符串,下面一个例子能让你明白:

#define PRINT_INT(x) printf(“The value of” #x “is:%d\n”,x)

调用:

int a=3;

PRINT_INT(a+10);

结果为:

The value of a+10 is:3

2.3 副作用

副作用 1

观察上面的定义,每一个字符都用括号括起来了,这是因为,宏并不求值而只是简单的替换,带参数的宏定义也如此。

如果写成下面这样:

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

在程序中假如有这样的语句:

int a=MAX(1+2,2+3);

展开后得到:

int a=1+2>2+3?1+2:2+3;

这可能不是我们想要的结果。

所以记住一点,它只是替换,并不求值。

副作用 2

在考虑上面的MAX宏,我们用下面的方式调用:

c=MAX(a++,b++);

我们希望他像函数一样,在比较完大小后,a和b的值都能加1.但是我们展开后得到:

c=((a++)>(b++)?(a++):(b++));

显然,较大的那个变量将自加两次,这显然不是我们所渴望的。所以我们一定要注意自己调用的函数是不是真的是一个函数,因为我们无法成外观上区分他们到底本质上是不是一个宏。如getchar() 它就是一个宏

副作用 3

有时候,因为宏而产生的错误是很隐蔽的,让你难以发现,考虑:

#define PRINT_TWICE(ch) putchar(ch);putchar(ch)

我们希望这个定义能够帮我们打印一个字符两次,当我们用下面这样的方法调用时:

PRINT_TWICE(fgetc(fp));

从文件中读取一个字符,然后打印两次。但是上,他从文件中读取了两次取得两个字符,然后输入到标准输出。

综上,当在使用宏的时候,一定要警惕,它是否会产生上面提到的这类不易察觉的副作用。其次为了区别宏与函数,通常用全部大写字母来为宏命名。

2.4 #undef

当我们需要在重新定义一个宏,或者要移除一个宏的时候,可以使用下面这样的形式:

#undef name

2.5 分号加不加?

当我们用宏定义了一条完整的语句的时候,我们可能希望给它后面加上一个分号,这可能不会产生大的问题。其实我们在使用了宏以后习惯性的会在其后面加上一个分号,向普通的语句一样。永远记住宏做的工作是替换,你在定义它的时候在其后加了分号,那么在调用的时候就可以不用加分号了。如过你加了那么一个分号将产生一条空语句。

虽然一条空语句可能不会影响到程序的执行,你也不会察觉,但是有时候它可能会导致发生错误,举例如下:

#define PRINT(x) putchar(x);

if(...)

PRINT(x);

else

...

这仅仅是由于if语句因为下面只有一条语句所以没有加花括号,但是这个宏实际上是两条语句。当然这个问题可以在if后加上花括号来解决。

3. 条件编译

有的时候,程序会更具编译环境来有逻辑的进行编译,举例如下:

#define DEBUG 1

#if DEBUG

打印变量的值,程序的状态等

#endif

当我们在调试程序的时候,我们可以将DEBUG设置为1,但调试完毕后将他改为0,这样我们不必去删除分布于源文件中各个地方的打印状态的语句了。

条件编译提供了以下关键字:

#if,#elif,#else,#endif,#ifdef,#ifndef

其中前四个意义明确,没错就是你理解的那样,#elif 的意思就是else if。而#ifdef name是说如果定义了name,#ifndef name 是说如果没有定义name。

还有define(…)它的意义如下:

#if defined(…) 同 #idef …

#if !define(…) 同 #ifndef

4. 文件包含

当我们的程序需要依赖于起来的头文件的时候,我们使用#include 这样的指令将源文件包含进来,就像用被包含的文件的内容替换掉#include 这句话一样。

使用尖括号是说明被包含的文件是库文件,它的路径由编译器的配置决定。使用#include "filename"这样的用双引号包围的形式,是说该文件不是库文件,它的路径引号内路径决定。如#include "cv/cv.h"是说包含当前目录下cv文件夹中的cv.h文件。

当工程很大的时候,文件互相包含,这个时候会出现同一个文件被嵌套包含多次的情况,为了避免这种情况,我们在定义头文件时,常常像下面这样写:

#ifndef __SPEACIAL_H_

#define __SPEACIAL_H_

//在这里写文件内容

#endif

每个头文件在被预处理的时候,都会定义一个特殊的宏。如果相同的头文件再次出现的时候,由于在#ifndef这里将为假,所以忽略里面的内容。

5. 其他指令

5.1 #error

#error error message

当处理到上面这条指令的时候,会出现错误信息。

5.2 #line

#line number “filename”

这个指令后面可以跟两个常量,前面一个是数字是必须有的,后面的字符串可有可无。前面的number将会修改LINE,它指明下一行的行号是number,而后面的字符串会修改FILENAME,即文件名。

5.3 #progma

这个指令因编译器不同而不同,它用来支持因编译器而异的特性。

6. 结语

预编译预指令不常用到,但是还有有必要了解,今天把处理器就介绍到这里,如有任何错误还望各位指出。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、树立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值