点击这里给我发消息 点击这里给我发消息

C语言预处理机制——学习笔记

添加时间:2013-12-7
    相关阅读: C语言
 

一、变量式宏定义(Oject-like Macro)

宏只是进行简单的字符串替换:
    #define N 20
    #define STR "hello, world\n"

 二、函数式宏定义(Function-like Macro)

宏可以类似函数一样使用:
    #define MAX(a, b) ((a)>(b)?(a):(b))

下面是他的几个特点特点:

参数没有类型,只是形式上的替换,不过类型检查,传参数时要格外小心

调用真正的函数和调用函数式宏定义的代码编译生成的指令不同

定义时要注意每个参数要加括号,最外层也要加括号

调用函数时先求实参表达式再传给形参,如果实参表达式有side effect,这些side effect只发生一次

有可能重复求值,小心定义这类宏

以下是几个应用举例:
#define sdram_selfrefresh_disable(saved_lpr0) \
    do
    { \
        at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0); \
        at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1); \
    }
    while (0)
这里的宏用do---while括起来就是为了让这个宏替换的代码总是当作一个整体看待。

三、内联函数(inline function)

内联函数是为了减少因函数调用产生的开销,会在调用函数时进行代码展开,而不是函数调用的方式,弊端是会导致编译文件变大:
    static inline int rwsem_is_locked(struct rw_semaphore *sem) { return sem->count != 0; }

四、#和##运算符和可变参数

#和##是预处理运算符,在函数式宏定义中,#运算符后面应该跟一个形参,之间用空格或Tab分割,用于创建字符串字面值:
#define STR(s) # s STR(hello world)
调用STR宏产生字符串字面值“hello world”。

在宏定义中##运算符把前后两个预处理Token连接成一个预处理Token,并且变量式宏定义中也可以用##运算符:

    #define CONCAT(a, b) a##b
    CONCAT(con, cat)

预处理之后是concat,比如定义一个宏展开为两个#符号:
    #define HASH_HASH # ## #
 注:中间空格不能少,因为####根据最长匹配原则是看作两个##,报错,因为预处理Token不能出现在开头或末尾。

函数式宏定义也有可变参数:
    #define showlist(...) printf(#__VA_ARGS__)
    #define report(test,...) ((test)?printf(#test):\
    printf(__VA_ARGS__))
    showlist(The first ,second, and third items.);
    report(x>y, "x is %d but y is %d",x,y);

预处理展开,宏定义中可变参数部分用__VA_ARGS__表示:
    printf("The first ,second, and third items.");
    ((x>y)?printf("x>y"):printf("x is %d but y is %d",x,y));

当__VA_ARGS__是空参数时,##运算符会把前面的逗号吃掉。

注:真正的宏应该学习Common Lisp的宏,体会宏的强大!

五、#undef预处理指示

如果用#define重复定义宏,规定这些宏定义必须一模一样,否则报错。

如果用#undef取消宏定义,取消一个没有定义的宏不会报错。

六、宏展开的步骤。

举例:
#define sh(x)
printf("n" #x "=%d, or %d\n",n##x,alt[x])
#define sub_z 26 sh(sub_z)

展开过程:
1、x的实参是sub_z
2、#x替换为sub_z
3、n##x替换为nsub_z
4、sh(x)展开为:printf("n sub_z =%d, or %d\n",nsub_z,alt[sub_z]);
5、其实sub_z最先替换为26
6、第4步的展开真实是:printf("n 26 =%d, or %d\n",n26,alt[26]);

七、条件预处理指示

实例1:
#ifndef HEADER_FILENAME
#define HEADER_FILENAME
/*body of header*/
#endif

实例2:
#if MACHINE == 68000 int x;
#elif MACHINE == 8086 long x;
#else #error UNKOWN TARGET MACHINE #endif

实例3:
#ifdef HEADER_FILENAME
#define HEADER_FILENAME
#undef HEADER_FILENUM
/*body of header*/
#endif

实例4:
    #if defined x
等于
    #ifdef x

    #if !defined x
等于
    #ifndef x

实例5:
#if 0
....
#endif

八、其它预处理特性

#pragma预处理指示供编译器实现一些扩展特性,C标准没有规定#pragma后面写什麽和起什麽作用,由编译器自己决定

宏__FILE__代表当前源文件的文件名

宏__LINE__代表展开成当前代码行的行号,是编译器内建的特殊宏定义

C标准规定C标准库的头文件是相互独立的

C99新特殊标识符__func__是一个变量名不是宏定义,因此不在预处理阶段求值,显示上一个调用的函数名称

咨询热线:020-85648757 85648755 85648616 0755-27912581 客服:020-85648756 0755-27912581 业务传真:020-32579052
广州市网景网络科技有限公司 Copyright◎2003-2008 Veelink.com. All Rights Reserved.
广州商务地址:广东省广州市黄埔大道中203号(海景园区)海景花园C栋501室
= 深圳商务地址:深圳市宝源路华丰宝源大厦606
研发中心:广东广州市天河软件园海景园区 粤ICP备05103322号 工商注册