C预处理程序在多大程度上考虑整数文字后缀?

Mic*_*chi 11 c c++ integer c-preprocessor suffix

今天,我偶然发现了这样的情况:

#define FOO 2u

#if (FOO == 2)
  unsigned int foo = FOO;
#endif
Run Code Online (Sandbox Code Playgroud)

不管代码为何如此(不要质疑why),我都想知道预处理器可以在多大程度上处理整数文字后缀。实际上,我感到惊讶的是它完全有效。在使用此代码对GCC和C99进行了一些实验之后,...

#include <stdio.h>

int main()
{
  #if (1u == 1)
    printf("1u == 1\n");
  #endif

  #if (1u + 1l == 2ll)
    printf("1u + 1l == 2ll\n");
  #endif

  #if (1ull - 2u == -1)
    printf("1ull - 2u == -1\n");
  #endif

  #if (1u - 2u == 0xFFFFFFFFFFFFFFFF)
    printf("1u - 2u == 0xFFFFFFFFFFFFFFFF\n");
  #endif

  #if (-1 == 0xFFFFFFFFFFFFFFFF)
    printf("-1 == 0xFFFFFFFFFFFFFFFF\n");
  #endif

  #if (-1l == 0xFFFFFFFFFFFFFFFF)
    printf("-1l == 0xFFFFFFFFFFFFFFFF\n");
  #endif

  #if (-1ll == 0xFFFFFFFFFFFFFFFF)
    printf("-1ll == 0xFFFFFFFFFFFFFFFF\n");
  #endif
}
Run Code Online (Sandbox Code Playgroud)

...仅打印所有语句:

1u == 1
1u + 1l == 2ll
1ull - 2u == -1
1u - 2u == 0xFFFFFFFFFFFFFFFF
-1 == 0xFFFFFFFFFFFFFFFF
-1l == 0xFFFFFFFFFFFFFFFF
-1ll == 0xFFFFFFFFFFFFFFFF
Run Code Online (Sandbox Code Playgroud)

...我想预处理器会完全忽略整数文字后缀,并且可能总是对本机整数大小进行算术和比较,在这种情况下为64位?

所以,这是我想知道的东西:

  1. 预处理程序在多大程度上考虑整数文字后缀?还是只是忽略它们?
  2. 在不同的环境中是否存在任何依赖关系或不同的行为,例如不同的编译器,C与C ++,32位与64位计算机等?即,预处理器的行为取决于什么?
  3. 所有指定/记录在哪里?

我想自己找出并检查 Wikipedia和C标准(工作论文)。我找到了有关整数后缀的信息和有关预处理器的信息,但没有找到有关这些组合的信息。显然,我也用谷歌搜索,但没有得到任何有用的结果。

我已经看到了这个Stack Overflow问题,该问题阐明了应在何处指定它,但是,我找不到我的问题的答案。

Jon*_*ler 6

正如我在评论中指出的那样,这是在C标准中定义的。这是§6.10.1¶4的全文(和两个脚注):

C11 §6.10.1条件包含 ...

¶4在评估之前,将替换将成为控制常量表达式的预处理令牌列表中的宏调用(除了由defined一元运算符修改的那些宏名称之外),就像在普通文本中一样。如果由于此替换过程而生成了定义的令牌,或者defined在宏替换之前使用一元运算符与两种指定格式之一都不匹配,则该行为未定义。由于宏观扩张和defined一元运算符已执行,所有剩余的标识符(包括与关键字在词法上相同的标识符)都替换为pp-number 0,然后将每个预处理令牌转换为令牌。生成的标记组成控制常数表达式,该表达式根据6.6的规则求值。出于此令牌转换和评估的目的,所有有符号整数类型和所有无符号整数类型的行为就像它们分别具有与header中定义的type intmax_t和相同的表示一样。167)uintmax_t<stdint.h>这包括解释字符常量,这可能涉及将转义序列转换为执行字符集成员。这些字符常量的数值是否与在表达式中(而不是在#if或#elif指令中)出现相同的字符常量时获得的值相匹配,由实现定义。168)此外,单字符字符常量是否可以具有负值是实现定义的。

167 167)因此,在INT_MAXis 0x7FFF和UINT_MAXis 的实现中0xFFFF,即使表达式在翻译阶段7中是无符号的,该常量0x8000在#if表达式中也是带正负号的。

168因此,不能保证以下#if指令和if语句中的常量表达式在这两种情况下得出的值相同。

#if 'z' - 'a' == 25
if ('z' - 'a' == 25)
Run Code Online (Sandbox Code Playgroud)

第6.6节是6.6节常量表达式,其中详细介绍了第6.5节表达式中的完整表达式与常量表达式之间的区别。

