精华内容
下载资源
问答
  • 宏定义有无参数宏定义和带参数宏定义两种。 无参数的宏定义的一般形式为 # define 标识符 字符序列 其中# define之后的标识符称为宏定义名(简称宏名),要求宏名与字符序列之间用空格符分隔。这种宏定义要求编译...

    宏定义有无参数宏定义和带参数宏定义两种。

    无参数的宏定义的一般形式为

    # define 标识符 字符序列
    

    其中# define之后的标识符称为宏定义名(简称宏名),要求宏名与字符序列之间用空格符分隔。这种宏定义要求编译预处理程序将源程序中随后所有的定名的出现(注释与字符串常量中的除外)均用字符序列替换之。前面经常使用的定义符号常量是宏定义的最简单应用。如有:

           # define TRUE 1
           # define FALSE 0
    

    则在定义它们的源程序文件中,凡定义之后出现的单词TRUE将用1替代之;出现单词FALSE将用0替代之。
    在宏定义的#之前可以有若干个空格、制表符,但不允许有其它字符。宏定义在源程序中单独另起一行,换行符是宏定义的结束标志。如果一个宏定义太长,一行不 够时,可采用续行的方法。续行是在键人回车符之前先键入符号"/"。注意回车要紧接在符号"/"之后,中间不能插入其它符号。
    宏定义的有效范围称为宏定义名的辖域,辖域从宏定义的定义结束处开始到其所在的源程序文件末尾。宏定义名的辖域不受分程序结构的影响。可以用预处理命令#undef终止宏定义名的辖域。
    在新的宏定义中,可以使用前面已定义的宏名。例如,

             # define R 2.5
             # define PI 3.1415926
             # define Circle 2*PI*R
             # define Area PI* R * R
    

    程序中的Circle被展开为23.1415926 2.5, Area被展开为3.14159262.52.5。
    如有必要,宏名可被重复定义。被重复定义后,宏名原先的意义被新意义所代替。
    通常,无参数的宏定义多用于定义常量。程序中统一用宏名表示常量值,便于程序前后统一,不易出错,也便于修改,能提高程序的可读性和可移植性。特别是给数组元素个数一个宏定义,并用宏名定义数组元素个数能部分弥补数组元素个数固定的不足。
    注意:预处理程序在处理宏定义时,只作字符序列的替换工作,不作任何语法的检查。如果宏定义不当,错误要到预处理之后的编译阶段才能发现。
    宏定义以换行结束,不需要分号等符号作分隔符。如有以下定定义:

       # define PI 3.1415926;
    

    原希望用PI求圆的周长的语句

       c=2*PI*r;
    

    经宏展开后,变成

      c=2*3.1415926*r;
    

    这就不能达到希望的要求。
    带参数宏定义进一步扩充了无参数宏定义的能力,在字符序列替换同时还能进行参数替换。带参数定定义的一般形式为

    # define 标识符(参数表)字符序列
    

    其中参数表中的参数之间用逗号分隔,字符序列中应包含参数表中的参数。在定义带参数的宏时,宏名标识符与左圆括号之间不允许有空白符,应紧接在一起,否则变成了无参数的宏定义。如有宏定义:

    # define MAX(A,B) ((A) > (B)?(A):(B))
    

    则代码 y= MAX( p+q, u+v)将被替换成 y=((p+q) >(u+v)?(p+q):(u+v)。
    程序中的宏调用是这样被替换展开的,分别用宏调用中的实在参数字符序列(如p+q和u+V) 替换宏定义字符序列中对应所有出现的形式参数(如用p+q替代所有形式参数A,用u+V替代所有形式参数B),而宏定义字符序列中的不是形式参数的其它字 符则保留。这样形成的字符序列,即为宏调用的展开替换结果。宏调用提供的实在参数个数必须与宏定义中的形式参数个数相同。
    注意:宏调用与函数调用的区别。

    函数调用宏调用
    程序运行时执行编译预处理阶段进行
    对实参有类型要求实参与形参没有类型的概念,只有字符序列的对应关系
    返回一个值C代码
    实参表达式分别独立求值在前,执行函数体在后实在参数字符序列替换形式参数,替换后,实在参数字符序列就与相邻的字符自然连 接,实在参数的独立性就不一定依旧存在。
    1. 函数调用在程序运行时实行,而宏展开是在编译的预处理阶段进行;
    2. 函数调用占用程序运行时间,宏调用只占编译时间
    3. 函数调用 对实参有类型要求,而宏调用实参与宏定义形式参数之间没有类型的概念只有字符序列的对应关系
    4. 函数调用可返回一个值,宏调用获得希望的C代码
    5. 函数调用时,实参表达式分别独立求值在前,执行函数体在后。宏调用是实在参数字符序列替换形式参数。替换后,实在参数字符序列就与相邻的字符自然连 接,实在参数的独立性就不一定依旧存在。如下面的宏定义:
      # define SQR(x) x*x
    

    希望实现表达式的平方计算。对于宏调用
    P=SQR(y)
    能得到希望的宏展开p= yy。但对于宏调用q=SQR(u+v)得到的宏展开是q=u+Vu+V。显然,后者的展开结果不是程序设计者所希望的。进一步,为了保证宏调用的独立性,作为算式的宏定义也应加括
    。如 SQR宏定义改写成:

      # define SQR(x) (x)*(x)
    

    才是正确的宏定义。(转载的原文出错了,我这才是正确的!!!)

    使用一些宏跟踪调试

    A N S I标准说明了五个预定义的宏名。它们是:

    _ L I N E _ (两个下划线),对应%d

    _ F I L E _ 对应%s

    _ D A T E _ 对应%s

    _ T I M E _ 对应%s

    _ S T D C _

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

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

    _ L I N E F I L E _宏指令在有关# l i n e的部分中已讨论,这里讨论其余的宏名。

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

    源代码翻译到目标代码的时间作为串包含在_ T I M E _中。串形式为时:分:秒。

    如果实现是标准的,则宏_ S T D C _含有十进制常量1。如果它含有任何其它数,则实现是

    非标准的。

    可以定义宏,例如:

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

    #ifdef _DEBUG

    #define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%s”,date,LINE,FILE)

    #else

      #define DEBUGMSG(msg,date)
    

    #endif

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

    用小括号包含。

    例如:#define ADD(a,b) (a+b)

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

    例如:#difne DO(a,b) a+b;/

                   a++;
    

    应用时:if(….)

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

    解决方法: #difne DO(a,b) do{a+b;/

                   a++;}while(0)
    

    宏中"#“和”##"的用法
    一、一般用法
    我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起(这里说的是在预处理是对源文件的操作).
    用法:

    #include<cstdio> 
    #include<climits> 
    using namespace std;
    
    #define STR(s)     #s 
    #define CONS(a,b) int(a##e##b)
    
    int main() 
    { 
    printf(STR(vck));           // 输出字符串"vck" 
    printf("%d/n", CONS(2,3)); // 2e3 输出:2000 
    return 0; 
    }
    

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

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

    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).

    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
    这行会被展开为:
    printf(“int max: %s/n”, “INT_MAX”);

    printf("%s/n", CONS(A, A)); // compile error
    这一行则是:
    printf("%s/n", int(AeA));

    INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏.
    加这层宏的用意是把所有宏的参数在中间层里全部展开, 那么在转换宏里的那一个宏(_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
    输出为: 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))

    三、’’#’‘和’’##’'的一些应用特例

    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__在第二层才能被解开;

    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”}};

    3、记录文件名
    #define _GET_FILE_NAME(f) #f
    #define GET_FILE_NAME(f) _GET_FILE_NAME(f)
    static char FILE_NAME[] = GET_FILE_NAME(FILE);

    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]

    本文转载自http://hi.baidu.com/ufo008ahw/blog/item/5e943d4f5f49513caec3abd1.html)


    本文来自 叮当小菜 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/swee111/article/details/73275977?utm_source=copy

    更新


    //如果没有定义 HEADTEST_H_,继续往下
    //否则,就退出
    #ifndef  _HEADTEST_H_
    //定义 _HEADTEST_H_
    #define _HEADTEST_H_
    
    //宏定义,预编译阶段,直接替换
    
    #endif
    
    
    
    展开全文
  • 1、宏定义说明 宏定义是比较常用的预处理指令,即使用“标识符”来表示“替换列表”中的内容。标识符称为宏名,在预处理过程中,预处理器会把源程序中所有宏名,替换成宏定义中替换列表中的内容。 常见的宏定义有两...

    1、宏定义说明

    • 宏定义是比较常用的预处理指令,即使用“标识符”来表示“替换列表”中的内容。标识符称为宏名,在预处理过程中,预处理器会把源程序中所有宏名,替换成宏定义中替换列表中的内容。
    • 常见的宏定义有两种,不带参数的宏定义和带参数的宏定义。

    2、无参宏定义

    • 无参数宏定义的格式为:
      #define 标识符 替换列表
    • 替换列表可以是数值常量、字符常量、字符串常量等,故可以把宏定义理解为使用标识符表示一常量,或称符号常量。
    • 说明:
    • 1) # 可以不在行首,但只允许它前面有空格符。例如:
    #define PI 3.1416 //正确,该行#前允许有空格
    int a;#define N 5 //错误,该行#前不允许有空格外的其他字符
    • 2) 标识符和替换列表之间不能加赋值号 =,替换列表后不能加分号
    #define N =5 //虽语法正确,但预处理器会把N替换成=5
    int a[N]; //错误,因为宏替换之后为 int a[=5];
    • 宏定义不是语句,是预处理指令,故结尾不加分号。如果不小心添加了分号,虽然有时该宏定义没问题,但在宏替换时,可能导致 C 语法错误,或得不到预期结果。例如:
    #define N 5; //虽语法正确,但会把N替换成5;
    int a[N]; //语法错误,宏替换后,为int a[5;];错误
    • 3) 由于宏定义仅是做简单的文本替换,故替换列表中如有表达式,必须把该表达式用括号括起来,否则可能会出现逻辑上的“错误”。例如:
    #define N 3+2
    int r = N * N;
    • 宏替换后为:
    int r=3+2*3+2; //r=11
    • 如果采用如下形式的宏定义:
    #define N (3+2)
    int r=N*N;
    • 则宏替换后,为:
    int r=(3+2)*(3+2); //r=25
    • 4) 当替换列表一行写不下时,可以使用反斜线\作为续行符延续到下一行。例如:
    #define USA "The United \
    States of \
    America"
    • 该宏定义中替换列表为字符串常量,如果该串较长,或为了使替换列表的结构更清晰,可使用续行符  把该串分若干行来写,除最后一行外,每行行尾都必须加续行符 。
    • 如果调用 printf 函数,以串的形式输出该符号常量,即:
    printf("%s\n",USA);
    • 则输出结果为:The United States of America
    • 注意:续行符后直接按回车键换行,不能含有包括空格在内的任何字符,否则是错误的宏定义形式。

    3、带参宏定义

    • 带参数的宏定义格式为:
      #define 标识符(参数1,参数2,...,参数n) 替换列表
    • 例如,求两个参数中最大值的带参宏定义为:
    #define MAX(a,b) ((a)>(b)?(a) : (b))
    • 当有如下语句时:
    int c=MAX(5,3);
    • 预处理器会将带参数的宏替换成如下形式:
    int c=((5)>(3)?(5) : (3));
    • 故计算结果c=5。
    • 删除宏定义的格式为:
      #undef 标识符
    • 说明:
      • 1) 标识符与参数表的左括号之间不能有空格,否则预处理器会把该宏理解为普通的无参宏定义,故以下是错误的带参宏定义形式。
      #define MAX (a,b) ( (a) > (b) ? (a) : (b) ) //错误的带参宏定义格式
      • 2) 宏替换列表中每个参数及整个替换列表,都必须用一对小括号 () 括起来,否则可能会出现歧义。
    • 【例 1】以下程序试图定义求两个参数乘积的宏定义,欲使用该宏求 3 与 6 的乘积,分析该程序能否实现预期功能,如果不能,请给出修改方案。
    #include <stdio.h>
    #define MUL(a,b) (a*b)
    int main (void)
    {
        int c;
        c=MUL(3,5+1);
        printf("c=%d\n",c);
        return 0;
    }
    • 分析:
      • 1) 由于该宏定义中的替换列表中的参数没有加括号,故宏调用时,如果参数是个表达式,可能会出现歧义,得不到预期结果。
      • 本例中宏调用 c=MUL(3,5+1); 会替换成 c=(3*5+1)=16;,与预期功能不符。
      • 2) 虽然把宏调用时的参数 5+1 括起来,可达到题目要求的效果,但这属于治标不治本。为统一编程规范,把替换列表中的每个参数均加括号,整个替换列表也加括号。
      • 同时,为达到标本兼治,在宏定义时,除单一值参数外,应显式加括号。
      • 修改代码为:
      #include <stdio.h>
      #define MUL(a,b) ((a)*(b))//修改处1
      int main (void)
      {
          int c;
          c=MUL(3,(5+1);//修改处2
          printf("c=%d\n",c);
          return 0;
      }
    • 带参宏定义 VS 函教调用
      • 接下来将从调用发生时间、参数类型检查、参数是否需要空间、运行速度等几个主要方面进行对比分析带参宏定义与函数调用的差异。
      • 调用发生的时间
      • 在源程序进行编译之前,即预处理阶段进行宏替换;而函数调用则发生在程序运行期间。
    • 参数类型检查
      • 函数参数类型检查严格。程序在编译阶段,需要检查实参与形参个数是否相等及类型是否匹配或兼容,若参数个数不相同或类型不兼容,则会编译不通过。
      • 在预处理阶段,对带参宏调用中的参数不做检查。即宏定义时不需要指定参数类型,既可以认为这是宏的优点,即适用于多种数据类型,又可以认为这是宏的一个缺点,即类型不安全。故在宏调用时,需要程序设计者自行确保宏调用参数的类型正确。
    • 参数是否需要空间
      • 函数调用时,需要为形参分配空间,并把实参的值复制一份赋给形参分配的空间中。而宏替换,仅是简单的文本替换,且替换完就把宏名对应标识符删除掉,即不需要分配空间。
    • 执行速度
      • 函数在编译阶段需要检查参数个数是否相同、类型等是否匹配等多个语法,而宏替换仅 是简单文本替换,不做任何语法或逻辑检查。
      • 函数在运行阶段参数需入栈和出栈操作,速度相对较慢。
    • 代码长度
      • 由于宏替换是文本替换,即如果需替换的文本较长,则替换后会影响代码长度;而函数不会影响代码长度。
      • 故使用较频繁且代码量较小的功能,一般采用宏定义的形式,比采用函数形式更合适。前面章节频繁使用的 getchar(),准确地说,是宏而非函数。
    • 为了使该宏调用像函数调用,故把该宏设计成了带参数的宏定义:
      #define getchar() getc(stdin)
    • 故调用该宏时,需要加括号,即传空参数:getchar()。

    转载于:https://www.cnblogs.com/CH520/p/10146414.html

    展开全文
  • 宏定义是比较常用的预处理指令,即使用“标识符”来表示“替换列表”中的内容。标识符称为宏名,在预处理过程中,预处理器会把源程序中所有宏名,替换成宏定义中替换列表中的内容。 常见的宏定义有两种,不带参数的...

    宏定义是比较常用的预处理指令,即使用“标识符”来表示“替换列表”中的内容。标识符称为宏名,在预处理过程中,预处理器会把源程序中所有宏名,替换成宏定义中替换列表中的内容。

    常见的宏定义有两种,不带参数的宏定义和带参数的宏定义。
    无参宏定义
    无参数宏定义的格式为:
    #define 标识符 替换列表

    替换列表可以是数值常量、字符常量、字符串常量等,故可以把宏定义理解为使用标识符表示一常量,或称符号常量。

    说明:

    1. 可以不在行首,但只允许它前面有空格符。例如:

    #define PI 3.1416 //正确,该行#前允许有空格
    int a;#define N 5 //错误,该行#前不允许有空格外的其他字符

    1. 标识符和替换列表之间不能加赋值号 =,替换列表后不能加分号
      #define N =5 //虽语法正确,但预处理器会把N替换成=5
      int a[N]; //错误,因为宏替换之后为 int a[=5];
      宏定义不是语句,是预处理指令,故结尾不加分号。如果不小心添加了分号,虽然有时该宏定义没问题,但在宏替换时,可能导致 C 语法错误,或得不到预期结果。例如:
      #define N 5; //虽语法正确,但会把N替换成5;
      int a[N]; //语法错误,宏替换后,为int a[5;];错误

    2. 由于宏定义仅是做简单的文本替换,故替换列表中如有表达式,必须把该表达式用括号括起来,否则可能会出现逻辑上的“错误”。例如:
      #define N 3+2
      int r=NN;
      宏替换后为:
      int r=3+2
      3+2; //r=11
      如果采用如下形式的宏定义:
      #define N (3+2)
      int r=NN;
      则宏替换后,为:
      int r=(3+2)
      (3+2); //r=25

    3. 当替换列表一行写不下时,可以使用反斜线\作为续行符延续到下一行。例如:
      #define USA “The United
      States of
      America”
      该宏定义中替换列表为字符串常量,如果该串较长,或为了使替换列表的结构更清晰,可使用续行符 \ 把该串分若干行来写,除最后一行外,每行行尾都必须加续行符 \。

    如果调用 printf 函数,以串的形式输出该符号常量,即:
    printf("%s\n",USA);
    则输出结果为:The United States of America

    注意:续行符后直接按回车键换行,不能含有包括空格在内的任何字符,否则是错误的宏定义形式。
    带参宏定义
    带参数的宏定义格式为:
    #define 标识符(参数1,参数2,…,参数n) 替换列表

    例如,求两个参数中最大值的带参宏定义为:
    #define MAX(a,b) ((a)>(b)?(a) : (b))
    当有如下语句时:
    int c=MAX(5,3);
    预处理器会将带参数的宏替换成如下形式:
    int c=((5)>(3)?(5) : (3));
    故计算结果c=5。

    删除宏定义的格式为:
    #undef 标识符

    说明:

    1. 标识符与参数表的左括号之间不能有空格,否则预处理器会把该宏理解为普通的无参宏定义,故以下是错误的带参宏定义形式。
      #define MAX (a,b) ( (a) > (b) ? (a) : (b) ) //错误的带参宏定义格式
    2. 宏替换列表中每个参数及整个替换列表,都必须用一对小括号 () 括起来,否则可能会出现歧义。

    【例 1】以下程序试图定义求两个参数乘积的宏定义,欲使用该宏求 3 与 6 的乘积,分析该程序能否实现预期功能,如果不能,请给出修改方案。
    #include <stdio.h>
    #define MUL(a,b) (a*b)
    int main (void)
    {
    int c;
    c=MUL(3,5+1);
    printf(“c=%d\n”,c);
    return 0;
    }
    分析:

    1. 由于该宏定义中的替换列表中的参数没有加括号,故宏调用时,如果参数是个表达式,可能会出现歧义,得不到预期结果。

    本例中宏调用 c=MUL(3,5+1); 会替换成 c=(3*5+1)=16;,与预期功能不符。

    1. 虽然把宏调用时的参数 5+1 括起来,可达到题目要求的效果,但这属于治标不治本。为统一编程规范,把替换列表中的每个参数均加括号,整个替换列表也加括号。

    同时,为达到标本兼治,在宏定义时,除单一值参数外,应显式加括号。

    修改代码为:
    #include <stdio.h>
    #define MUL(a,b) ((a)*(b))//修改处1
    int main (void)
    {
    int c;
    c=MUL(3,(5+1);//修改处2
    printf(“c=%d\n”,c);
    return 0;
    }
    带参宏定义 VS 函教调用
    接下来将从调用发生时间、参数类型检查、参数是否需要空间、运行速度等几个主要方面进行对比分析带参宏定义与函数调用的差异。

    调用发生的时间
    在源程序进行编译之前,即预处理阶段进行宏替换;而函数调用则发生在程序运行期间。

    参数类型检查
    函数参数类型检查严格。程序在编译阶段,需要检查实参与形参个数是否相等及类型是否匹配或兼容,若参数个数不相同或类型不兼容,则会编译不通过。

    在预处理阶段,对带参宏调用中的参数不做检查。即宏定义时不需要指定参数类型,既可以认为这是宏的优点,即适用于多种数据类型,又可以认为这是宏的一个缺点,即类型不安全。故在宏调用时,需要程序设计者自行确保宏调用参数的类型正确。

    参数是否需要空间
    函数调用时,需要为形参分配空间,并把实参的值复制一份赋给形参分配的空间中。而宏替换,仅是简单的文本替换,且替换完就把宏名对应标识符删除掉,即不需要分配空间。

    执行速度
    函数在编译阶段需要检查参数个数是否相同、类型等是否匹配等多个语法,而宏替换仅 是简单文本替换,不做任何语法或逻辑检查。

    函数在运行阶段参数需入栈和出栈操作,速度相对较慢。

    代码长度
    由于宏替换是文本替换,即如果需替换的文本较长,则替换后会影响代码长度;而函数不会影响代码长度。

    故使用较频繁且代码量较小的功能,一般采用宏定义的形式,比采用函数形式更合适。前面章节频繁使用的 getchar(),准确地说,是宏而非函数。

    为了使该宏调用像函数调用,故把该宏设计成了带参数的宏定义:

    #define getchar() getc(stdin)
    故调用该宏时,需要加括号,即传空参数:getchar()。

    展开全文
  • 宏定义是C语言提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译。 宏定义和操作符的区别:宏定义是替换,不做计算,也不做表达式求解。宏定义又称为宏代换、宏替换,简称“宏”。在 ...

    宏定义是C语言提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译。

    宏定义和操作符的区别:宏定义是替换,不做计算,也不做表达式求解。宏定义又称为宏代换、宏替换,简称“宏”。在 C 语言中,宏是产生内嵌代码的唯一方法。对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方法。

    宏定义的格式:#DEFINE 标识符 字符串

    掌握"宏"概念的关键是“换”,一切以换为前提、做任何事情之前先要换,准确理解之前就要“换”,即在对相关命令或语句的含义和功能作具体分析之前就要换。

    定义宏需要注意以下几点:

    (1)宏名一般用大写;

    (2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义;

    (3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查;

    (4)宏定义末尾不加分号;

    (5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头;

    (6)可以用#undef命令终止宏定义的作用域;

    (7)宏定义允许嵌套;

    (8)字符串( " " )中永远不包含宏;

    (9)宏定义不分配内存,变量定义分配内存;

    (10)宏定义不存在类型问题,它的参数也是无类型的。

    在C语言/类C语言环境中,宏的作用有如下几种:

    1.取代Magic Number

    这个作用在嵌入式开发时太重要了。一个SoC 50多页的datasheet,上百个寄存器的位操作,如果没有很好的语义宏定义,调程序查手册绝对是崩溃的节奏!

    2.防止重复定义

    #ifndef XXXX

    #define XXXX

    #endif

    3.快速控制代码编译

    #define SIMULATION_DEBUG 1

    #if SIMULATION_DEBUG

    use simulated data

    #else

    real data

    #endif

    4.编译系统控制链

    在某些系统下,可以把宏定义传递给编译器,从而通过编译脚本(makefile)来控制编译选项,例如,配合上面代码,就可以在Makefile里定义:

    CDEFINES=$(CDEFINES) /SIMULATION_DEBUG

    说到编译,自然还要提一下 ANSI C中预定义的几个非常有用的编译宏:

    • _ L I N E _

    • _ F I L E _

    • _ D A T E _

    • _ T I M E _

    • _ S T D C _

    例如

    #define DEBUGMSG(msg,date) printf(msg);

    printf(“%d%d%d”,date,_LINE_,_FILE_)

    5.使用typedef

    C语言下typedef也是属于宏定义,我会告诉你C语言下struct+typedef+函数指针可以做到面向对象的继承,重载和多态吗?(有兴趣可以看一下MFC的源码实现,把传统的C风格的Windows API各种包装修饰和装逼。)

    typedef int (*PFFunc1)(int);

    6.简化操作

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

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

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

    展开全文
  • 宏定义

    2019-08-31 10:46:11
    常见的宏定义有两种,不带参数的宏定义和带参数的宏定义。 无参宏定义 无参数宏定义的格式为: #define 标识符 替换列表 替换列表可以是数值常量、字符常量、字符串常量等,故可以把宏定义理解为使用标识符表示...
  • C++ 宏定义

    万次阅读 多人点赞 2016-10-19 16:28:04
    ———————— #define基本用法 ————————#define命令是C语言中的一个宏定义命令,它用来将一个标识符(宏名)定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。程序编译之前,编译的...
  • C语言宏定义宏定义函数

    千次阅读 2016-03-13 19:55:59
    要写好C语言,漂亮的宏定义是非常重要的。宏定义可以帮助我们防止出错,提高代码的可移植性和可读性等。  在软件开发过程中,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为...
  • C语言宏定义

    2019-05-12 12:45:18
    C语言宏定义: 本节介绍不带参数和带参数宏定义的使用及注意事项,以及宏定义和函数调用的区别: 不带参数的宏定义: 用一个指定的标识符(即名字) 来代表一个字符串,它的一般形式为: #define 标识符 字符串 列:...
  • 宏定义是比较常用的预处理指令,其原理就是替换,即使用“标识符”来表示“替换列表”中的内容。标识符称为宏名,在预处理过程中,预处理器会把源程序中所有宏名,替换成宏定义中替换列表中的内容。 常见的宏定义有...
  • C语言的头文件和宏定义详解

    万次阅读 多人点赞 2018-01-24 21:00:44
    之前对C语言的头文件和宏定义抱着一知半解的态度,现理清思路并以文字的形式整理出来,以供不时之需 头文件 头文件,顾名思义就是定义在C语言文件头部的那一坨东西 #include 这就是一个标准输入输出的...
  • 宏定义详解

    2019-09-26 15:50:05
    宏定义有无参数宏定义和带参数宏定义两种。 无参数的宏定义的一般形式为 # define 标识符 字符序列其中# define之后的标识符称为宏定义名(简称宏名),要求宏名与字符序列之间用空格符分隔。这种宏定义要求编译预处理...
  • C++宏定义

    2016-08-04 17:22:55
     (一) 宏定义  (二) 文件包含  (三) 条件编译  在C++中,我们一般用const定义符号常量。很显然,用const定义常量比用define定义常量更好。  在使用宏定义时应注意的是:  (a) 在书写#...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 153,436
精华内容 61,374
关键字:

对于以下宏定义