一、#define
1.简单的define定义
1 #define MAXTIME 1000
这样的定义看起来类似于普通的常量定义CONST,但也有着不同,因为define的定义更像是简单的文本替换,而不是作为一个量来使用,这个问题在下面反映的尤为突出。
2.define的“函数定义”
define可以像函数那样接受一些参数,如下
1 #define max(x,y) (x)>(y)?(x):(y);
定义就将返回两个数中较大的那个。因为这个“函数”没有类型检查,就好像一个函数模板似的,当然,它绝对没有模板那么安全就是了。可以作为一个简单的模板来使用而已。
但是这样做的话存在隐患,例子如下:
1 #define Add(a,b) a+b;
如果遇到如:c * Add(a,b) * d的时候就会出现问题,代数式的本意是a+b然后去和c,d相乘,但是因为使用了define,所以式子实际上变成了c*a + b*d
3.宏的单行定义
1 #define A(x) T_##x 2 #define B(x) #@x 3 #define C(x) #x
我们假设:x=1,则有:
A(1)------〉T_1
B(1)------〉'1'
C(1)------〉"1"
4.define的多行定义
define可以替代多行的代码,例如MFC中的宏定义(非常的经典,虽然让人看了恶心)
1 #define MACRO(arg1, arg2) do { / 2 /* declarations */ / 3 stmt1; / 4 stmt2; / 5 /* ... */ / 6 } while(0) /* (no trailing ; ) */
关键是要在每一个换行的时候加上一个"/"
5.在大规模的开发过程中,特别是跨平台和系统的软件里,define 最重要的功能是条件编译。
就是:
1 #ifdef WINDOWS 2 ...... 3 #endif 4 #ifdef LINUX 5 ...... 6 #endif
可以在编译的时候通过#define设置编译环境
6.如何定义宏、取消宏
1 //定义宏 2 #define [MacroName] [MacroValue] 3 //取消宏 4 #undef [MacroName] 5 //普通宏 6 #define PI (3.1415926)
1 //带参数的宏 2 #define max(a,b) ((a)>(b)? (a),(b))
7.条件编译
1 #ifdef XXX…(#else) … 2 #endif 3 #ifdef DV22_AUX_INPUT 4 #define AUX_MODE 3 5 #else 6 #define AUY_MODE 3 7 #endif 8 #ifndef XXX … (#else) … 9 #endif
8.头文件(.h)可以被头文件或C文件包含
重复包含(重复定义)
由于头文件包含可以嵌套,那么C文件就有可能包含多次同一个头文件,就可能出现重复定义的问题的。
通过条件编译开关来避免重复包含(重复定义)
例如
1 #ifndef __headerfileXXX__ 2 #define __headerfileXXX__ 3 … 4 //文件内容 5 … 6 #endif
9. #define中的#、## && #@
前些一段时间在看WinCE的Code时发现在宏定义中有用到##,如下所示
1 #define GPEBLT_FUNCNAME(basename) (SCODE (GPE::*)(struct GPEBltParms *))&GPE::##basename
在#define中,标准只定义了#和##两种操作。#用来把参数转换成字符串,##则用来连接两个前后两个参数,把它们变成一个字符串。
1 #define ToString(a) #a 2 ToString( A b Cd ); //A b Cd 3 ToString( A/n b Cd ); //A 4 // b Cd 5 ToString( A/ n b Cd ); //A n b Cd
另外,在网上搜到还有一种用法:#@,把参数转换成字符
1 #define ToChar(a) #@a 2 ToChar(a); // a 3 ToChar(ab); // b 4 ToChar(abc); // c 5 ToChar(abcd); // d 6 //ToChar(abcde); // too many characters in constant 7 ToChar(1.); // .
二、typedef
用途一:定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。
比如:
1 char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针,和一个字符变量; 2 typedef char* PCHAR; 3 PCHAR pa, pb;
这种用法很有用,特别是char* pa, pb的定义,初学者往往认为是定义了两个字符型指针,其实不是,而用typedef char* PCHAR就不会出现这样的问题,减少了错误的发生。
用途二:用在旧的C代码中,帮助struct。
以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名对象名,如:
1 struct tagPOINT1 2 { 3 int x; 4 int y; 5 }; 6 struct tagPOINT1 p1;
用途三:用typedef来定义与平台无关的类型。
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
1 typedef long double REAL;
在不支持 long double 的平台二上,改为:
1 typedef double REAL;
在连 double 都不支持的平台三上,改为:
1 typedef float REAL;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健。
这个优点在我们写代码的过程中可以减少不少代码量哦!
用途四:为复杂的声明定义一个新的简单的别名。
方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:
1 void (*b[10]) (void (*)()); //原声明 2 //变量名为b,先替换右边部分括号里的,pFunParam为别名一: 3 typedef void (*pFunParam)(); 4 //再替换左边的变量b,pFunx为别名二: 5 typedef void (*pFunx)(pFunParam); 6 //原声明的最简化版: 7 pFunx b[10];
理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;
括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
1 int (*func)(int *p);
//首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;
//然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。
1 int (*func[5])(int *);
//func右边是一个[]运算符,说明func是具有5个元素的数组;
//func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。
//跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。
这种用法是比较复杂的,出现的频率也不少,往往在看到这样的用法却不能理解,相信以上的解释能有所帮助。
三、#define和typedef的区别
1) #define是预处理指令,在编译预处理时进行简单的替换,不作正确性检查,不关含义是否正确照样带入,只有在编译已被展开的源程序时才会发现可能的错误并报错。例如:
#define PI 3.1415926
程序中的:area=PI*r*r 会替换为3.1415926*r*r
如果你把#define语句中的数字9 写成字母g 预处理也照样带入。
2)typedef是在编译时处理的。它在自己的作用域内给一个已经存在的类型一个别名,但是You cannot use the typedef specifier inside a function definition。
3)typedef int * int_ptr; 和 #define int_ptr int *
作用都是用int_ptr代表 int * ,但是二者不同,正如前面所说 ,#define在预处理时进行简单的替换,而typedef不是简单替换 ,而是采用如同定义变量的方法那样来声明一种类型。也就是说;
1 //refer to (xzgyb(老达摩)) 2 #define int_ptr int * 3 int_ptr a, b; //相当于int * a, b; 只是简单的宏替换 4 5 typedef int* int_ptr; 6 int_ptr a, b; //a, b 都为指向int的指针,typedef为int* 引入了一个新的助记符
这也说明了为什么下面观点成立
1 //QunKangLi(维护成本与程序员的创造力的平方成正比) 2 typedef int * pint ; 3 #define PINT int *
那么:
1 const pint p ;//p不可更改,但p指向的内容可更改 2 const PINT p ;//p可更改,但是p指向的内容不可更改。
pint是一种指针类型 const pint p 就是把指针给锁住了 p不可更改
而const PINT p 是const int * p 锁的是指针p所指的对象。
4)还应经注意到#define 不是语句 不要在行末加分号,否则 会连分号一块置换。
补充:
#elif
等同于
#else
#if
转载自:
https://www.cnblogs.com/Jency/articles/C_Cplusplus_define.html
https://blog.csdn.net/dexingchen/article/details/3411680
https://www.cnblogs.com/csyisong/archive/2009/01/09/1372363.html