宏的使用

C/C++宏的使用

 

基础部分

 

1. __VA_ARGS__: 用来替换任意参数部分, 相当于c语言中的va_list;

:

#define OUT(...)   printf(__VA_ARGS__)

2. 宏从内向外展开(例外情况见4)

原因是, 当一个红的参数也是宏的时候, 它会尝试先展开它的参数;

:

max(max(1, 3), 2) => max(3, 2) =>3

3. ###. 前者用来将一个文本转化为语言内字符串, 后者用来连接两个文本

:

#define TO_STRING(s)   #s

#define CAT(a,b)          a#b

TO_STRING(a)               => "a"

int CAT(a,b);                => int ab;

4. 当一个宏对它的某个参数进行#或者##, 这个参数使用点并不被替换为展开后的文本(这里的展开意思是, 将参数中的其他宏展开). 这句话隐含的意思是, 如果这个参数会多次使用, 只要不是#或者##, 都会被替换为展开后文本;

:

#defineTEST(a)           a; a +1; a##a; #a

#define MAX(a, b)       ((a) > (b) ? (a) : (b))

TEST(MAX(1,1))           => ((1) >(1) ? (1) : (1)); ((1) > (1) ? (1) : (1)) + 1; MAX(1, 1)MAX(1, 1);"MAX(1, 1)"

5. ###做特殊处理; 由于一般宏在展开时, 它的参数都已经被展开, 而对参数应用了###的宏, 这里的参数中的宏却不展开, 所以有必要对它们进行特殊处理, 以符合一般的需要

工具:

#define TO_STRING(s)      TO_STRING_D(s)

#define TO_STRING_D(s)  #s

#define CONN(a,b)           CONN_D(a, b)

#define CONN_D(a,b)       a##b

原理: 利用一般宏会展开它参数的特性, TO_STRINGCONN先将参数完全展开, 然后再交给实际调用###的宏其中, TO_STRING非常有用, 除了服务一般编程外, 在使用宏的预处理期编程尤为重要, 是预处理期调试的重要打印手段

6. 带参数宏在无参数时被视为文本, 不展开

:

#define MAX(a,b)                   ((a) > (b) ? (a) : (b))

TO_STRING(MAX(1,2))            => ((1) > (2) ? (1) : (2))

TO_STRING(MAX(MAX,MAX))    => ((MAX) > (MAX) ? (MAX) : (MAX))

7. 宏不支持递归

运行时函数递归的方法是, 改变参数然后进行下步调用, 通过对不同参数的测试来判断是否到达终点应该停止递归.

元编程递归是通过特化、偏特化或者重载, 通过令终点历程不进行进一步调用的方式, 来终止递归.

表面上看, 由于宏并不支持结构控制(相对于函数递归的if和元编程的If模板), 所以宏不支持递归, 一旦预处理器发现一个宏在展开的过程中第二次出现, 则停止这个宏的展开.

关于这一点的更详细说明见后文.

 

预处理期编程

 

1.

 

宏没有实质上的变量, 宏的数据来源是用户编码; 而各种意义的常数中, 可用的只有宏和整形文字常量(cout <<TO_STRING(...); 来打印预处理期展开情况):

:

#defineINT_5               5

const intINT_7              = 7;

TO_STRING(INT_5)        => 5

TO_STRING(6)               => 6

TO_STRING(INT_7)        => INT_7

可见, 由于预处理期早于编译器, c++常量无效; 唯一的数据源只剩下宏和硬编码整数. 由于一个提供一定功能的宏只有一个文字数据源, 而又没有宏变量的说法, 所以, 要想在宏内部实现变量的效果, 就只有采用特殊手段:

#define INC_0   1

#define INC_1   2

#define INC_2   3

#define INC_3   4

#define DEC_0   0

#define DEC_1   0

#define DEC_2   1

#define DEC_3   2

TO_STRING(INC(INC(INC(0)))) => 3

TO_STRING(DEC(DEC(5)))     => 3

这样, 就实现了将文字数据源转化为范围受限整数的效果; 由于受宏嵌套深度和使用范围影响, 这些定义不需要太多, 一般不超过256左右就合适(宏自动生成的代码,比如模板类型参数, 32个以内就够用了)

2. BOOL

由于BOOL值是非常有用的类型, 是结构控制中条件转移必须的元素, 所以这里需要一种手段将上述有限范围的整数转化为01

#defineBOOL_0              0

#defineBOOL_1              1

#defineBOOL_2              1

#defineBOOL_3              1

#defineTO_BOOL(i)         CONN(BOOL_, i)

TO_STRING(TO_BOOL(3)) => 1

TO_STRING(TO_BOOL(0)) => 0

3. if-else

利用上面的BOOL

 

#define IF_THEN_ELSE(condition, then,else)     CONN(CONDITION_,TO_BOOL(condition))(then, else)

#define CONDITION_1(a,b)                              a

#define CONDITION_0(a,b)                              b

4. for

由于宏不允许递归, 所以试图使for循环调用自身是不可能的, 这里将采用其他做法

调用FOR采用下面这种格式

 

FOR(数据集, 条件宏, 增量宏)

(1) 数据集是这样的格式(data1, data2,data3, ...), 含括号

(2) 条件宏, 接受两个参数-当前迭代轮数和数据集, 当条件宏判断为假, 表示不再迭代

(3) 增量宏, 增量宏同样接受迭代轮数和数据集, 它改变数据集后作为下次迭代的数据源

 

最终FOR返回的是一个数据集, 元素数和源数据相同, 用户提取其中相应的元素

#define GC_3(a, b, c)  

#define FOR1(data, toConntinue, inc)IF_THEN_ELSE(toConntinue(2, data), FOR2, data GC_3)(inc(2, data), toConntinue,inc)

#define FOR2(data, toConntinue, inc)IF_THEN_ELSE(toConntinue(3, data), FOR3, data GC_3)(inc(3, data), toConntinue,inc)

#define FOR3(data, toConntinue, inc)IF_THEN_ELSE(toConntinue(4, data), FOR4, data GC_3)(inc(4, data), toConntinue,inc)

其中传入的轮数是当前轮 + 1, 原因最后阐述. 这里暂时无法演示FOR的使用, 看下节

5. 加法

#defineDIG(n)                                        n

#define TUPLE_SEL(sz, idx,tpl)              DIG(CONN(CONN(TUPLE_SEL_,sz), CONN(_,idx)) tpl)

#define TUPLE_SEL_2_0(p0,p1)             p0

TO_STRING(TUPLE_SEL(2, 0, (5,10)))      => 5

TUPLE_SEL的作用是从数据集tpl, 选出第idx个元素, sz表示数据集的尺寸

 

下面使用FOR来实现加法

#define ADD(x,y)                         TUPLE_SEL(2,0, FOR1((x, y), ADD_NO_END_I,ADD_INC_I))

#define ADD_NO_END_I(i,xy)      TUPLE_SEL(2, 1, xy)

#define ADD_INC_I(i,xy)             ADD_INC(TUPLE_SEL(2, 0, xy), TUPLE_SEL(2,1, xy))

#define ADD_INC(x,y)                (INC(x), DEC(y))

也就是说, 对于数据集(x, y), 每次将x1, 并将y1, 然后将结果传给下轮迭代, y减到0, 从结果数据集(x + y, 0)中取出第一个元素就是加法的结果

 

6. 其他运算

减法和加法类似, 而乘法就是多次加法, 除法是多次减法

#define SUB(x, y)                      TUPLE_SEL(2, 0, FOR1((x, y), SUB_NO_END_I,SUB_INC_I))

#define SUB_N(x, y,n)              TUPLE_SEL(2, 0, CONN(FOR,n)((x, y),SUB_NO_END_I, SUB_INC_I))

#defineSUB_NO_END_I             ADD_NO_END_I

