#define、typedef用法


一、#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

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM