C语言宏定义技巧和指针.docx

上传人:b****5 文档编号:5150718 上传时间:2022-12-13 格式:DOCX 页数:16 大小:20.88KB
下载 相关 举报
C语言宏定义技巧和指针.docx_第1页
第1页 / 共16页
C语言宏定义技巧和指针.docx_第2页
第2页 / 共16页
C语言宏定义技巧和指针.docx_第3页
第3页 / 共16页
C语言宏定义技巧和指针.docx_第4页
第4页 / 共16页
C语言宏定义技巧和指针.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

C语言宏定义技巧和指针.docx

《C语言宏定义技巧和指针.docx》由会员分享,可在线阅读,更多相关《C语言宏定义技巧和指针.docx(16页珍藏版)》请在冰豆网上搜索。

C语言宏定义技巧和指针.docx

C语言宏定义技巧和指针

 

C语言宏定义技巧

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

2,

3,#ifndefCOMDEF_H

4,

5,#defineCOMDEF_H

6,

7,//头文件内容

8,

9,#endif

10,

11,2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。

12,

13,typedefunsignedcharboolean;/*Booleanvaluetype.*/

14,

15,

16,

17,typedefunsignedlongintuint32;/*Unsigned32bitvalue*/

18,

19,typedefunsignedshortuint16;/*Unsigned16bitvalue*/

20,

21,typedefunsignedcharuint8;/*Unsigned8bitvalue*/

22,

23,

24,

25,typedefsignedlongintint32;/*Signed32bitvalue*/

26,

27,typedefsignedshortint16;/*Signed16bitvalue*/

28,

29,typedefsignedcharint8;/*Signed8bitvalue*/

30,

31,

32,

33,

34,

35,//下面的不建议使用

36,

37,typedefunsignedcharbyte;/*Unsigned8bitvaluetype.*/

38,

39,typedefunsignedshortword;/*Unsinged16bitvaluetype.*/

40,

41,typedefunsignedlongdword;/*Unsigned32bitvaluetype.*/

42,

43,

44,

45,typedefunsignedcharuint1;/*Unsigned8bitvaluetype.*/

46,

47,typedefunsignedshortuint2;/*Unsigned16bitvaluetype.*/

48,

49,typedefunsignedlonguint4;/*Unsigned32bitvaluetype.*/

50,

51,

52,

53,typedefsignedcharint1;/*Signed8bitvaluetype.*/

54,

55,typedefsignedshortint2;/*Signed16bitvaluetype.*/

56,

57,typedeflongintint4;/*Signed32bitvaluetype.*/

58,

59,

60,

61,typedefsignedlongsint31;/*Signed32bitvalue*/

62,

63,typedefsignedshortsint15;/*Signed16bitvalue*/

64,

65,typedefsignedcharsint7;/*Signed8bitvalue*/

66,

67,

68,

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

70,

71,#defineMEM_B(x)(*((byte*)(x)))

72,

73,#defineMEM_W(x)(*((word*)(x)))

74,

75,4,求最大值和最小值

76,

77,#defineMAX(x,y)(((x)>(y))?

(x):

(y))

78,

79,#defineMIN(x,y)(((x)<(y))?

(x):

(y))

80,

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

82,

83,#defineFPOS(type,field)\

84,

85,/*lint-e545*/((dword)&((type*)0)->field)/*lint+e545*/

86,

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

88,

89,#defineFSIZ(type,field)sizeof(((type*)0)->field)

90,

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

92,

93,#defineFLIPW(ray)((((word)(ray)[0])*256)+(ray)[1])

94,

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

96,

97,#defineFLOPW(ray,val)\

98,

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

100,

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

102,

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

104,

105,#defineB_PTR(var)((byte*)(void*)&(var))

106,

107,#defineW_PTR(var)((word*)(void*)&(var))

108,

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

110,

111,#defineWORD_LO(xxx)((byte)((word)(xxx)&255))

112,

113,#defineWORD_HI(xxx)((byte)((word)(xxx)>>8))

114,

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

116,

117,#defineRND8(x)((((x)+7)/8)*8)

118,

119,12,将一个字母转换为大写

120,

121,#defineUPCASE(c)(((c)>='a'&&(c)<='z')?

((c)-0x20):

(c))

122,

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

124,

125,#defineDECCHK(c)((c)>='0'&&(c)<='9')

126,

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

128,

129,#defineHEXCHK(c)(((c)>='0'&&(c)<='9')||\

130,

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

132,

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

134,

135,15,防止溢出的一个方法

136,

137,#defineINC_SAT(val)(val=((val)+1>(val))?

(val)+1:

(val))

138,

139,16,返回数组元素的个数

140,

141,#defineARR_SIZE(a)(sizeof((a))/sizeof((a[0])))

142,

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

144,

145,#defineMOD_BY_POWER_OF_TWO(val,mod_by)\

146,

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

148,

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

150,

151,#defineinp(port)(*((volatilebyte*)(port)))

