宏定义 订阅
宏(Macro),是一种批量处理的称谓。计算机科学里的宏是一种抽象(Abstraction),它根据一系列预定义的规则替换一定的文本模式。解释器或编译器在遇到宏时会自动进行这一模式替换。对于编译语言,宏展开在编译时发生,进行宏展开的工具常被称为宏展开器。宏这一术语也常常被用于许多类似的环境中,它们是源自宏展开的概念,这包括键盘宏和宏语言。绝大多数情况下,“宏”这个词的使用暗示着将小命令或动作转化为一系列指令。 展开全文
宏(Macro),是一种批量处理的称谓。计算机科学里的宏是一种抽象(Abstraction),它根据一系列预定义的规则替换一定的文本模式。解释器或编译器在遇到宏时会自动进行这一模式替换。对于编译语言,宏展开在编译时发生,进行宏展开的工具常被称为宏展开器。宏这一术语也常常被用于许多类似的环境中,它们是源自宏展开的概念,这包括键盘宏和宏语言。绝大多数情况下,“宏”这个词的使用暗示着将小命令或动作转化为一系列指令。
信息
格    式
#define  标识符   字符串
归属门类
计算机C语言
别    名
宏代换
中文名
宏定义
功    能
预处理功能
外文名
macro definition
宏定义简介
宏(Macro),是一种批量处理的称谓。计算机科学里的宏是一种抽象(Abstraction),它根据一系列预定义的规则替换一定的文本模式。解释器或编译器在遇到宏时会自动进行这一模式替换。对于编译语言,宏展开在编译时发生,进行宏展开的工具常被称为宏展开器。宏这一术语也常常被用于许多类似的环境中,它们是源自宏展开的概念,这包括键盘宏和宏语言。绝大多数情况下,“宏”这个词的使用暗示着将小命令或动作转化为一系列指令。宏的用途在于自动化频繁使用的序列或者是获得一种更强大的抽象能力。计算机语言如C语言或汇编语言有简单的宏系统,由编译器或汇编器的预处理器实现。C语言的宏预处理器的工作只是简单的文本搜索和替换,使用附加的文本处理语言如M4,C程序员可以获得更精巧的宏。Lisp类语言如Common Lisp和Scheme有更精巧的宏系统:宏的行为如同是函数对自身程序文本的变形,并且可以应用全部语言来表达这种变形。一个C宏可以定义一段语法的替换,然而一个Lisp的宏却可以控制一节代码的计算。获得了控制代码的执行顺序(见惰性计算和非限制函数)的能力,使得新创建的语法结构与语言内建的语法结构不可区分。例如,一种Lisp方言有cond而没有if,就可以使用宏由前者定义后者。Lisp语法的去部主要扩展,比如面向对象的CLOS系统,可以由宏来定义。MacroML有型别语法宏,一种有效的理解方式是把这种语法宏看作是多阶段计算。 [1]  宏定义是指用一个宏名(名字)来代表一个字符串。宏定义的功能是在编译预处理时,对程序中所有出现的“宏名”都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
收起全文
精华内容
参与话题
问答
  • 宏定义

    千次阅读 2017-03-21 17:57:58
    宏定义
    cocos2dx3.13  中在glfw3native.h取消了定义#undef APIENTRY导致3.13中其他依赖于ktmw32.h的类出错(APIENTRY宏定义取消),问题定位步骤,宏定义取消,未搜索到取消宏定义的类,从起始运行引用的第一个头文件开始定位,看宏定义是在引用了哪个头文件后才消失
    
    展开全文
  • 宏定义

    2009-05-05 13:16:00
    宏定义 在C语言源程序中允许用一个标识符来表示一个字符串, 称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换, 这称为“宏代换”或...

    宏定义
    在C语言源程序中允许用一个标识符来表示一个字符串, 称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换, 这称为“宏代换”或“宏展开”。

    宏定义是由源程序中的宏定义命令完成的。 宏代换是由预处理程序自动完成的。在C语言中,“宏”分为有参数和无参数两种。 下面分别讨论这两种“宏”的定义和调用。

    无参宏定义
    无参宏的宏名后不带参数。其定义的一般形式为: #define 标识符 字符串 其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。 “标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。在前面介绍过的符号常量的定义就是一种无参宏定义。 此外,常对程序中反复使用的表达式进行宏定义。例如: # define M (y*y+3*y) 定义M表达式(y*y+3*y)。在编写源程序时,所有的(y*y+3*y)都可由M代替,而对源程序作编译时,将先由预处理程序进行宏代换,即用(y*y+3*y)表达式去置换所有的宏名M,然后再进行编译。
    #define M (y*y+3*y)
    main(){
    int s,y;
    printf("input a number: ");
    scanf("%d",&y);
    s=3*M+4*M+5*M;
    printf("s=%d/n",s);
    }
    上例程序中首先进行宏定义,定义M表达式(y*y+3*y),在s= 3*M+4*M+5* M中作了宏调用。在预处理时经宏展开后该语句变为:s=3*(y*y+3*y)+4(y*y+3*y)+5(y*y+3*y);但要注意的是,在宏定义中表达式(y*y+3*y)两边的括号不能少。否则会发生错误。
    当作以下定义后: #difine M y*y+3*y在宏展开时将得到下述语句: s=3*y*y+3*y+4*y*y+3*y+5*y*y+3*y;这相当于; 3y2+3y+4y2+3y+5y2+3y;显然与原题意要求不符。计算结果当然是错误的。 因此在作宏定义时必须十分注意。应保证在宏代换之后不发生错误。对于宏定义还要说明以下几点:

    1. 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。

    2. 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。

    3. 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结 束。如要终止其作用域可使用# undef命令,例如: # define PI 3.14159
    main()
    {
    ……
    }
    # undef PIPI的作用域
    f1()
    ....表示PI只在main函数中有效,在f1中无效。
    4. 宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。
    #define OK 100
    main()
    {
    printf("OK");
    printf("/n");
    }
    上例中定义宏名OK表示100,但在printf语句中OK被引号括起来,因此不作宏代换。程序的运行结果为:OK这表示把“OK”当字符串处理。

    5. 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换。例如: #define PI 3.1415926
    #define S PI*y*y /* PI是已定义的宏名*/对语句: printf("%f",s);在宏代换后变为: printf("%f",3.1415926*y*y);

    6. 习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。

    7. 可用宏定义表示数据类型,使书写方便。例如: #define STU struct stu在程序中可用STU作变量说明: STU body[5],*p;#define INTEGER int 在程序中即可用INTEGER作整型变量说明: INTEGER a,b; 应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换, 而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。请看下面的例子: #define PIN1 int* typedef (int*) PIN2;从形式上看这两者相似, 但在实际使用中却不相同。下面用PIN1,PIN2说明变量时就可以看出它们的区别: PIN1 a,b;在宏代换后变成 int *a,b;表示a是指向整型的指针变量,而b是整型变量。然而:PIN2 a,b;表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是作字符
    代换。在使用时要分外小心,以避出错。

    8. 对“输出格式”作宏定义,可以减少书写麻烦。例9.3 中就采用了这种方法。
    #define P printf
    #define D "%d/n"
    #define F "%f/n"
    main(){
    int a=5, c=8, e=11;
    float b=3.8, d=9.7, f=21.08;
    P(D F,a,b);
    P(D F,c,d);
    P(D F,e,f);
    }

    带参宏定义

    C语言允许宏带有参数。在宏定义中的参数称为形式参数, 在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开, 而且要用实参去代换形参。

    带参宏定义的一般形式为: #define 宏名(形参表) 字符串 在字符串中含有各个形参。带参宏调用的一般形式为: 宏名(实参表);
    例如:
    #define M(y) y*y+3*y /*宏定义*/
    :
    k=M(5); /*宏调用*/
    : 在宏调用时,用实参5去代替形参y, 经预处理宏展开后的语句
    为: k=5*5+3*5
    #define MAX(a,b) (a>b)?a:b
    main(){
    int x,y,max;
    printf("input two numbers: ");
    scanf("%d%d",&x,&y);
    max=MAX(x,y);
    printf("max=%d/n",max);
    }
    上例程序的第一行进行带参宏定义,用宏名MAX表示条件表达式(a>b)?a:b,形参a,b均出现在条件表达式中。程序第七行max=MAX(x,
    y)为宏调用,实参x,y,将代换形参a,b。宏展开后该语句为: max=(x>y)?x:y;用于计算x,y中的大数。对于带参的宏定义有以下问题需要说明:

    1. 带参宏定义中,宏名和形参表之间不能有空格出现。
    例如把: #define MAX(a,b) (a>b)?a:b写为: #define MAX (a,b) (a>b)?a:b 将被认为是无参宏定义,宏名MAX代表字符串 (a,b)(a>b)?a:b。
    宏展开时,宏调用语句: max=MAX(x,y);将变为: max=(a,b)(a>b)?a:b(x,y);这显然是错误的。

    2. 在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。

    3. 在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
    #define SQ(y) (y)*(y)
    main(){
    int a,sq;
    printf("input a number: ");
    scanf("%d",&a);
    sq=SQ(a+1);
    printf("sq=%d/n",sq);
    }
    上例中第一行为宏定义,形参为y。程序第七行宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换y,再用(y)*(y) 代换SQ,得到如下语句: sq=(a+1)*(a+1); 这与函数的调用是不同的, 函数调用时要把实参表达式的值求出来再赋予形参。 而宏代换中对实参表达式不作计算直接地照原样代换。

    4. 在宏定义中,字符串内的形参通常要用括号括起来以避免出错。 在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的。如果去掉括号,把程序改为以下形式:
    #define SQ(y) y*y
    main(){
    int a,sq;
    printf("input a number: ");
    scanf("%d",&a);
    sq=SQ(a+1);
    printf("sq=%d/n",sq);
    }
    运行结果为:input a number:3
    sq=7 同样输入3,但结果却是不一样的。问题在哪里呢? 这是由于代换只作符号代换而不作其它处理而造成的。 宏代换后将得到以下语句: sq=a+1*a+1; 由于a为3故sq的值为7。这显然与题意相违,因此参数两边的括号是不能少的。即使在参数两边加括号还是不够的,请看下面程序:
    #define SQ(y) (y)*(y)
    main(){
    int a,sq;
    printf("input a number: ");
    scanf("%d",&a);
    sq=160/SQ(a+1);
    printf("sq=%d/n",sq);
    }
    本程序与前例相比,只把宏调用语句改为: sq=160/SQ(a+1); 运行本程序如输入值仍为3时,希望结果为10。但实际运行的结果如下:input a number:3 sq=160为什么会得这样的结果呢?分析宏调用语句,在宏代换之后变为: sq=160/(a+1)*(a+1);a为3时,由于“/”和“*”运算符优先级和结合性相同, 则先作160/(3+1)得40,再作40*(3+1)最后得160。为了得到正确答案应在宏定义中的整个字符串外加括号, 程序修改如下
    #define SQ(y) ((y)*(y))
    main(){
    int a,sq;
    printf("input a number: ");
    scanf("%d",&a);
    sq=160/SQ(a+1);
    printf("sq=%d/n",sq);
    }
    以上讨论说明,对于宏定义不仅应在参数两侧加括号, 也应在整个字符串外加括号。

    5. 带参的宏和带参函数很相似,但有本质上的不同,除上面已谈到的各点外,把同一表达式用函数处理与用宏处理两者的结果有可能是不同的。main(){
    int i=1;
    while(i<=5)
    printf("%d/n",SQ(i++));
    }
    SQ(int y)
    {
    return((y)*(y));
    }#define SQ(y) ((y)*(y))
    main(){
    int i=1;
    while(i<=5)
    printf("%d/n",SQ(i++));
    }
    在上例中函数名为SQ,形参为Y,函数体表达式为((y)*(y))。在例9.6中宏名为SQ,形参也为y,字符串表达式为(y)*(y))。 两例是相同的。例9.6的函数调用为SQ(i++),例9.7的宏调用为SQ(i++),实参也是相同的。从输出结果来看,却大不相同。分析如下:在例9.6中,函数调用是把实参i值传给形参y后自增1。 然后输出函数值。因而要循环5次。输出1~5的平方值。而在例9.7中宏调用时,只作代换。SQ(i++)被代换为((i++)*(i++))。在第一次循环时,由于i等于1,其计算过程为:表达式中前一个i初值为1,然后i自增1变为2,因此表达式中第2个i初值为2,两相乘的结果也为2,然后i值再自增1,得3。在第二次循环时,i值已有初值为3,因此表达式中前一个i为3,后一个i为4, 乘积为12,然后i再自增1变为5。进入第三次循环,由于i 值已为5,所以这将是最后一次循环。计算表达式的值为5*6等于30。i值再自增1变为6,不再满足循环条件,停止循环。从以上分析可以看出函数调用和宏调用二者在形式上相似, 在本质上是完全不同的。

    6. 宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。看下面的例子。
    #define SSSV(s1,s2,s3,v) s1=l*w;s2=l*h;s3=w*h;v=w*l*h;
    main(){
    int l=3,w=4,h=5,sa,sb,sc,vv;
    SSSV(sa,sb,sc,vv);
    printf("sa=%d/nsb=%d/nsc=%d/nvv=%d/n",sa,sb,sc,vv);
    }
    程序第一行为宏定义,用宏名SSSV表示4个赋值语句,4 个形参分别为4个赋值符左部的变量。在宏调用时,把4 个语句展开并用实参代替形参。使计算结果送入实参之中。

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

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

    常见的宏定义有两种,不带参数的宏定义和带参数的宏定义。
    无参宏定义
    无参数宏定义的格式为:
    #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()。

    展开全文
  • 宏定义有无参数宏定义和带参数宏定义两种。 无参数的宏定义的一般形式为 # 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
    
    
    
    展开全文
  • C语言宏定义(常用宏定义

    千次阅读 多人点赞 2018-11-07 12:50:10
    C语言常用宏定义常用宏定义数值相关的宏定义字符相关的宏定义byte相关的宏定义bit相关的宏定义数组与结构体相关的宏定义对齐的宏定义 常用宏定义 数值相关的宏定义 闰年的判断 ,年份可以整除4并且不能整除...
  • C语言宏定义宏定义函数

    千次阅读 2019-03-10 17:13:18
    C语言宏定义宏定义函数 宏定义可以帮助我们防止出错,提高代码的可移植性和可读性等。  在软件开发过程中,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为宏定义。那么究竟...
  • c c++宏定义使用编译器展开宏定义

    千次阅读 2019-02-18 17:12:40
    最近研究VLC源码,对于其中模块加载对接部分很是困惑,跟踪代码的时候总会有断掉线索的嫌疑,因为其中就很多用函数指针+宏定义来实现的,对于其中的宏定义实现的函数定义,自行展开宏定义去分析很是麻烦,还不可靠,...
  • C++ 宏定义

    万次阅读 多人点赞 2016-10-19 16:28:04
    ———————— #define基本用法 ————————#define命令是C语言中的一个宏定义命令,它用来将一个标识符(宏名)定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。程序编译之前,编译的...
  • 尽管函数式宏定义和普通函数相比有很多缺点,但只要小心使用还是会显著提高代码的执行效率,毕竟省去了分配和释放栈帧、传参、传返回值等一系列工作,因此那些简短并且被频繁调用的函数经常用函数式宏定义来代替实现...
  • 取消宏定义

    2019-10-04 23:23:07
    今天遇到一个问题是:重复宏定义。想到的解决方法是使用undef取消该重复宏。
  • 宏定义 使用分析

    千次阅读 2017-02-09 12:48:03
    宏定义
  • 宏定义详解

    万次阅读 多人点赞 2017-08-29 15:32:09
    宏定义的黑魔法 - 宏菜鸟起飞手册 宏定义在C系开发中可以说占有举足轻重的作用。底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行。而在更高层级进行...

空空如也

1 2 3 4 5 ... 20
收藏数 48,016
精华内容 19,206
关键字:

宏定义