【问题标题】:So many parentheses in gcc standard headersgcc 标准头文件中有这么多括号
【发布时间】:2015-02-24 03:51:04
【问题描述】:

为什么GCC头文件中的常量表达式用括号括起来,像这样?

#define INTMAX_MIN (-9223372036854775807LL)
#define INTMAX_MAX (9223372036854775807LL)

如果我这样省略括号会有什么区别?

#define INTMAX_MIN -9223372036854775807LL
#define INTMAX_MAX 9223372036854775807LL

为什么会有“L”后缀?如果我写以下内容会不会一样?

#define INTMAX_MIN -9223372036854775807
#define INTMAX_MAX 9223372036854775807

有实际用处还是总是一样的东西?

我知道“L”代表很长,我也很清楚括号在 C 宏中的重要性;我是出于好奇而问这个的。

【问题讨论】:

    标签: c gcc macros


    【解决方案1】:

    如果你写了

    a = 7 INTMAX_MIN;
    

    你会期望得到一个语法错误,因为从表面上看这将是一个非法的表达式。它会的,因为它会扩展到

    a = 7 (-9223372036854775807LL);
    

    这确实给你一个语法错误。但如果没有括号,它会扩展为:

    a = 7 -9223372036854775807LL;
    

    这不会给你一个错误,尽管显然不是你想要的。

    更一般地说,所有这些定义都为看起来像标识符的事物提供了扩展。在算术表达式中,标识符是“主表达式”,但 -9223372036854775807LL 不是。但是,带括号的表达式是“主要表达式”。

    这才是真正的原因。因此,宏将 看起来 的主要表达式扩展为 的主要表达式。你永远不会对发生的事情感到惊讶。在编程中,惊喜通常是不好的。

    通常没关系,但编写定义的人不希望它们通常工作。他们希望他们一直工作。

    尾随的 LL 将此整数文字标记为 long long 类型,通常(在本例中为)64 位。如果没有 LL 后缀,文字可以解释为 int、long int 或 long long int,以最先支持 64 位值的为准。确定类型与确定价值一样重要。

    【讨论】:

    • 您还可以讨论a = 7-INT_MAX;INT_MAX 周围加括号和不加括号,注意结果会与a = 7 - INT_MAX; 不同。
    • @JonathanLeffler 结果会有什么不同? INT_MAX 不需要括号,因为整数文字是主要表达式。
    • @EricMSchmidt:……废话,废话…… ……除非……哦,废话!最初的单独预处理器进程倾向于给出一个答案,但现代(如 1989 年或更高版本)版本对输入进行标记,并且输入保持标记化,因此 #define X -7int main(void) { int x = 7-X; return x; } 产生一个以状态 14 退出的程序。我应该记起来了。
    • “文字将被解释为普通整数”不正确,因为 @mafso cmets。试试printf("%zu\n", sizeof (9223372036854775807));
    • @mafso:从语言的角度来看,这并不完全正确。唯一允许对无后缀常量使用 unsigned 类型的 C 语言标准版本是 C89/90。此功能在 C99 中被禁止。无后缀常量只能有 signed 类型。如果常量不适合最大的有符号类型,则行为未定义。 IE。该语言不支持将unsigned long long 用于无后缀常量。
    【解决方案2】:

    将括号放在具有整数常量和一元运算符的宏上是一种很好的做法,如下所示:

    #define BLA (-1)
    

    作为一元运算符(此处为-)在C 中没有最高优先级。后缀运算符的优先级高于一元运算符。请记住,C 没有负常量,例如 -1 是一个以 - 一元运算符开头的常量表达式。

    顺便说一句,PC-Lint 建议在此类宏中使用括号:

    (规则 973):括号 #define N (-1) 宏“符号”中的一元运算符未加括号 - 一元 发现出现在类似表达式的宏中的运算符没有括号。为了 示例:

    #define N -1
    

    用户可能更喜欢用括号括起来:

    #define N (-1)
    

    【讨论】:

      【解决方案3】:

      此答案试图说明为什么需要LL。 (这并不容易。)
      其他答案已经说明了为什么需要括号。

      我们来编写三个宏,都是十进制常量

      #define MAXILL (9223372036854775807LL)
      #define MAXIL  (9223372036854775807L)
      #define MAXI   (9223372036854775807)
      

      即使MAXI 没有后缀,也不能使其类型为intMAXI 将具有它适合的第一个类型,intlonglong long 或扩展整数类型。

      即使MAXILL 后缀,它也会有它适合的第一个类型,longlong long 或扩展整数类型。

      即使MAXILLLL 后缀,它也会有它适合的第一个类型,long long 或扩展整数类型。

      在每种情况下,宏都具有相同的值,但可能类型不同。

      有关类型确定,请参阅 C11dr §6.4.4.1 5。


      让我们尝试打印它们,intlong 是 32 位的,long longintmax_t 是 64 位。

      printf("I   %d %d %d",       MAXI, MAXIL, MAXILL);    //Error: type mismatch
      printf("LI  %ld %ld %ld",    MAXI, MAXIL, MAXILL);    //Error: type mismatch
      printf("LLI %lld %lld %lld", MAXI, MAXIL, MAXILL);    //Ok
      

      最后一行中的三个都是正确的,因为所有三个宏都是long long,前面的格式说明符和数字之间的类型不匹配。

      如果我们有 int 是 32 位,longlong longintmax_t 是 64,那么以下是正确的。

      printf("LI  %ld %ld",    MAXI, MAXIL);
      printf("LLI %lld", MAXILL);
      

      最大宽度整数类型的格式说明符为"%" PRIdMAX。此宏无法扩展为"%ld" "%lld" 以容纳MAXI, MAXIL, MAXILL。它设置为"%lld",并且与intmax_t 相关的数字需要具有相同的类型。在这种情况下,应该使用long long 并且只能使用MAXILL 形式。


      其他实现可能具有扩展的整数类型(如int128_t),在这种情况下,可以使用实现特定的后缀或某种机制。

      【讨论】:

      • 请注意,使用 C11 _Generic(之前使用流行的 typeof 扩展)类型变得更容易观察到。 (在没有扩展的 C99 中,如果 longlong long 具有相同的大小和表示形式,我不知道如何观察它们之间的差异;假设实现对调用约定做出假设是可以的,并且它是longlong long 的传递方式不太可能不同,严格的 C99 是否真的需要后缀的问题仍然存在。)
      猜你喜欢
      • 2017-11-26
      • 1970-01-01
      • 1970-01-01
      • 2018-06-06
      • 2012-05-04
      • 1970-01-01
      • 2016-02-17
      • 1970-01-01
      • 2018-02-02
      相关资源
      最近更新 更多