【define\typedef 用法】define用法集锦\typedef

来自百度文库

Definition[定义]

The #define Directive

You can use the #define directive to give a meaningful name to a

constant in your program. The two forms of the syntax are:

Syntax

#define identifier token-stringopt

#define identifier[( identifieropt, ... , identifieropt )] token-stringopt

Usage[用法]

1.简单的define定义

#define MAXTIME 1000

一个简单的MAXTIME 就定义好了,它代表1000,如果在程序里面写

if(i

编译器在处理这个代码之前会对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 然后去和cd 相乘,但是因为使用了define(它只是一个简单的替换),所以式子实际上变成了

c*a + b*d

另外举一个例子:

#define pin (int*);

pin a,b;

本意是ab 都是int 型指针,但是实际上变成int* a,b;

a int 型指针,而bint 型变量。

这是应该使用typedef 来代替define,这样ab 就都是int 型指针了。

所以我们在定义的时候,养成一个良好的习惯,建议所有的层次都要加括号。

3.1宏的单行定义(少见用法)

#define A(x) T_##x

#define Bx) #@x

#define Cx) #x

我们假设:x=1,则有:

A(1)------T_1

B(1)------'1'

C(1)------"1"

(这里参考了hustli 的文章)

3.2 define的多行定义

define 可以替代多行的代码,例如MFC 中的宏定义(非常的经典,虽然让人看了恶心)

#define MACRO(arg1, arg2) do { \

\

stmt1; \

stmt2; \

\

} while(0)

关键是要在每一个换行的时候加上一个"\"

4.条件编译

在大规模开发,特别是跨平台和系统的软件里,define最重要的功能是条件编译。

就是:

#ifdef WINDOWS

......

......

#endif

#ifdef LINUX

......

......

#endif

可以在编译的时候通过#define 设置编译环境

5.如何定义宏、取消宏

//定义宏

#define [MacroName] [MacroValue]

//取消宏

#undef [MacroName]

//普通宏

#define PI (3.1415926)

带参数的宏

#define max(a,b) ((a)>(b)? (a),(b))

关键是十分容易产生错误,包括机器和人理解上的差异等等。

6.条件编译

#ifdef XXX

(#else)

#endif

例如

#ifdef DV22_AUX_INPUT

#define AUX_MODE 3

#else

#define AUY_MODE 3

#endif

#ifndef XXX

(#else)

#endif

7.头文件(.h)可以被头文件或C文件包含;

重复包含(重复定义)

由于头文件包含可以嵌套,那么C 文件就有可能包含多次同一个头文件,就可能出现重复定义的问题的

通过条件编译开关来避免重复包含(重复定义)

例如

#ifndef __headerfileXXX__

#define __headerfileXXX__

//文件内容

#endif

Instances[实例]

1、防止一个头文件被重复包含

#ifndef COMDEF_H

#define COMDEF_H

//头文件内容

#endif

2、重新定义一些类型typedef 

防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。

typedef 声明,简称 typedef,为现有类型创建一个新的名字,或称为类型别名,在结构体定义,还有一些数组等地方都大量的用到。


typedef unsigned char boolean;

typedef unsigned long int uint32;

typedef unsigned short uint16;

typedef unsigned char uint8;

typedef signed long int int32;

typedef signed short int16;

typedef signed char int8;

//下面的不建议使用

typedef unsigned char byte;

typedef unsigned short word;

typedef unsigned long dword;

typedef unsigned char uint1;

typedef unsigned short uint2;

typedef unsigned long uint4;

typedef signed char int1;

typedef signed short int2;

typedef long int int4;

typedef signed long sint31;

typedef signed short sint15;

typedef signed char sint7;


  typedef 声明,简称 typedef,为现有类型创建一个新的名字,或称为类型别名,在结构体定义,还有一些数组等地方都大量的用到。

  typedef 还可以掩饰复合类型,如 指针 和数组。
  例如,你不用像下面这样重复定义有 81 个字符元素的数组:
  char line[81];
  char text[81];
  定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:
  typedef char Line[81];
  此时Line类型即代表了具有81个元素的 字符数组,使用方法如下:
  Line text, secondline;
  getline(text);
  同样,可以象下面这样隐藏指针语法:
  typedef char * pstr;
  int mystrcmp(pstr, pstr);
  这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘ const char *'类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():
  int mystrcmp(const pstr, const pstr);
  用GNU的gcc和g++ 编译器 ,是会出现警告的,按照顺序,‘const pstr'被解释为‘char* const‘(一个指向 char 的 指针常量 ),两者表达的并非同一意思(详见C++ Primer 第四版 P112)。为了得到正确的类型,应当如下声明:
  typedef const char* pstr;


 

代码简化

  上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让 编译器 来应付超越 预处理器 能力的 文本替换 。例如:
  typedef int (*PF) (const char *, const char *);
  这个声明引入了 PF 类型作为 函数指针 的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:
  PF Register(PF pf);
  Register() 的参数是一个 PF 类型的 回调函数 ,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:
  int (*Register (int (*pf)(const char *, const char *)))
  (const char *, const char *);
  很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:"OK,有人还会写这样的代码吗?",快速浏览一下揭示 signal()函数的头文件 ,一个有同样接口的函数。
  typedef 和存储类 关键字 (storage class specifier)
  这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并不是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的 变量 声明。下面将带到第二个陷阱:
  typedef register int FAST_COUNTER; // 错误
  编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。


3、得到指定地址上的一个字节或字

#define MEM_B( x ) ( *( (byte *) (x) ) )

#define MEM_W( x ) ( *( (word *) (x) ) )

4、求最大值和最小值

#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )

#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )

5、得到一个field在结构体(struct)中的偏移量

#define FPOS( type, field ) \

( (dword) &(( type *) 0)-> field )

6、得到一个结构体中field所占用的字节数

#define FSIZ( type, field ) sizeof( ((type *) 0)->field )

7、按照LSB格式把两个字节转化为一个Word

#define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )

8、按照LSB格式把一个Word转化为两个字节

#define FLOPW( ray, val ) \

(ray)[0] = ((val) / 256); \

(ray)[1] = ((val) & 0xFF)

9、得到一个变量的地址(word宽度)

#define B_PTR( var ) ( (byte *) (void *) &(var) )

#define W_PTR( var ) ( (word *) (void *) &(var) )

10、得到一个字的高位和低位字节

#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))

#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))

11、返回一个比X大的最接近的8的倍数

#define RND8( x ) ((((x) + 7) / 8 ) * 8 )

12、将一个字母转换为大写

#define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )

13、判断字符是不是10进值的数字

#define DECCHK( c ) ((c) >= '0' && (c) <= '9')

14、判断字符是不是16进值的数字

#define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\

((c) >= 'A' && (c) <= 'F') ||\

((c) >= 'a' && (c) <= 'f') )

15、防止溢出的一个方法

#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))

16、返回数组元素的个数

#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )

17、返回一个n位无符号数的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)

