本文共 3655 字,大约阅读时间需要 12 分钟。
当我们写完c的源代码后,不管是在Linux环境下输入命令行手动编译链接程序,还是在IDE集成环境下(列如vs)中点击生成,或者直接运行程序,对于源码处理的第一步,就是预处理器来对源代码处理。我使用的vs2010,编译器版本为vc10,__STDC__是未定的,可见此编译器不是完全遵循ANSI C标准。其余的预定义符号可正常打印。故,第一步要干的事(也就是预处理器要干的事):删除注释,插入被#include指令包含的文件内容,定义和替换由#define指令定义的符号,根据条件编译指令来选择编译内容。
1.预定义符号:由预处理器定义的符号(值类型,字符串常量,十进制数字常量)
常见的:
__FILE__ :被编译的源文件的名称
__LINE__:文件当前的行号
__TIME__:被编译的时间
__DATE__:被编译的日期
__STDC__:看编译器是否遵循ANSI C标准,若遵循,则值为1,否则未定义
实验源码:(vs2010下)
#includeint 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;}
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 ) #endif2.
我的答案:
先定义一个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#includemian.cppvoid 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);}
#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}