152,

153,#defineinpw(port)(*((volatileword*)(port)))

154,

155,#defineinpdw(port)(*((volatiledword*)(port)))

156,

157,

158,

159,#defineoutp(port,val)(*((volatilebyte*)(port))=((byte)(val)))

160,

161,#defineoutpw(port,val)(*((volatileword*)(port))=((word)(val)))

162,

163,#defineoutpdw(port,val)(*((volatiledword*)(port))=((dword)(val)))

164,

165,19,使用一些宏跟踪调试

166,

167,ANSI标准说明了五个预定义的宏名。

它们是:

168,

169,_LINE_

170,

171,_FILE_

172,

173,_DATE_

174,

175,_TIME_

176,

177,_STDC_

178,

179,如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。

记住编译程序

180,

181,也许还提供其它预定义的宏名。

182,

183,_LINE_及_FILE_宏指令在有关#line的部分中已讨论,这里讨论其余的宏名。

184,

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

186,

187,源代码翻译到目标代码的时间作为串包含在_TIME_中。

串形式为时:

分:

秒。

188,

189,如果实现是标准的,则宏_STDC_含有十进制常量1。

如果它含有任何其它数,则实现是

190,

191,非标准的。

192,

193,可以定义宏,例如:

194,

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

196,

197,#ifdef_DEBUG

198,

199,#defineDEBUGMSG(msg,date)printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)

200,

201,#else

202,

203,#defineDEBUGMSG(msg,date)

204,

205,#endif

206,

207,

208,

209,20,宏定义防止使用是错误

210,

211,用小括号包含。

212,

213,例如:

#defineADD(a,b)(a+b)

214,

215,用do{}while(0)语句包含多语句防止错误

216,

217,例如:

#difneDO(a,b)a+b;\

218,

219,a++;

220,

221,应用时:

if(….)

222,

223,DO(a,b);//产生错误

224,

225,else

226,

227,

228,

229,解决方法:

#difneDO(a,b)do{a+b;\

230,

231,a++;}while(0)

232,

233,

234,宏中"#"和"##"的用法

235,一、一般用法

236,我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.

237,用法:

238,#include

239,#include

240,usingnamespacestd;

241,

242,#defineSTR(s)#s

243,#defineCONS(a,b)int(a##e##b)

244,

245,intmain()

246,{

247,printf(STR(vck));//输出字符串"vck"

248,printf("%d\n",CONS(2,3));//2e3输出:

2000

249,return0;

250,}

251,

252,二、当宏参数是另一个宏的时候

253,需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.

254,

255,1,非'#'和'##'的情况

256,#defineTOW

(2)

257,#defineMUL(a,b)(a*b)

258,

259,printf("%d*%d=%d\n",TOW,TOW,MUL(TOW,TOW));

260,这行的宏会被展开为:

261,printf("%d*%d=%d\n",

(2),

(2),(

(2)*

(2)));

262,MUL里的参数TOW会被展开为

(2).

263,

264,2,当有'#'或'##'的时候

265,#defineA

(2)

266,#defineSTR(s)#s

267,#defineCONS(a,b)int(a##e##b)

268,