#define MOD_BY_POWER_OF_TWO( val, mod_by ) \

( (dword)(val) & (dword)((mod_by)-1) )

18、对于IO空间映射在存储空间的结构,输入输出处理

#define inp(port) (*((volatile byte *) (port)))

#define inpw(port) (*((volatile word *) (port)))

#define inpdw(port) (*((volatile dword *)(port)))

#define outp(port, val) (*((volatile byte *) (port)) = ((byte) (val)))

#define outpw(port, val) (*((volatile word *) (port)) = ((word) (val)))

#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))

19、使用一些宏跟踪调试

ANSI 标准说明了五个预定义的宏名。它们是:

__LINE__

__FILE__

__DATE__

__TIME__

__STDC__

C++中还定义了__cplusplus

如果编译器不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定

义的宏名。

__LINE__ __FILE__ 宏指示,#line 指令可以改变它的值,简单的讲,编译时,它们包含程序的当前行

数和文件名。

__DATE__ 宏指令含有形式为月//年的串,表示源文件被翻译到代码时的日期。

__TIME__ 宏指令包含程序编译的时间。时间用字符串表示,其形式为:分:秒

__STDC__ 宏指令的意义是编译时定义的。一般来讲,如果__STDC__已经定义,编译器将仅接受不包含任

何非标准扩展的标准C/C++代码。如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何

其它数,则实现是非标准的。

__cplusplus 与标准c++一致的编译器把它定义为一个包含至少6 为的数值。与标准c++不一致的编译器

将使用具有5 位或更少的数值。

可以定义宏,例如:

当定义了_DEBUG,输出数据信息和所在文件所在行

#ifdef _DEBUG

#define DEBUGMSG(msg,date) printf(msg);printf(%d%d%d,date,_LINE_,_FILE_)

#else

#define DEBUGMSG(msg,date)

#endif

20、宏定义防止错误使用小括号包含。

例如:

有问题的定义:#define DUMP_WRITE(addr,nr) {memcpy(bufp,addr,nr); bufp += nr;}

应该使用的定义: #difne DO(a,b) do{a+b;a++;}while(0)

例如:

if(addr)

DUMP_WRITE(addr,nr);

else

do_somethong_else();

宏展开以后变成这样:

if(addr)

{memcpy(bufp,addr,nr); bufp += nr;};

else

do_something_else();

gcc 在碰到else 前面的“;”时就认为if 语句已经结束,因而后面的else 不在if 语句中。而采用do{} while(0)

的定义,在任何情况下都没有问题。而改为#difne DO(a,b) do{a+b;a++;}while(0) 的定义则在任何情况

下都不会出错

21. define中的特殊标识符

#define Conn(x,y) x##y

#define ToChar(x) #@x

#define ToString(x) #x

int a=Conn(12,34);

char b=ToChar(a);

char c[]=ToString(a);

结果是a=1234,c='a',c='1234';

可以看出## 是简单的连接符,#@用来给参数加单引号,#用来给参数加双引号即转成字符串

posted on 2022-10-04 01:30  bdy  阅读(55)  评论(0编辑  收藏  举报

导航