#define SUB_INC_I(i,xy)           SUB_INC(TUPLE_SEL(2, 0, xy), TUPLE_SEL(2,1, xy))

#define SUB_INC(x,y)               (DEC(x), DEC(y))

#define MUL(x,y)                       TUPLE_SEL(3, 0, FOR1((0, x, y), MUL_NO_END_I, MUL_INC_I))

#define MUL_NO_END_I(i,sxy)   TUPLE_SEL(3, 2, sxy)

#define MUL_INC_I(i,sxy)           MUL_INC(i,TUPLE_SEL(3, 0, sxy), TUPLE_SEL(3, 1, sxy), TUPLE_SEL(3, 2, sxy))

#define MUL_INC(i, s, x,y)          (ADD_N(s, x, i), x,DEC(y))

#define DIV(x,y)                        TUPLE_SEL(3, 0, FOR1((0, x, y), DIV_NO_END_I, DIV_INC_I))

#define DIV_NO_END_I(i,cxy)    TUPLE_SEL(3, 1, cxy)

#define DIV_INC_I(i,cxy)           DIV_INC(i, TUPLE_SEL(3, 0, cxy), TUPLE_SEL(3, 1, cxy), TUPLE_SEL(3, 2, cxy))

#define DIV_INC(i, c, x,y)           (INC(c),SUB_N(x, y, i), y)

这里还有一些其他运算:

#define MOD(x,y)                      MOD_(SUB(MUL(y, DIV(x, y)), x), y)

#define MOD_(x,y)                    IF_THEN_ELSE(x, SUB(y, x), 0)

#define EQUAL(x,y)                    BITNOT(NOT_EQUAL(x, y))

#define NOT_EQUAL(x,y)            IF_THEN_ELSE(SUB(x,y), 1, SUB(y, x))

 

#define LESS(x,y)                      SUB(y, x)

#define GREATER(x,y)                SUB(x,y)

#define AND(x,y)                       BITAND(TO_BOOL(x), TO_BOOL(y))

#define OR(x,y)                         BITOR(TO_BOOL(x), TO_BOOL(y))

#defineNOT(x)                           BITNOT(TO_BOOL(x))

#define BITAND(x,y)                 CONN(CONN(BITAND_,x), CONN(_,y))

#define BITOR(x,y)                   CONN(CONN(BITOR_,x), CONN(_,y))

#define BITXOR(x,y)                 CONN(CONN(BITXOR_,x), CONN(_,y))

#defineBITNOT(x)                     CONN(BITNOT_,x)

7. 关于宏递归和嵌套循环

前面强调过, 宏不支持递归, 因为预处理器一发现出现过的宏就会停止展开. 那上面的乘法要怎么做? 乘法是用加法来实现的, 不可避免的就需要嵌套FOR; 一种做法是再准备另一份FOR2_0, FOR2_1...,用于嵌套中第二曾, 结果就是FOR(; ; )FOR2(; ;){}. 由于完全是两组宏, 自然也就避免了重复出现导致预处理器拒绝展开的问题.再讨论乘法的实现之前先看点其他的:

#define A(a)    B(a)

#define B(a)    A(a)C(a)

#define C(a)    a

TO_STRING(A(1))的结果? A(1)1. 因为预处理器在展开B时发现A已经出现过, 所以拒绝再展开

TO_STRING(CONN(0, CONN(CONN(1, 2),CONN(3, 4))))结果? 结果是01234, 完全展开了

这就需要追究所谓"出现过的宏", 精确定义; 其实上面两个例子已经在一定程度上演示了怎样的宏是出现过的宏. A(1)的例子中, 发生了一次根展开, 根由A变成了B: A(1) -> B(1). 假如B的展开中, 又出现A, 那么就会有递归的嫌疑, 所以B中的A调用将不会展开. CONN, 完全没有根展开, 所以整个表达式肯定是用户编码, 没有递归可能, 因此预处理器进行了常规展开. 所以, "出现过的宏", 精确定义, 不是指编码重复, 而是指对于宏A, A所在的宏调用嵌套层中, 其父层次及本层次已经展开的宏中, 并没有包含A. 比如(为了比较和演示, 并不是预处理器实际展开顺序):

