为什么这个宏被替换为 20 而不是 10?
Why is this macro replaced as 20 instead 10?
1. #define NUM 10
2. #define FOO NUM
3. #undef NUM
4. #define NUM 20
5.
6. FOO
当我只运行预处理器时,输出文件包含20个。
但是,据我了解,预处理器只是进行文本替换。所以这就是我认为正在发生的事情(这显然是错误的但很愚蠢):
- NUM 定义为 10。
- 因此,在第 2 行中,NUM 被替换为 10。所以现在我们有了“#define FOO 10”。
- NUM 未定义。
- NUM 被重新定义,现在是 20。
- FOO根据第4行重定义之前的第2行替换为10
所以我认为输出应该是 10 而不是 20。有什么可以解释哪里出了问题吗?
文本替换是在使用宏的地方完成的,而不是在您编写 #define
的地方完成的。在您使用 FOO
时,它会将 FOO
替换为 NUM
,并且 NUM
当前定义为 20
。
在:
FOO
预处理器会将其替换为 NUM
,然后它将 NUM
替换为当前定义的内容,即 20
.
前四行相当于:
#define FOO NUM
#define NUM 20
C11 标准说(以及其他版本的 C 和 C++,类似说):
A preprocessing directive of the form # define identifier replacement-list new-line
defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive. The replacement list is then rescanned for more macro names as specified below.
不过另一部分也说了(感谢rici指出这一点)。
The preprocessing tokens within a preprocessing directive are not subject to macro expansion unless otherwise stated.
因此,在另一个 #define
指令 中找到的宏名称 的后续实例实际上 未被 替换。
您的行 #define FOO NUM
定义当稍后找到标记 FOO
时(在另一个 #define
指令之外!),它将被标记 NUM
替换.
标记被替换后,重新扫描,如果NUM
本身是一个宏,那么NUM
会在那个点被替换。 (如果 NUM
扩展为包含宏,那么它会被扩展,依此类推)。
所以你的步骤顺序实际上是:
NUM
定义为 10
FOO
定义为 NUM
NUM
未定义并重新定义为 20
FOO
扩展为 NUM
- (重新扫描)
NUM
扩展为 20
此行为可以在另一个常见的预处理器技巧中看到,将宏的定义值转换为字符串:
#define STR(X) #X
#define STR_MACRO(X) STR(X)
#define NUM 10
puts( STR_MACRO(NUM) ); // output: 10
如果我们写 puts( STR(NUM) )
那么输出将是 NUM
.
10
的输出是可能的,因为和以前一样,这里的第二个 #define
实际上并没有展开 STR
。所以这段代码中的步骤顺序是:
STR(X)
定义为 #X
STR_MACRO(X)
定义为 STR(X)
NUM
定义为 10
STR_MACRO
和 NUM
都展开了;结果是 puts( STR(10) );
- (上次展开的重新扫描结果)
STR(10)
展开为"10"
- (上次扩展的重新扫描结果)无法进一步扩展。
为了从标准中收集所有相关规范,我从评论线程中提取了这些信息,并根据 N4527 草案(两个标准中的规范文本相同)添加了 C++ 部分编号。标准在这个问题上是绝对清楚的。
#define
预处理器指令不进行宏替换。
(C11 §6.10¶7; C++ §16[cpp] ¶6): The preprocessing tokens within a preprocessing directive are not subject to macro expansion unless otherwise stated.
宏被其替换文本替换后,将重新扫描新文本。如果程序中该点的标记有活动的宏定义,则替换中的预处理器标记将扩展为宏。
(C11 §6.10.3¶9; C++ §16.3[cpp.replace] ¶9) A preprocessing directive of the form
<b># define</b> <i>identifier replacement-list new-line</i>
defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive. The replacement list is then rescanned for more macro names as specified below.
宏定义从 #define
之后的行开始激活,直到宏名称的 #undef
或文件末尾。
(C11 §6.10.3.5¶1; C++ §16.3.5[cpp.scope] ¶1) A macro definition lasts (independent of block structure) until a corresponding #undef
directive is encountered or (if none is encountered) until the end of the preprocessing translation unit. Macro definitions have no significance after translation phase 4.
如果我们看程序:
#define NUM 10
#define FOO NUM
#undef NUM
#define NUM 20
FOO
我们看到第 1 行中 NUM
的宏定义恰好持续到第 3 行。这些行中没有可替换的文本,因此从未使用过该定义;因此,该程序实际上与:
#define FOO NUM
#define NUM 20
FOO
在此程序中,在第三行,有一个活动定义 FOO
,替换列表 NUM
,NUM
,替换列表 20
. FOO
被它的替换列表替换,成为 NUM
,然后再次扫描它的宏,导致 NUM
被它的替换列表 20 替换。那个替换又是重新扫描,但没有定义宏,所以最终结果是令牌 20
留给翻译阶段 5 处理。
1. #define NUM 10
2. #define FOO NUM
3. #undef NUM
4. #define NUM 20
5.
6. FOO
当我只运行预处理器时,输出文件包含20个。
但是,据我了解,预处理器只是进行文本替换。所以这就是我认为正在发生的事情(这显然是错误的但很愚蠢):
- NUM 定义为 10。
- 因此,在第 2 行中,NUM 被替换为 10。所以现在我们有了“#define FOO 10”。
- NUM 未定义。
- NUM 被重新定义,现在是 20。
- FOO根据第4行重定义之前的第2行替换为10
所以我认为输出应该是 10 而不是 20。有什么可以解释哪里出了问题吗?
文本替换是在使用宏的地方完成的,而不是在您编写 #define
的地方完成的。在您使用 FOO
时,它会将 FOO
替换为 NUM
,并且 NUM
当前定义为 20
。
在:
FOO
预处理器会将其替换为 NUM
,然后它将 NUM
替换为当前定义的内容,即 20
.
前四行相当于:
#define FOO NUM
#define NUM 20
C11 标准说(以及其他版本的 C 和 C++,类似说):
A preprocessing directive of the form
# define identifier replacement-list new-line
defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive. The replacement list is then rescanned for more macro names as specified below.
不过另一部分也说了(感谢rici指出这一点)。
The preprocessing tokens within a preprocessing directive are not subject to macro expansion unless otherwise stated.
因此,在另一个 #define
指令 中找到的宏名称 的后续实例实际上 未被 替换。
您的行 #define FOO NUM
定义当稍后找到标记 FOO
时(在另一个 #define
指令之外!),它将被标记 NUM
替换.
标记被替换后,重新扫描,如果NUM
本身是一个宏,那么NUM
会在那个点被替换。 (如果 NUM
扩展为包含宏,那么它会被扩展,依此类推)。
所以你的步骤顺序实际上是:
NUM
定义为10
FOO
定义为NUM
NUM
未定义并重新定义为20
FOO
扩展为NUM
- (重新扫描)
NUM
扩展为20
此行为可以在另一个常见的预处理器技巧中看到,将宏的定义值转换为字符串:
#define STR(X) #X
#define STR_MACRO(X) STR(X)
#define NUM 10
puts( STR_MACRO(NUM) ); // output: 10
如果我们写 puts( STR(NUM) )
那么输出将是 NUM
.
10
的输出是可能的,因为和以前一样,这里的第二个 #define
实际上并没有展开 STR
。所以这段代码中的步骤顺序是:
STR(X)
定义为#X
STR_MACRO(X)
定义为STR(X)
NUM
定义为10
STR_MACRO
和NUM
都展开了;结果是puts( STR(10) );
- (上次展开的重新扫描结果)
STR(10)
展开为"10"
- (上次扩展的重新扫描结果)无法进一步扩展。
为了从标准中收集所有相关规范,我从评论线程中提取了这些信息,并根据 N4527 草案(两个标准中的规范文本相同)添加了 C++ 部分编号。标准在这个问题上是绝对清楚的。
#define
预处理器指令不进行宏替换。(C11 §6.10¶7; C++ §16[cpp] ¶6): The preprocessing tokens within a preprocessing directive are not subject to macro expansion unless otherwise stated.
宏被其替换文本替换后,将重新扫描新文本。如果程序中该点的标记有活动的宏定义,则替换中的预处理器标记将扩展为宏。
(C11 §6.10.3¶9; C++ §16.3[cpp.replace] ¶9) A preprocessing directive of the form
<b># define</b> <i>identifier replacement-list new-line</i>
defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive. The replacement list is then rescanned for more macro names as specified below.
宏定义从
#define
之后的行开始激活,直到宏名称的#undef
或文件末尾。(C11 §6.10.3.5¶1; C++ §16.3.5[cpp.scope] ¶1) A macro definition lasts (independent of block structure) until a corresponding
#undef
directive is encountered or (if none is encountered) until the end of the preprocessing translation unit. Macro definitions have no significance after translation phase 4.
如果我们看程序:
#define NUM 10
#define FOO NUM
#undef NUM
#define NUM 20
FOO
我们看到第 1 行中 NUM
的宏定义恰好持续到第 3 行。这些行中没有可替换的文本,因此从未使用过该定义;因此,该程序实际上与:
#define FOO NUM
#define NUM 20
FOO
在此程序中,在第三行,有一个活动定义 FOO
,替换列表 NUM
,NUM
,替换列表 20
. FOO
被它的替换列表替换,成为 NUM
,然后再次扫描它的宏,导致 NUM
被它的替换列表 20 替换。那个替换又是重新扫描,但没有定义宏,所以最终结果是令牌 20
留给翻译阶段 5 处理。