预处理器:
编译程序之前,先由预处理器检查程序,根据程序中使用的预处理器指令,预处理器用符号缩略语所代表的内容替换程序中的缩略语。
预处理器可以根据包含其他文件,还可以选择让编译器处理哪些代码,预处理器不能理解C,它一般是接受一些文本并将其转换成其他文本
1.#define 的用法
定义:
宏 #define 预处理指令 可出现在源文件的任何地方 指令定义的作用于从定义出现的位置开始直到文件的结尾。
预处理器在程序中发现了宏的实例后,总会用实体代替该宏,从宏变成最终的替换文本的过程称为宏展开。
预处理器发现程序中的宏后,会用它的等价替换文本代替宏。如果该字符还包括宏,则继续替换这些宏。
const关键字得到了C的支持,这确实提供了一种创建常量的更灵活的方法,使用const可以创建全局变量和局部变量、数字常量、数组常量和结构常量,另一方面宏常量可以用来指定标准数组大小并作为const值得初始化值。
#define LIMIT 20
const int LIM =50;
static int data1[LIMIT]; //合法
static int data2[LIM]; //无效
const int LIM2 = 2 * LIMIT; //合法
const int LIM3 = 2* LIM; // 无效
(1)简单的用法
#define MAXTIME 1000
一个简单的MAXTIME就定义好了,它代表1000,如果在程序里面写
if(i<MAXTIME){.........}
编译器在处理这个代码之前会对MAXTIME进行处理替换为1000。
这样的定义看起来类似于普通的常量定义CONST,但也有着不同,因为define的定义更像是简单的文本替换,而不是作为一个量来使用。
(2)函数定义(时刻注意#define只是进行文本替换 而不进行任何编译处理)
define可以像函数那样接受一些参数,如下
#define max(x,y) (x)>(y)?(x):(y);
这个定义就将返回两个数中较大的那个,看到了吗?因为这个“函数”没有类型检查,就好像一个函数模板似的,当然,它绝对没有模板那么安全就是了。可以作为一个简单的模板来使用而已。
但是这样做的话存在隐患,例子如下:
#define Add(a,b) a+b;
在一般使用的时候是没有问题的,但是如果遇到如:c * Add(a,b) * d的时候就会出现问题,代数式的本意是a+b然后去和c,d相乘,但是因为使用了define(它只是一个简单的替换),所以式子实际上变成了
c*a + b*d
另外举一个例子:
#define pin (int*);
pin a,b;
本意是a和b都是int型指针,但是实际上变成int* a,b;
a是int型指针,而b是int型变量。
这是应该使用typedef来代替define,这样a和b就都是int型指针了。
所以我们在定义的时候,养成一个良好的习惯,建议所有的层次都要加括号。
(3)#define 多行定义
define可以替代多行的代码,例如MFC中的宏定义(非常的经典,虽然让人看了恶心)
#define MACRO(arg1, arg2) do { /
/* declarations */ /
stmt1; /
stmt2; /
/* ... */ /
} while(0) /* (no trailing ; ) */
关键是要在每一个换行的时候加上一个"/"
(4).在大规模的开发过程中,特别是跨平台和系统的软件里,define 最重要的功能是条件编译。
就是:
#ifdef WINDOWS
......
......
#endif
#ifdef LINUX
......
......
#endif
#ifdef XXX…(#else) …#endif
例如 #ifdef DV22_AUX_INPUT
#define AUX_MODE 3
#else
#define AUY_MODE 3
#endif
#ifndef XXX … (#else) … #endif
可以在编译的时候通过#define设置编译环境
(5).如何定义宏、取消宏
//定义宏
#define [MacroName] [MacroValue]
//取消宏
#undef [MacroName]
普通宏
#define PI (3.1415926)
带参数的宏
#define max(a,b) ((a)>(b)? (a),(b))
关键是十分容易产生错误,包括机器和人理解上的差异等等。
(6)头文件(.h)可以被头文件或C文件包含
重复包含(重复定义)
由于头文件包含可以嵌套,那么C文件就有可能包含多次同一个头文件,就可能出现重复定义的问题的。
通过条件编译开关来避免重复包含(重复定义)
例如
#ifndef __headerfileXXX__
#define __headerfileXXX__
…
文件内容
…
#endif
此功能也可以使用#pargam once 指令代替
(7)define中的三个特殊符号:#,##,#@
===========================================================
1. #define Conn(x,y) x##y
2. #define ToChar(x) #@x
3. #define ToString(x) #x
(1)x##y表示什么?表示x连接y,举例说:
1. int n = Conn(123,456); /* 结果就是n=123456;*/
2. char* str = Conn("asdf", "adf"); /*结果就是 str = "asdfadf";*/
(2)再来看#@x ,其实就是给x加上单引号,结果返回是一个const char。举例说:
char a = ToChar(1);结果就是a=‘1‘;
做个越界试验char a = ToChar(123);结果就错了;
但是如果你的参数超过四个字符,编译器就给给你报错了!
error C2015: too many characters in constant :P
(3)最后看看#x,估计你也明白了,他是给x加双引号
char* str = ToString(123132);就成了str="123132";
宏与函数的选择:
宏与函数间的选择实际上是时间与空间的权衡,宏产生内联代码 也就是说在程序中产生语句,如果使用了宏20次 则会把20行代码插入程序中,如果使用函数20次,那么程序中只有一份函数语句的拷贝,因此节省了空间,另一方面,程序的控制必须转移到函数中并随后返回调用程序,因此这比内联代码花费的时间多。
宏的一个有点事不检查其中的变量类型
C 标准的预定义宏
_ _DATE_ _ 进行预处理的日期 (“Mmm dd yyy”形式的字符串文字)
_ _FILE_ _ 代表当前源代码文件名的字符串文字
_ _LINE_ _ 代表当前源代码文件中的行号的整数常量
_ _STDC_ _ 设置为1 时 表示该实现遵循C标准
_ _STDC_HOSTED_ _ 在本机环境设置为1,否则设置为0
_ _STDC_VERSION_ _ 在C99是设置为199901L
_ _TIME_ _ 源文件编译时间 格式为"hh:mm:ss"
2.#include
(1)两种使用形式
#include<stdio.h>
#include "myStuuff.h"
在Unix系统中 尖括号告诉预处理器在一个或多个标准系统目录中寻找文件, 双引号告诉预处理器现在当前目录中寻找文件,然后再标准位置选在文件
(2)使用头文件
头文件中最常见的形式包括:
a.明显常量
b.宏函数
c.函数声明
d.结构模板定义
e.类型定义
原文:http://my.oschina.net/u/818523/blog/487172