宏定义 订阅
宏(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]  宏定义是指用一个宏名(名字)来代表一个字符串。宏定义的功能是在编译预处理时,对程序中所有出现的“宏名”都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
收起全文
精华内容
下载资源
问答
  • 要写好C语言,漂亮的宏定义是非常重要的。宏定义可以帮助我们防止出错,提高代码的可移植性和可读性等。
  • 今天小编就为大家分享一篇C语言#define拼接宏定义实现方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 在1999年版本的ISO C 标准中,可以象函数一样,定义时可以带有可变参数。的语法和函数的语法类似
  • 常用宏定义

    2017-06-30 12:05:05
    常用的几个宏定义
  • 仅供参考,分享出来而已
  • C语言宏定义用法大全

    2018-06-11 22:12:36
    关于C语言宏定义的用法,作了一个总结,除了常用宏定义外,还有类似函数的宏的用法
  • 内联函数在运行时可调试,而宏定义不可以;2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会; 3.内联函数可以访问类的成员变量,宏定义则不能; 4.在类中声明同时定义的成员...
  • 写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性 等等。
  • Unity编辑器语言开发的,拖入项目Assest目录下,点击Unity编辑器上方栏的“我的拓展编辑器”即可打开使用。项目展示写了一个博客:...欢迎大家讨论
  • C++宏定义说明(详解)

    2017-11-28 22:21:39
    C++_宏定义说明(详解)
  • 20个C语言中常用宏定义总结
  • Inphic专业级宏定义游戏鼠标-安装程序
  • stm32端口宏定义

    2015-08-27 11:48:12
    STM32 像51一样端口任意宏定义.包括端口上拉,下拉,设置为输入,输出。模拟I2C时用到
  • define宏定义和const常量定义之间的区别。
  • dll导出导入宏定义使用总结,主要讲了两种宏定义的使用方法
  • c语言宏定义详解

    2018-04-10 12:37:10
    写好 C 语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方 便性 等等。下面列举一些成熟软件中常用得宏定义。。。。。。
  • iOS常用宏定义framework

    2014-02-20 17:09:47
    iOS常用宏定义和常用类别方法的framework包.
  • c 语言中形式参数和实际参数的宏定义分析 C 语言允许宏带有参数在宏定义中的参数称为形式参数在宏调用中的参数称为实际参 数这点和函数有些类似 对带参数的宏在调用中不仅要宏展开而且要用实参去代换形参 带参宏定义...
  • C语言宏定义使用分析

    2020-12-26 07:36:47
    1、如何区分宏定义中的“宏名称”和“宏字符串”?对于带参数的宏又该注意什么? 在宏定义中,“宏名称”和“宏字符串”是通过“空格”来区分的。编译器在处理时宏定义时,首先从“#define”后第一个空格开始读取...
  • 宏定义有无参数宏定义和带参数宏定义两种。 无参数的宏定义的一般形式为 # 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

    展开全文
  • 宏定义

    千次阅读 多人点赞 2018-02-06 13:20:57
    定义一个带参的,使两个参数的值互换,并写出程序,输入两个数作为使用宏时的实参。输出已交换后的两个值。 输入 两个数,空格隔开 输出 交换后的两个数,空格隔开 样例输入 1 2 ...
    题目描述
    定义一个带参的宏,使两个参数的值互换,并写出程序,输入两个数作为使用宏时的实参。输出已交换后的两个值。
    输入
    两个数,空格隔开
    输出
    交换后的两个数,空格隔开
    样例输入
    1 2
    样例输出
    2 1
    #include<stdio.h>
    #define Y(a,b) t=a;a=b;b=t;//两数交换位置
    int main(){
        int a,b,t;
        scanf("%d%d",&a,&b);
        Y(a,b)
        printf("%d %d",a,b);
        return 0;
    }
    

     #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时误用该命令,使得程序的运行与预期的目的不一致,或者在读别人写的程序时,把运行结果理解错误,这对C语言的学习很不利。下面将分别对基本用法和特殊做详细介绍。

    一、#define的基本用法

    1 #define命令剖析

    1.1   #define的概念

    #define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。

    (1)   无参数宏定义:

    #define   <宏名>  <字符串>

    例:   #define PI 3.1415926

    注意几个错误的用法:

           #define NAME "zhangyuncong"

          #define 0x abcd

          #define NAME "zhang

         #define NAME "zhangyuncong"

    四个题答案都是否定的。 

    第一个,""内的东西不会被宏替换。这一点应该大都知道。 

    第二个,宏定义前面的那个必须是合法的用户标识符 

    第三个,宏定义也不是说后面东西随便写,不能把字符串的两个""拆开。 

    第四个:只替换标识符,不替换别的东西

    注意:

    1)不替换程序中字符串里的东西。 

    2)第一位置只能是合法的标识符(可以是关键字) 

    3)第二位置如果有字符串,必须把""配对。 

    4)只替换与第一位置完全相同的标识符

    也就是说,这种情况下记住:#define 第一位置 第二位置 

    (2) 带参数的宏定义

     #define   <宏名> (<参数表>)   <宏体>     例: #define   A(x) x

    则遇到MAX(1+2,value)则会把它替换成: 

    ((1+2)>(value)?(1+2):(value)) 

    注意事项和无参宏着不多。

    一个标识符被宏定义后,该标识符便是一个宏名。这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。

    还有就是老生常谈的话:记住这是简单的替换而已,不要在中间计算结果,一定要替换出表达式之后再算。

    2.1 简单宏定义使用中出现的问题

    在简单宏定义的使用中,当替换文本所表示的字符串为一个表达式时,容易引起误解和误用。如下例:

    例1   #define   N   2+2

    void main()

    {

    int   a=N*N;

     printf(“%d”,a);

    }

    (1) 出现问题:在此程序中存在着宏定义命令,宏N代表的字符串是2+2,在程序中有对宏N的使用,一般同学在读该程序时,容易产生的问题是先求解N为2+2=4,然什么结后在程序中计算a时使用乘法,即N*N=4*4=16,其实该题的结果为8,为果有这么大的偏差?

    (2) 问题解析:如1节所述,宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏N出现的地方只是简单地使用串2+2来代替N,并不会增添任何的符号,所以对该程序展开后的结果是a=2+2*2+2,计算后=8,这就是宏替换的实质,如何写程序才能完成结果为16的运算呢?

    (3)解决办法:将宏定义写成如下形式

    #define   N   (2+2)

    这样就可替换成(2+2)*(2+2)=16

    2.2 带参数的宏定义出现的问题

    在带参数的宏定义的使用中,极易引起误解。例如我们需要做个宏替换能求任何数的平方,这就需要使用参数,以便在程序中用实际参数来替换宏定义中的参数。一般学生容易写成如下形式: #define   area(x)   x*x 这在使用中是很容易出现问题的,看如下的程序

    void main()

    {     int   y=area(2+2);     printf(“%d”,y);

    }

    按理说给的参数是2+2,所得的结果应该为4*4=16,但是错了,因为该程序的实际结果为8,仍然是没能遵循纯粹的简单替换的规则,又是先计算再替换了,在这道程序里,2+2即为area宏中的参数,应该由它来替换宏定义中的x,即替换成2+2*2+2=8了。那如果遵循(1)中的解决办法,把2+2 括起来,即把宏体中的x括起来,是否可以呢?  #define   area(x) (x)*(x),对于area(2+2),替换为(2+2)*(2+2)=16,可以解决;但是对于area(2+2)/area(2+2)又会怎么样呢?又错了,还是忘了遵循先替换再计算的规则了,这道题替换后会变为 (2+2)*(2+2)/(2+2)*(2+2)即4*4/4*4按照乘除运算规则,结果为16/4*4=4*4=16,那应该怎么呢?解决方法是在整个宏体上再加一个括号,即#define   area(x) ((x)*(x)),不要觉得这没必要,没有它,是不行的。      要想能够真正使用好宏定义,一定要记住先将程序中对宏的使用全部替换成它所代表的字符串,不要自作主张地添加任何其他符号,完全展开后再进行相应的计算,就不会写错运行结果。在编程使用宏替换时,当字符串中不只一个符号时,加上括号表现出优先级,如果是带参数的宏定义,则要给宏体中的每个参数加上括号,并在整个宏体上再加一个括号。

    展开全文
  • 喜欢读一些开源项目源码的人,总是会发现,大神的代码中总是有那么一些简短而高效的宏定义,点击进去一看,发现晦涩难懂,别说学习了,有时候理解都是一种困难,但是宏定义本身并没有那么难,但是写出一个好的宏当然...
  • 一、不带参数的宏定义 1.定义 2.#undef 指令取消宏定义 二、带参数的宏定义 1.定义 2.宏定义参数替换的注意事项 三、带参数的宏定义和带参函数的区别 一、不带参数的宏定义 1.定义 在程序中,经常会定义...

    目录

    一、不带参数的宏定义

    1.定义

    2.#undef 指令取消宏定义

    二、带参数的宏定义

    1.定义

    2.宏定义参数替换的注意事项

    三、带参数的宏定义和带参函数的区别


    一、不带参数的宏定义

    1.定义

    在程序中,经常会定义一些常量,例如圆周率3.1415、“ABC”等。如果这些常量在程序中频繁使用,难免会出现书写错误的情况。为了避免程序书写错误,可以使用不带参数的宏定义来定义这些常量。

    例如:#define PI 3.14159

    解析:#define是用来标识一个宏定义,标识符PI是定义的宏名,3.14159是宏体,它可以是常量或表达式等。

    一般情况下,宏定义需要放在源程序的开头,函数定义外,它的有效范围是从宏定义语句开始至源文件结束。一般宏名都是大写字母,方便与其他操作符区别。

    使用宏定义PI就可以在随后的源代码中出现PI的位置替换为3.14159.

    案例演示:

    #include <stdio.h>
    #define PI 3.141592
    
    int main(){
    	int r;
    	scanf("%d",&r);
    	double area=PI*r*r;
    	double cicle=2*PI*r;
    	printf("半径为%d的圆面积:area=%lf  周长cicle=%lf\n",r,area,cicle);
    } 

     运行结果:

    2.#undef 指令取消宏定义

    #undef指令用于取消宏定义,当使用#define定义了一个宏之后,如果预处理在接下来的源代码中看到#undef指令,那么#undef指令后面这个宏就会失效。


    二、带参数的宏定义

    1.定义

    带参数的宏定义,其语法格式如下:

    #define 标识符(形参列表)字符串

    形参列表中的参数之间用逗号进行分隔。对于带参数的宏定义来说,同样需要使用字符串替换宏名,使用实参替换形参。

    由于宏定义在程序预处理的时候执行,因此,相对于函数来说,宏定义的开销要小一些。

    【注意】宏定义中的参数替换是“整体替换”。

    实例分析:

    #define ABS(x)((x)>=0?(X):-(X);

    int a=12;

    ABS(++a);

    我们期望的值是12,但实际a的结果为14,显然这个用来替代求绝对值函数是错误的。

    原因是在进行预处理时“ABS(++a)”替换为“(++a)((++a)>=0?(++a):-(++a);”,所以结果为14

    案例实现:

    #include <stdio.h>
    #define SWAP(a,b) {int temp;temp=a;a=b;b=temp;}
    
    int main() {
    	int a[5] = { 3,4,5,6,7 };
    	for (int i = 0; i < 5; i++) {
    		for (int j = 0; j < 5 - i - 1; j++) {
    			if (a[j] < a[j + 1])  
    				SWAP(a[j], a[j + 1]);
    		}
    	}
    
    	for (int i = 0; i < 5; i++) {
    		printf("%d ", a[i]);
    	}
    	printf("\n");
    }

    运行结果:

    2.宏定义参数替换的注意事项

    • 若宏定义中字符串出现运算符,需要在合适的位置加上括号,如果不加括号可能会出现错误。

    #define S 3+4

    a=S*c;//宏定义替换后的语句是a=3+4*c,与期望不符合。

    修改为#define S (3+4)

    则原语句替换为a=(3+4)*c

    • 宏定义的末尾不用加分号,如果加了分号,将会被视为被替换的字符串的一部分。宏定义只是简单的字符串替换,并不进行语法检查,因此宏替换的错误要等到系统编译时才能被发现。
    • 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在替换时由预处理程序嵌套替换。但宏定义不支持递归

    #define PI 3.14159

    #define P PI*x

    double c=2*P;宏替换后的语句为c=2*3.14159*x;

    宏定义不支持递归

    所以#define MAX MAX+5是错误定义方法


    三、带参数的宏定义和带参函数的区别

    基本操作带参数的宏定义带参数的函数
    处理时间预处理期间程序运行时
    参数类型需要定义的类型
    参数传递不分配空间,无值传递的问题(只是字符串替换)分配内存,将实参传入形参
    运行速度相对较慢,因为函数的调用会涉及到参数的传递,压栈和出栈等操作

     

    展开全文
  • 详细罗列出宏定义和函数调用的区别,很全。。。
  • C语言_宏定义

    万次阅读 多人点赞 2019-02-02 00:20:03
    C预处理器在源代码编译之前对其进行一些文本性质的操作,主要任务包括删除注释、插入被#include进来的文件内容、定义和替换由#define 定义的符号以及确定代码部分内容是否根据条件编译(#if )来进行编译。...
  • 取消宏定义

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

    2012-04-05 14:19:19
    有关宏定义的总结,从什么地方copy过来的忘记了。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 420,652
精华内容 168,260
关键字:

宏定义