• C陷阱与缺陷 第6章 预处理器 6.2 宏并不是函数


    宏并不是函数 
    #define abs(x) (((x) >= 0) ? (x) : -(x))
    #define max(a, b) ((a) > (b) ? (a) : (b))
    宏定义中的出现的所有这些括号,它们的作用是预防引起与优先级有关的问题。 
    #define abs(x) x > 0 ? x : -x
    abs(a - b)展开的结果为: 
    a - b > 0 ? a - b : -a - b
    这里的子表示-a - b相当于(-a)-b,而不是我们期望的-(a-b),因此会得到一个错误结果。因此,我们最好在宏定义中把每个参数都用括号括起来。同时,这个结果表达式也应该用括号括起来,以防止当宏作用于一个更大一些的表达式时可能引起的问题。 
    abs(a) + 1展开的结果为: 
    a > 0 ? a : -a + 1
    这个表达式显示是错误的。abs正确的定义应该是这样的: 
    #define abs(x) (((x) >= 0) ? (x) : -(x))
    abs(a - b)正确展开为: 
    (((a - b) >= 0) ? (a - b) : -(a - b))
    abs(a) + 1正确展开为: 
    (((a) >= 0) : (a) : -(a)) + 1
    即使宏定义中的各个参数与整个结果表达式都被括号括起来,也仍然可能存在其他问题,比如,一个操作数如果在两处被用到,就会被求值两次。例如,在表达式中max(a, b)中,如果a大于b,那么a将被求值两次---第一次是在与a与b比较期间,第二次是在计算max应该得到的结果值时:这种做法不但效率低下,而且可能是错误的: 
    biggest = x[0];
    i = 1;
    while (i < n) {
        biggest = max(biggeset, x[i++]);
    }
    max是函数,代码能正常工作;如果max是一个宏,那么就不能正常工作。 
    x[0] = 2;
    x[1] = 3;
    x[2] = 1;
    bigggist = ((biggest) > (x[i++]) ? (biggest) : (x[i++]));
    首先,变量biggest将与x[i++]比较。计算后i的值会加1,就不是原来的值了。
    解决这类问题的一个办法是,确保宏max中的参数没有副作用: 
    biggest = x[0];
    for (i = 1; i < n; i++) {
        biggest = max(biggest, x[i]);
    }
    另一个办法是让max作为函数而不是宏,或者直接编写用来比较两数且取较大者的运算代码: 
    biggest = x[0];
    for (i = 1; i < n; i++) {
        if (x[i] > biggest) {
            biggest = x[i];
        }
    }
    下面这个宏混合了宏和递增运算的副作用,代码显得岌岌可危。 
    #define putc(x, p) (--(p)->_cnt >= 0 ? (*(p)->_ptr++ = (x)) : _flsbuf(x, p))
    x是将要写入文件的字符,p是一个指针,指向一个用于描述文件的内部数据结构。请注意这里的x
    ,它极有可能是类似于*z++这样的表达式。景观x在宏putc的定义中两个不同的地方出现了两次,
    但是因为这两次出现的地方是在运算符:的两侧,所以x只会被求值一次。
    第二个参数p则恰恰相反,它代表将要写入字符的文件,总是会被求值两次。因为文件参数p一般
    不需要作递增递减之类有副作用的操作,所以这很少引起麻烦。不过,ANSI C标准还是提出了警告:putc的第二个函数可能会求值两次。 

    将所有小写字母转换为相应的大写字母,而其他的字符则保持原状。 
    int toupper(int c) {
        if (c >= 'a' && c <= 'z') {
            c += 'A' - 'a'; 
        }
        return c;
    }
    在大多数C语言实现中,toupper函数在调用是造成的系统开销要远远大于函数体的实际计算操作。 
    #define toupper(c) \
    ((c) >= 'a' && (c) <= 'z' ? (c) + ('A'-'a') : (c))
    toupper(*p++)
    最后的结果是不正确的。 
    使用宏的另一个危险是,宏展开可能产生非常庞大的表达式,占用的空间会远远超过编程人员所
    期望的空间。例如: 
    #define max(a, b) ((a) > (b) ? (a) : (b));
    max(a, max(b, max(c, d)))
    max(max(a, b), max(c, d))

    biggest = a;
    if (biggest < b) biggest = b;
    if (biggest < c) biggest = c;
    if (biggest < d) biggest = d;
     

  • 相关阅读:
    基于条件谱矩的时间序列分析(以轴承故障诊断为例,MATLAB)
    Keras深度学习实战(35)——构建机器翻译模型
    SI3262—高度集成的低功耗SOC读卡器芯片
    #分支语句详解
    python opencv图像模糊
    JS防抖和节流
    什么是实时流引擎?
    文心一言:中文生成式对话产品
    python离线环境下安装第三方模块的方法
    java之《浅入了解异常》适合预习,复习
  • 原文地址:https://blog.csdn.net/weixin_40186813/article/details/126045171