zoukankan      html  css  js  c++  java
  • C语言宏

      本文主要讲解《C陷阱与缺陷》第六章中宏的使用。

      了解宏,我们首先得了解编译器编译C语言程序的过程:

    C语言编译过程

    其中预处理器工作有:

    (1) 文件包含:可以把源程序中的#include 扩展为文件正文,即把包含的.h文件找到并展开到#include 所在处。
    (2) 条件编译:预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外。
    (3) 宏展开:预处理器将源程序文件中出现的对宏的引用展开成相应的宏定义。

    预处理中宏的作用:
    (1)方便程序的修正:将某个特定数量在程序中出现的所有实例统统加以修改;

    (2)提高程序的运行效率:C语言在实现函数调用时会带来重大系统开销,宏可以实现一种这样的程序块:它看上去像函数,但却没有函数调用的开销。

    宏有有两种定义格式:
    (1) 简单的宏定义:#define <宏名> <字符串>                                  //对应宏作用1

    #define PI 3.1415926

    #define PI 3.14
    (2) 带参数的宏定义:#define <宏名> (<参数表>) <宏体>                //对应宏作用2

    #define f(x) x

    宏应该注意的地方:

    (1)不能忽视宏定义中的空格:

      例子:下面宏定义中 f 是否带了一个参数?

    #define f (x) ((x)-1)

     答案两种:f(x) = ((x) - 1) 或者 f=(x)  ((x)-1)

    在上述定义中f = (x)  ((x) - 1)是正确的(虽然无法使用),因为 f 和后面的(x)之间多了一个空格。f (3) = 2;

    因此在定义宏的时候,要严格的注重空格的使用,一般来讲带参数的宏,其参数列表与宏名要相连。

    (很有趣:定义#define f(x) ((x) - 1), 调用f(3) = f (3) = 2)

    (2)宏不是函数,需注重括号的使用:

    例子:

    #define abs(x) x > 0 ? x : -x //正确使用:#define abs(x) (((x)>0) ? (x) : (-x))

    对abs(a-b)求值,宏被展开为:a-b >0 ? a-b : -a-b

    例子中的-a-b相当于(-a)-b,而不是我们期望的-(a-b);因此,在定义宏的时候我们最好把每个参数都用括号括起来,整个表达式也括起来。

    (3)宏不是类型定义:

    看下面代码:

    #define NEWTYPE struct newType
    NEWTYPE a;
    NEWTYPE b,c;

    我们只需要改动一行代码,就可以改变a,b,c的类型,宏的这种用法有一个有点:可移植性,得到了所有C编译器的支持,但是,我们最好还是用typedef来定义:

    typedef struct newType NEWTYPE

    因为使用宏会带来意想不到的错误,例如:

    #define T1 struct foo*
    typedef struct foo *T2;

    从定义上看,T1和T2的概念上完全符同,都是指向了foo的指针。但是,当我们试图使用它们来声明多个变量时,就会出现问题:

    T1 a, b; //宏扩展:struct foo * a, b;
    T2 a, b; //typedef定义:struct foo *a, *b;

    可以看见使用宏扩展并不能得到我们想要的东西。

    所以,使用类型定义的时候,我们最好不要使用宏,应该使用typedef。

    对于宏的讲解结束,让我们回过头看看 宏应该注意的地方(1)

    #define f (x) ((x)-1)

    对于表达式 (x) ((x) -1) 是否能够成为一个合法的C表达式呢?

    给出一种合理解:

    我们定义:typedef int x;

    在这种情况下:(x) ((x) - 1) 等价于 (int) ((int) - 1)

    即将-1转换为int类型两次!

  • 相关阅读:
    luogu P3657 (NOIP2017) 跳房子(二分+DP+单调队列)
    BZOJ 3331 (Tarjan缩点+树上差分)
    Libre OJ 2255 (线段树优化建图+Tarjan缩点+DP)
    LibreOJ 6177 题解(状压DP)
    BZOJ 1179 (Tarjan缩点+DP)
    BZOJ 4919 (树上LIS+启发式合并)
    BZOJ 1100 &&luogu 3454(计算几何+KMP)
    HDU 3228 题解(最小生成树)(Kruskal)(内有详细注释)
    Codeforces 1058C(思维+最大公因数)
    周记 2015.05.30
  • 原文地址:https://www.cnblogs.com/liuxiaoming/p/2645107.html
Copyright © 2011-2022 走看看