</pre>当我们写完c的源代码后,不管是在Linux环境下输入命令行手动编译链接程序,还是在IDE集成环境下(列如vs)中点击生成,或者直接运行程序,对于源码处理的第一步,就是预处理器来对源代码处理。</p><p>故,第一步要干的事(也就是预处理器要干的事)<span style="color:#cc0000;">:删除注释,插入被#include指令包含的文件内容,定义和替换由#define指令定义的符号,根据条件编译指令来选择编译内容</span>。</p><p>1.预定义符号:<span style="color:#990000;background-color: rgb(255, 255, 255);">由预处理器定义的符号(值类型,字符串常量,十进制数字常量)</span></p><p> 常见的:</p><p><span style="white-space:pre"> </span>__FILE__ :被编译的源文件的名称</p><p><span style="white-space:pre"> </span>__LINE__:文件当前的行号</p><p><span style="white-space:pre"> </span>__TIME__:被编译的时间</p><p><span style="white-space:pre"> </span>__DATE__:被编译的日期</p><p><span style="white-space:pre"> </span>__STDC__:看编译器是否遵循ANSI C标准,若遵循,则值为1,否则未定义</p><p>实验源码:(vs2010下)</p><p></p><pre name="code" class="cpp">#include <stdio.h>
int main()
{
<span style="white-space:pre"> </span>const char * fileName=__FILE__;
<span style="white-space:pre"> </span>const char * date=__DATE__;
<span style="white-space:pre"> </span>const char * time=__TIME__;
<span style="white-space:pre"> </span>//const char * stdc=__STDC__;
<span style="white-space:pre"> </span>printf("%s\n",fileName);
<span style="white-space:pre"> </span>printf("%s\n",date);
<span style="white-space:pre"> </span>printf("%s\n",time);
#if (_MSC_VER==1600)
<span style="white-space:pre"> </span>printf_s("编译器版本vc10\n");
#endif
<span style="white-space:pre"> </span>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<stdio.h>
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<stdio.h>
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
}
以上是书籍给出的标准答案,但是发现“如果超过一个符号被定义,那么其结果是未定义的这个条件”不符合标准。