#defineA(a)                                     A1(B(C(a), D(a)))

#defineC(a)                                    C1(a)

#defineD(a)                                    C1(a)

#defineC1(a)                                  C(a)D(a)

A(#)                                                 => A展开成A1(...), 发生根替换, A不再可用

A1( B( C (#), D (#) ))                       => C展开成C1, 对于本分支C1的展开, AC不可用

A1( B( C1(#), D (#) ))                      => D展开成C1, 对于本分支C1的展开, AD不可用

A1( B( C1(#), C1(#) ))                     => 把两个C1都展开, 左分支A, C, C1不可用, 右分支A, D, C1不可用

A1( B( C(#)D(#), C(#)D(#) ))            => 左分支, 由于AC不可用, 所以只有D展开, 进而A, C, C1, D都不可用

A1( B( C(#)C1(#), C(#)D(#) ))          => 右分支的C展开, 进而A, D, C1, C都不可用

A1( B( C(#)C1(#), C1(#)D(#) ))        => 左右分支都不可再展开, A1, B又未定义, 所以宏展开终点

TO_STRING(A(#))                              => A1(B(C(#)C1(#), C1(#)D(#)))

讲了这么多, 其实就是为了精确定义哪些宏进入了黑名单(这个黑链表应该是随着预处理器处理深度, 不停调整长度的).

 

再来说乘法的两层迭代实现方法:

对于MUL(x, y), 定义data => (s, x, y); 所以:

 

while (y != 0)

{

    s += x;

    --y;

}

 

由于宏运算只有++--, 将上面换个写法:

 

for (s = 0; y != 0; --y)

{

    for (int i = 0; i< x; ++i)

    {

       ++s;

    }

}

 

最终: data => (xy,x, 0)

前面提到的矛盾就在于, 外层有个FOR, 而内层也有个FOR, 必须要使用某种手段, 令两层FOR并不冲突. 再来看FOR的迭代过程:

 

for1( data1, test, inc)           

            

=>  // 展开for1, for1已经不可用

if (test(..., data1))

{

    for2 (inc(...,data), test, inc);

}

else

{

    end;

}

 

如果把上面的表达式看作乘法的最外层循环的话, 那么内层的循环就在inc(..., data); 这个inc(..., data), 就是将一个x累加到s, 也就是一个加法, 也就包含有另一个for循环. 上面已经指出了, for(n)展开的时候, for1, for2,...for(n)都不可用,那么能够由于inc(..., data)中的, 只有for(n + 1), 所以inc(..., data) = inc(n + 1, data), inc的实现, 就是一个利用for(n + 1)开始进行的加法; 最终MUL(x, y)展开:

for1

    for2 for3... for(x +1)                <= 其中for(x + 1)就是执行++s

for2

    for3 for4... for(x +2)

for3

    for4 for5... for(x +3)

...

for(y)

    for(y + 1) for(y +2)... for(x + y)

这样就实现了利用一组for实现两层迭代

下面是乘法的完整代码:

#define ADD_N(x, y,n)                     TUPLE_SEL(2, 0, CONN(FOR,n)((x, y), ADD_NO_END_I, ADD_INC_I))

#define MUL(x, y)                             TUPLE_SEL(3, 0, FOR1((0, x, y), MUL_NO_END_I, MUL_INC_I))

#define MUL_NO_END_I(i,sxy)         TUPLE_SEL(3, 2, sxy)

#define MUL_INC_I(i,sxy)                 MUL_INC(i,TUPLE_SEL(3, 0, sxy), TUPLE_SEL(3, 1, sxy), TUPLE_SEL(3, 2, sxy))

#define MUL_INC(i, s, x,y)                (ADD_N(s,x, i), x, DEC(y))

8. 最后

宏编程最主要的作用还是代码生成, 看过上面的描述后实现一个

LOOP(n, fItem, delimit)

(n = 3, fItem(n) = typename Tn, delimit= , ) =>

typename T1, typename T2, typename T3

的功能宏, 已经很容易了

, 由于boost里面已经有preprocessor库了, 它的稳定性、功能、可移植性都比自己写的要高出很多; 所以, 这篇宏运用的文字, 主要也就是让我这样的普通c++程序员有个底, 到底boost那些神秘强大的宏都在怎样做成代码的, 最终使用的, 还是boost.

这篇本来是作为自己宏编程笔记的, 毕竟被人们冠以奇技淫巧的宏编程和元编程是为库作者准备的, 我也就看看, 隔天就忘了... Kevin Lynx代码自动生成-宏递归思想中学习宏生成代码, boost中学习宏的图灵完备性...

下面是这篇文章提到的代码

http://download.csdn.net/source/1842009

1. 防止多重包含 2

2. 条件编译 2

3. 定义字面值常量 2

4. 定义为函数 2

5. 可变参数宏 3

6. 宏组合 3

6.1 一般用法 4

6.2 当宏参数是另一个宏的时候 4

6.2.1 '#''##'的情况 4

6.2.2 当有'#''##'的时候 4

6.3 '#''##'的一些应用特例 5

6.3.1 合并匿名变量名 5

6.3.2 填充结构 5

6.3.3 记录文件名 6

6.3.4 得到一个数值类型所对应的字符串缓冲大小 6

7. 其他使用例子 6

7.1 得到指定地址上的一个字节或字 6

7.2 求最大值和最小值 6

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

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

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

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

7.7 得到一个变量的地址(word宽度) 7

7.8 得到一个字的高位和低位字节 7

7.9 返回一个比X大的最接近的8的倍数 7

7.10 将一个字母转换为大写 7

7.11 判断字符是不是10进值的数字 7

7.12 判断字符是不是16进值的数字 8

7.13 防止溢出的一个方法 8

7.14 返回数组元素的个数 8

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

 

1. 防止多重包含

防止头文件多重包含:

如下

CODE

#ifndef MAIN_H_

#define MAIN_H_

其它内容

#endif

作用就是阻止这个头文件被多次include。多次include就会出现重复的定义情况,所以需要在每个头文件中都使用这个定义。

2. 条件编译

#ifdef _DEBUG

printf("this debug info/n");

#endif

如果没有定义_DEBUG宏,那么上面那一行是不会编译进去。但是定义了_DEBUG后,上面那行就会编译进去。

#ifdef  _M_IX86

#elif defined _M_MRX000

#endif

3. 定义字面值常量

方便修改,尽量做到修改地方少。

#define PRINT_STR "你好"

main()

{

printf(PRINT_STR);

return 0;

}

4. 定义为函数

#ifdef _DEBUG

#define A(x) a(x)

#else

#define A(x) b(x)

#endif

int a(int x)

{

return x+1;

}

int b(int x)

{

return x+100;

}

int main()

{

printf ("A(10) value is %d",A(10));

return 0;

}

其实也可以定义成#define A a

但是定义成A(x)后只有A后面带一个(x)类型的编译器才会执行替换,比较安全,可以保证只替换函数而不替换变量。

5. 可变参数宏

有些时候定义一个宏来代替某个函数,但是这个函数是可变参数的话,那就需要考虑办法了

定义方法如下

#include <iostream>

using namespace std;

 

#define PRINT(...)   cout<<(__VA_ARGS__)

#define PRINTC(...)   printf(__VA_ARGS__)

 

 

int _tmain(int argc, _TCHAR* argv[])

{

//C++6.0不可运行

PRINT("FLY编译例子");

PRINT(endl);

PRINTC("%d %s %s",1,"吃饭了吗 smile MM:)","/n");

return 0;

}

6. 宏组合

也就是###的用法

##是连接符号,连接两个宏

#是把名字代替成字符串

6.1 一般用法

#define s5(a)    supper_##a

#include <stdio.h>

void supper_printf(const char* p )

{

printf("this is supper printf:/n%s/n",p);

}

int main()

{

s5(printf)("hello owrld");

return 0;

}

#用法如下

#include <stdio.h>

#define s(p)   #p

int main()

{

printf(s(p)"/n");

return 0;

}

6.2 当宏参数是另一个宏的时候

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

6.2.1 '#''##'的情况

#define TOW      (2)

#define MUL(a,b) (a*b)

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

这行的宏会被展开为:

printf("%d*%d=%d/n", (2), (2), ((2)*(2)));

MUL里的参数TOW会被展开为(2)

6.2.2 当有'#''##'的时候

#define A          (2)

#define STR(s)     #s

#define CONS(a,b)  int(a##e##b)

printf("int max: %s/n",  STR(INT_MAX));  // INT_MAX include<climits>

这行会被展开为:

printf("int max: %s/n", "INT_MAX");

printf("%s/n", CONS(A,A));              // compile error

这一行则是:

printf("%s/n", int(AeA));

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

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

#define A           (2)

#define _STR(s)     #s

#define STR(s)     _STR(s)          // 转换宏

#define _CONS(a,b)  int(a##e##b)

#define CONS(a,b)  _CONS(a,b)       // 转换宏

printf("int max: %s/n",STR(INT_MAX));          //INT_MAX,int型的最大值,为一个变量#include<climits>

输出为: int max: 0x7fffffff

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

printf("%d/n", CONS(A, A));

输出为:200

CONS(A, A)-->_CONS((2), (2))-->int((2)e(2))

6.3 '#''##'的一些应用特例

6.3.1 合并匿名变量名

#define     ___ANONYMOUS1(type, var, line) type  var##line

#define  __ANONYMOUS0(type, line)  ___ANONYMOUS1(type, _anonymous,line)

#define    ANONYMOUS(type)  __ANONYMOUS0(type,__LINE__)

例:ANONYMOUS(static int);  : static int_anonymous70;  70表示该行行号;

第一层:ANONYMOUS(static int);  -->  __ANONYMOUS0(static int,__LINE__)

第二层:                       -->  ___ANONYMOUS1(static int, _anonymous, 70)

第三层:                       -->  static int  _anonymous70;

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

6.3.2 填充结构

#define    FILL(a)   {a, #a}

enum IDD{OPEN, CLOSE};

typedef struct MSG

{

IDD id;

const char * msg;

}MSG;

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};

相当于:

MSG _msg[] = {{OPEN, "OPEN"}, {CLOSE, "CLOSE"}};

6.3.3 记录文件名

#define  _GET_FILE_NAME(f)   #f

#define  GET_FILE_NAME(f)    _GET_FILE_NAME(f)

static char  FILE_NAME[] = GET_FILE_NAME(__FILE__);

6.3.4 得到一个数值类型所对应的字符串缓冲大小

#define  _TYPE_BUF_SIZE(type)  sizeof #type

#define  TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type)

char  buf[TYPE_BUF_SIZE(INT_MAX)];

-->  char  buf[_TYPE_BUF_SIZE(0x7fffffff)];

-->  char  buf[sizeof "0x7fffffff"];

这里相当于:

char  buf[11];

7. 其他使用例子

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

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

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

7.2 求最大值和最小值

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

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

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

#define FPOS( type, field ) /

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

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

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

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

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

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

#define FLOPW( ray, val ) /

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

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

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

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

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

7.8 得到一个字的高位和低位字节

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

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

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

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

7.10 将一个字母转换为大写

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

7.11 判断字符是不是10进值的数字

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

7.12 判断字符是不是16进值的数字

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

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

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

7.13 防止溢出的一个方法

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

7.14 返回数组元素的个数

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

7.15 对于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)))

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值