【问题标题】:The output of this C code is 49 but can someone explain me how? [duplicate]此 C 代码的输出是 49,但有人可以解释一下吗? [复制]
【发布时间】:2021-01-20 07:26:13
【问题描述】:
#include <stdio.h>

#define CUBE(x) (x * x * x)

int main() {
    printf("%d", CUBE(4+5));
    return 0;
}

【问题讨论】:

标签: c c-preprocessor


【解决方案1】:

这是宏在编译过程中的扩展方式:

printf("%d", (4+5 * 4+5 * 4+5));

因为* 的优先级高于+,所以这个表达式被评估为(4 + (5 * 4) + (5 * 4) + 5),产生49 而不是预期的729

为避免此类运算符优先级问题,所有宏参数必须在宏定义以及表达式本身中用括号括起来:

#define CUBE(x)  ((x) * (x) * (x))

但是请注意,CUBE 的这种扩展会多次计算 x,如果宏参数有副作用,例如 CUBE(i++),这是一个问题。

为避免所有这些问题,请使用函数并让编译器对其进行优化:

int cube(int x) {
    return x * x * x;
}

您可以在此函数定义前添加static inline,但现代优化器仍会在没有此的情况下内联该函数。

【讨论】:

    【解决方案2】:

    宏会将代码转为:

    printf("%d", (4+5 * 4+5 * 4+5));
    

    实际上是:

    printf("%d", 4 + (5*4) + (5*4) + 5); // 29
    

    如果你想用立方9 得到729,你应该写CUBE((4+5))

    【讨论】:

    • 或者停止使用类似函数的宏。在当今优化编译器的世界里,只要把它写成一个函数,如果好处足够多,编译器就会内联它(你甚至可以用inline向编译器建议)。
    • Re“如果你想要 729,你应该写 CUBE((4+5))”:不,宏应该用括号写,这样它的用户就不必提供它们。
    • @Eric,涵盖所有种情况(例如CUBE(x++))的宏魔法数量使类似函数的宏成为一个非常糟糕的主意。最好放弃它们,对简单的定义使用宏,对其他所有内容使用内联建议的函数。
    • @paxdiablo:这与我的评论无关。此外,即使另一个宏从未用新代码编写过,C 程序员也必须了解宏是如何工作的。建议人们使用内联函数来代替答案中的评论或补充信息是可以的,但不会回答所提出的问题。
    • 更常见的是,这是在宏本身中处理的:#define CUBE(x) ((x)*(x)*(x))
    【解决方案3】:

    C 预处理器会将 x 的所有实例替换为 4+5,从而产生以下代码:

    i = 4+5*4+5*4+5;
    

    (先乘法,后加法)

    【讨论】:

      【解决方案4】:

      如果要添加数字,则需要输入CUBE((4+5)) 之类的输入,然后发送至CUBE。因为CUBE(4+5) 基本上扩展为4+5*4+5*4+5,因为它将整个4+5 设置为x。所以,4+5*4+5*4+5 = 4+20+20+5 首先是乘法,然后相加得到49

      定义像#define CUBE(x) ((x)*(x)*(x)) 这样的宏,它实际上首先对每个(x) 执行(4+5) 操作,然后执行* 操作。

      另一种方法是在调用宏时只使用CUBE((4+5)),它基本上是先将两个数字相加(4+5) = 9,然后再做CUBE(9),例如:

      #include<stdio.h>
      #define CUBE(x) (x * x * x)
      
      int main( )
      {
          printf("%d", CUBE((4+5)));
          return 0;
      }
      

      【讨论】:

        【解决方案5】:

        阅读Modern C,然后阅读n1570 草案C 标准,以及GCCGDBCPP 的文档。另请参阅this C reference 网站。

        从现有的开源项目中汲取灵感,例如在github,或GNU software

        使用最近的GCC 编译器,invoke itgcc -Wall -Wextra -g。也可以使用-C -E 选项来获取预处理的表单。

        您也可能对 GCC 的 statement-expr 扩展感兴趣。

        您的CUBE(4+5) 被宏扩展为4+5*4+5*4+5,根据C operators 的优先级计算为4+(5*4)+(5*4)+5

        考虑编写一个 static inline 之类的函数

        static inline int cube(int x) { return x*x*x; }
        

        或者如果你需要一个宏,至少

        #define CUBE(X) ((X)*(X)*(X))
        

        这不适用于 CUBE(i++)(而使用 inline 函数时,cube(i++) 会执行您希望它执行的操作:将 i 递增一次!)。

        顺便说一句,您可以使用GPPGNU m4(或您自己的生成器,或GNU bison)来生成一些C 代码。根据经验,用ASTs 思考:当你生成 C 代码时,会发出很多无用的括号,例如在Chicken SchemeSWIGCAIA 或我的manydl.c

        您也可以在代码中使用Clang static analyzer(也可以使用Frama-C)。

        您可能有兴趣阅读一些编码指南,例如 MISRA CGNU ones

        【讨论】:

          猜你喜欢
          • 2020-06-03
          • 2020-05-29
          • 1970-01-01
          • 1970-01-01
          • 2021-10-04
          • 1970-01-01
          • 1970-01-01
          • 2014-11-21
          • 1970-01-01
          相关资源
          最近更新 更多