269,printf("intmax:

%s\n",STR(INT_MAX));//INT_MAX#include

270,这行会被展开为:

271,printf("intmax:

%s\n","INT_MAX");

272,

273,printf("%s\n",CONS(A,A));//compileerror

274,这一行则是:

275,printf("%s\n",int(AeA));

276,

277,INT_MAX和A都不会再被展开,然而解决这个问题的方法很简单.加多一层中间转换宏.

278,加这层宏的用意是把所有宏的参数在这层里全部展开,那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.

279,

280,#defineA

(2)

281,#define_STR(s)#s

282,#defineSTR(s)_STR(s)//转换宏

283,#define_CONS(a,b)int(a##e##b)

284,#defineCONS(a,b)_CONS(a,b)//转换宏

285,

286,printf("intmax:

%s\n",STR(INT_MAX));//INT_MAX,int型的最大值,为一个变量#include

287,输出为:

intmax:

0x7fffffff

288,STR(INT_MAX)-->_STR(0x7fffffff)然后再转换成字符串;

289,

290,printf("%d\n",CONS(A,A));

291,输出为:

200

292,CONS(A,A)-->_CONS(

(2),

(2))-->int(

(2)e

(2))

293,

294,三、'#'和'##'的一些应用特例

295,1、合并匿名变量名

296,#define___ANONYMOUS1(type,var,line)typevar##line

297,#define__ANONYMOUS0(type,line)___ANONYMOUS1(type,_anonymous,line)

298,#defineANONYMOUS(type)__ANONYMOUS0(type,__LINE__)

299,例:

ANONYMOUS(staticint);即:

staticint_anonymous70;70表示该行行号;

300,第一层:

ANONYMOUS(staticint);-->__ANONYMOUS0(staticint,__LINE__);

301,第二层:

-->___ANONYMOUS1(staticint,_anonymous,70);

302,第三层:

-->staticint_anonymous70;

303,即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;

304,

305,2、填充结构

306,#defineFILL(a){a,#a}

307,

308,enumIDD{OPEN,CLOSE};

309,typedefstructMSG{

310,IDDid;

311,constchar*msg;

312,}MSG;

313,

314,MSG_msg[]={FILL(OPEN),FILL(CLOSE)};

315,相当于:

316,MSG_msg[]={{OPEN,"OPEN"},

317,{CLOSE,"CLOSE"}};

318,

319,3、记录文件名

320,#define_GET_FILE_NAME(f)#f

321,#defineGET_FILE_NAME(f)_GET_FILE_NAME(f)

322,staticcharFILE_NAME[]=GET_FILE_NAME(__FILE__);

323,

324,4、得到一个数值类型所对应的字符串缓冲大小

325,#define_TYPE_BUF_SIZE(type)sizeof#type

326,#defineTYPE_BUF_SIZE(type)_TYPE_BUF_SIZE(type)

327,charbuf[TYPE_BUF_SIZE(INT_MAX)];

328,-->charbuf[_TYPE_BUF_SIZE(0x7fffffff)];

329,-->charbuf[sizeof"0x7fffffff"];

330,这里相当于:

331,charbuf[11];

 

右左法则----复杂指针解析

因为C语言所有复杂的指针声明,都是由各种声明嵌套构成的。

如何解读复杂指针声明呢?

右左法则是一个既著名又常用的方法。

不过,右左法则其实并不是C标准里面的内容,它是从C标准的声明规定中归纳出来的方法。

C标准的声明规则,是用来解决如何创建声明的,而右左法则是用来

解决如何辩识一个声明的,两者可以说是相反的。

右左法则的英文原文是这样说的:

Theright-leftrule:

Startreadingthedeclarationfromtheinnermostparentheses,goright,andthengoleft.Whenyou

encounterparentheses,thedirectionshouldbereversed.Onceeverythingintheparentheseshasbeen

parsed,jumpoutofit.Continuetillthewholedeclarationhasbeenparsed.

 

这段英文的翻译如下:

右左法则:

首先从最里面的圆括号看起,然后往右看,再往左看。

每当遇到圆括号时,就应该掉转阅读方向。

一旦解析完圆括号里面所有的东西,就跳出圆括号。

重复这个过程直到整个声明解析完毕。

笔者要对这个法则进行一个小小的修正,应该是从未定义的标识符开始阅读,而不是从括号读起,之所以是未定义的标识符,是因为一个声明里面可能有多个标识符,但未定义的标识符只会有一个。

现在通过一些例子来讨论右左法则的应用,先从最简单的开始,逐步加深:

int(*func)(int*p);

首先找到那个未定义的标识符,就是func,它的外面有一对圆括号,而且左边是一个*号,这说明func是一个指针,然后跳出这个圆括号,先看右边,也是一个圆括号,这说明(*func)是一个函数,而func是一个指向这类函数的指针,就是一个函数指针,这类函数具有int*类型的形参,返

回值类型是int。

int(*func)(int*p,int(*f)(int*));

func被一对括号包含,且左边有一个*号,说明func是一个指针,跳出括号,右边也有个括号,那么func是一个指向函数的指针,这类函数具有int*和int(*)(int*)这样的形参,返回值为int类型。

再来看一看func的形参int(*f)(int*),类似前面的解释,f也是一个函数指针,指向的函

数具有int*类型的形参,返回值为int。

int(*func[5])(int*p);

func右边是一个[]运算符,说明func是一个具有5个元素的数组,func的左边有一个*,说明func的元素是指针,要注意这里的*不是修饰func的,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合,因此*修饰的是func[5]。

跳出这个括号,看右边,也是一对圆括号,说

明func数组的元素是函数类型的指针,它所指向的函数具有int*类型的形参,返回值类型为int。

 

int(*(*func)[5])(int*p);

func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。

总结一下,就是

func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,返回值为int类型的函数。

int(*(*func)(int*p))[5];

func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。

要注意有些复杂指针声明是非法的,例如:

intfunc(void)[5];

func是一个返回值为具有5个int元素的数组的函数。

但C语言的函数返回值不能为数组,这是因为如果允许函数返回值为数组,那么接收这个数组的内容的东西,也必须是一个数组,但C语言的数组名是一个右值,它不能作为左值来接收另一个数组,因此函数返回值不能为数组。

intfunc[5](void);

func是一个具有5个元素的数组,这个数组的元素都是函数。

这也是非法的,因为数组的元素除了类型必须一样外,每个元素所占用的内存空间也必须相同,显然函数是无法达到这个要求的,即使函数的类型一样,但函数所占用的空间通常是不相同的。

作为练习,下面列几个复杂指针声明给读者自己来解析。

int(*(*func)[5][6])[7][8];

int(*(*(*func)(int*))[5])(int*);

int(

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 药学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1