博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c的预处理器
阅读量:4213 次
发布时间:2019-05-26

本文共 3655 字,大约阅读时间需要 12 分钟。

当我们写完c的源代码后,不管是在Linux环境下输入命令行手动编译链接程序,还是在IDE集成环境下(列如vs)中点击生成,或者直接运行程序,对于源码处理的第一步,就是预处理器来对源代码处理。

故,第一步要干的事(也就是预处理器要干的事):删除注释,插入被#include指令包含的文件内容,定义和替换由#define指令定义的符号,根据条件编译指令来选择编译内容

1.预定义符号:由预处理器定义的符号(值类型,字符串常量,十进制数字常量)

      常见的:

__FILE__ :被编译的源文件的名称

__LINE__:文件当前的行号

__TIME__:被编译的时间

__DATE__:被编译的日期

__STDC__:看编译器是否遵循ANSI C标准,若遵循,则值为1,否则未定义

实验源码:(vs2010下)

#include 
int main(){
const char * fileName=__FILE__;
const char * date=__DATE__;
const char * time=__TIME__;
//const char * stdc=__STDC__;
printf("%s\n",fileName);
printf("%s\n",date);
printf("%s\n",time);#if (_MSC_VER==1600)
printf_s("编译器版本vc10\n");#endif
return 0;}
我使用的vs2010,编译器版本为vc10,__STDC__是未定的,可见此编译器不是完全遵循ANSI C标准。其余的预定义符号可正常打印。

2.#define讲解

  1.纯粹的文本替换:#define name  something

列如:常见的为静态数组确定一个最大容量:#define MAXSIZE 1000

    2.宏定义:#define name(参数列表)     something

                列如:define DOUBLE(x)  ((x)+(x))     //此括号最后都打上,避免因字符串替换导致运算的优先级改变等不容易发现的错误。

           

4.宏与函数的对照

        1.宏不局限于具体类型(宏与类型无关),而函数的值类型必须明确

        举例:#define malloc(n,type)  \

((type*)malloc((n)*sizeof(type)))

                 宏可以做到此种效果,不同的type分配出不同种类的内存空间,而函数却做不到。(题外话,最后的效果有点类似于java的反射机制)

        2.宏的速度一般都要快些,因为调用函数时需要阻塞等待,保留现场,调用需要时间,调用的函数运行又需要花时间,返回结果,回复现场又需要花时间,相比宏直接嵌入源码运行,时间上来说开销大些。

         3.每个使用宏的地方,都需要把宏定义代码插入到程序中,代码有重复性,没有函数的复用方便,导致程序源码的长度会大大增加。

5.根据条件来编译

   举例:

   #if

do something

   #elif

do something

   #endif

do something

    

6.常用的预处理指令

#if defined(A)  //等价于 #ifdef(A) ,若是否定,则为  #if  !defined(A)  或者#ifndef(A)

do something about A

#elif defined(B)

        do something about B

#else

#error   errorMsg

#endif

以下摘自《c和指针》的编程经验总结:

 1.避免使用#define指令定义可以用函数实现的很长序列代码

2.在那些对表达式求值的宏中,每个宏参数出现的地方都应该加上括号,并且在整个宏定义的两边也加上括号

3.避免使用#define宏创建一种新语言。

4.采用命名约定,使程序员很容易看出某个标识符是否为#define宏

5.只要适合就应该使用文件包含,不必担心它的额外开销

6.头文件只应该包含一组函数和(或)数据的申明

7.把不同集合的申明分离到不同的头文件中可以改善信息隐藏

8.嵌套的#include 文件使我们很难判断文件之间的依赖关系

编程题目:

 1.编写一个用于调试的宏,打印出任意的表达式。它被调用时应该接受两个参数,第一个是printf格式吗,第2个是需要打印的表达式

我编写的代码:

第一步:添加头文件print.h

第二步:#ifndef __PRINT_H

                #define PRINT(x,y)  \

                   printf(x,(y))

               #endif

标准答案:

#ifndef  __PRINT__H

#define DEBUG_PRINT( fmt, expr ) \
printf( "File %s, line %d: %s = " \
fmt "\n", \
__FILE__, __LINE__, \
#expr, expr )
#endif

2.

我的答案:

先定义一个var_print.h头文件,其中的代码

#ifndef VAR_PRINT	#if defined(OPTION_LONG)		#define  print_ledger(x) print_ledger_long(x)	#elif defined(OPTION_DETAILED)		#define  print_leger(x) print_ledger_detailed(x)	#else 		#define  print_ledger(x) print_ledger_default(x)	#endif#endif#include
void print_ledger_long(int x){ printf("print_ledger_long:%d\n",x);}void print_ledger_detailed(int x){ printf("print_ledger_detailed:%d\n",x);}void print_ledger_default(int x){ printf("print_ledger_default:%d\n",x);}
mian.cpp

敲打

#define OPTION_LONG#include "var_print.h"int main(){	int a=3;	print_ledger(a);	return 0;}
  程序成功运行

3.

步骤1:cpu_types.h

#ifndef CPU_TYPES_H/************************************************************************//* 系统类型定义                                                                     *//************************************************************************/#define CPU_VAX 0#define CPU_6000 1#define CPU_68020 2#define CPU_80386 3#define CPU_6809 4#define CPU_6502 5#define CPU_U3B2 6#define CPU_UNKNOW 7#endif  cpu_types.c#include"cpu_types.h"#include
int  cpu_type();int main(){     int a=cpu_type();    printf("%d\n",a);    return 0;}int  cpu_type(){#if defined(VAX)    return  CPU_VAX;#elif defined(M68000)    return CPU_6000;#elif defined(M68020)    return CPU_68020;#elif defined(I80386)    return CPU_80386;#elif defined(X6809)    return CPU_6809;#elif defined(X6502)    return CPU_6502;#elif defined(U3B2)    return CPU_U3B2;#else     return CPU_UNKNOW;#endif}
以上是书籍给出的标准答案,但是发现“如果超过一个符号被定义,那么其结果是未定义的这个条件”不符合标准。

   

你可能感兴趣的文章
HashMap无序与LinkedHashMap有序
查看>>
Spring异步任务@asynch
查看>>
Curl实现web serivce调试调用
查看>>
Tomcat NioEndPoint初探
查看>>
Tomcat NIO/APR对静态资源提速
查看>>
Servlet Filter与Spring interceptor的执行顺序
查看>>
使用HttpServletResponseWrapper获取渲染jsp以后的html
查看>>
Tomcat的ThreadLocalLeakPreventionListener工作原理
查看>>
利用springMVC的interceptor实现页面性能监控(Filter亦可)
查看>>
SpringMVC 拦截器实现分析
查看>>
初始化(Map,List)容器类的容量会有一定的性能提升
查看>>
StringBuffer与StringBuilder浅析
查看>>
BoneCP数据源记录SQl比hibernate的show sql好用
查看>>
对Cookie的一点认识
查看>>
说一说hibernate的Get和Load
查看>>
如何修改tomcat的server信息增加服务器的安全
查看>>
浅谈tomcat的ThreadLocalLeakPreventionListener实现原理
查看>>
说一下多线程中用到的join
查看>>
扩展hibernate Criteria的Order使其支持sql片段(oracle)
查看>>
spring+mybatis利用interceptor(plugin)实现数据库读写分离
查看>>