实际上,预处理器在很大程度上忽略了后缀。十六进制常量是无符号的。您显示的结果是一台机器,其中上可以预料的intmax_t和uintmax_t是64位的。如果在限制intmax_t和uintmax_t为大,一些表情可能会改变。

  • 预处理器不能忽略后缀。它可能会忽略它们的宽度影响,但不会忽略符号性。 (2认同)

Eri*_*hil 5

C 2018 6.10.1处理条件包含(#if以及相关语句和defined运算符)。第1段说:

控制条件包含的表达式应为整数常量表达式,不同之处在于:标识符(包括与关键字在词法上相同的标识符)的解释如下;它可能包含以下形式的一元运算符表达式

defined 识别码

要么

defined ( 标识符 ) ...

整数常量表达式在6.6 6中定义:

一个整数常量表达式应具有整数型和应仅具有是积分常数,枚举常数,字符常数,操作数sizeof表达式,其结果是积分常数,_Alignof表达式和浮动是铸件的立即操作数的常量。整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,除非作为sizeofor _Alignof运算符的一部分。

该段通常用于C,而不仅仅是预处理器。使得可以出现在表达式#if语句中的相同,可以在C.一般出现然而整数常量表达式,如在上面的报价说明,sizeof并且_Alignof只是标识符; 它们不被视为C运算符。6.10.1 4特别告诉我们:

…由于宏扩展和defined一元运算符而执行了所有替换之后,所有剩余的标识符(包括与关键字在词法上相同的标识符)都将替换为pp-number 0,…

因此,在表达式中出现sizeof或变为。因此,表达式只能具有为常量和表达式的操作数。_Alignof#if0#ifdefined

第4段继续说:

…结果令牌组成控制常数表达式,该表达式根据6.6的规则求值。为了此令牌转换和评估的目的,所有有符号整数类型和所有无符号整数类型的行为就好像它们分别具有与标头中定义的类型intmax_t和类型相同的表示形式一样。…uintmax_t<stdint.h>

6.6是常量表达式的部分。

因此,编译器将在#if表达式中接受整数后缀,并且该后缀不依赖于C实现(对于核心C语言所需的后缀;实现可以允许扩展)。但是,所有算法都将使用intmax_t或来执行uintmax_t,而这些算法确实取决于实现方式。如果您的表达式不依赖于大于所需的最小值1的整数的宽度,则在任何C实现中都应将它们评估为相同。

另外,第4段继续说字符常量和值可能会有一些变化,在此我将其省略,因为它与该问题无关。

脚注

1 intmax_t表示能够表示任何带符号整数类型(7.20.1.5 1)的任何值的带符号类型,并且long long int该符号类型必须至少为64位(5.2.4.2.1 1),因此任何符合标准的C实现都必须提供预处理器中的64位整数算术。


Joh*_*ger 5

  1. 预处理器在多大程度上考虑整数文字后缀?或者它只是忽略它们?

整型常量的类型后缀对预处理器本身没有意义,但它们是相应预处理标记的固有部分,而不是单独的。标准对他们有这样的说法:

预处理编号以数字开头,可选地以句点 (.) 开头,后面可以跟有效的标识符字符和字符序列 e+、e-、E+、E-、p+、p-、P+ 或 P-。

在词法上预处理数字标记包括所有浮点和整数常量标记。

( C11 6.4.8/2-3 ; 强调)

在大多数情况下,预处理器不会将这种类型的预处理标记与其他任何标记区别对待。#if指令的控制表达式是个例外,它是通过执行宏扩展,将标识符替换为 0,然后在根据 C 规则评估结果之前将每个预处理标记转换为标记来评估的。将类型后缀转换为令牌帐户,产生真正的整数常量。

但是,这不一定会产生与您从相同表达式的运行时评估中获得的结果相同的结果,因为

出于此标记转换和评估的目的,所有有符号整数类型和所有无符号整数类型的行为就好像它们分别与类型intmax_t和具有相同的表示形式uintmax_t。

( C2011, 6.10.1/4 )

你继续问

  1. 在不同的环境中是否存在任何依赖关系或不同的行为,例如不同的编译器、C 与 C++、32 位与 64 位机器等?即,预处理器的行为取决于什么?

唯一的直接依赖是实现对intmax_t和的定义uintmax_t。这些都不是直接关系到语言选择或机器架构,虽然可能有相关性与那些。

  1. 所有指定/记录的内容在哪里?

当然,在各自语言的语言规范中。我已经引用了 C11 规范中两个更相关的部分,并将您链接到该标准的最新草案。(当前的 C 是 C18,但在这些方面都没有改变。)

  • 整数常量的后缀对预处理器有意义,因为它们影响符号性。`-1u &lt; 0` 计算结果为 false,但 `-1l &lt; 0` 计算结果为 true。 (2认同)