宏观扩张的奇怪结果

问题描述 投票:3回答:4

请考虑以下代码段

#include<stdio.h>
#define A -B
#define B -C
#define C 5

int main()
{
  printf("The value of A is %d\n", A);
  return 0;
}

Output

The value of A is 5

但这根本不应该编译,因为在扩展后它应该看起来像printf("The value of A is %d\n", --5);然后它应该给编译错误说lvalue需要。不是吗?

c macros
4个回答
4
投票

传递-E选项(例如:gcc -E a.c)。这将输出预处理的源代码。

int main()
 {
    printf("The value of A is %d\n", - -5);
    return 0;
 }

所以它将在--5之间引入一个空间,因此它不会被视为减量运算符--,所以printf将打印5。

关于Token Spacing的GCC文档提供了有关为何产生额外空间的信息:

首先,考虑一个仅涉及独立预处理器的问题:需要保证重新读取其预处理输出会产生相同的令牌流。如果不采取特殊措施,由于宏观替代,情况可能并非如此。例如:

 #define PLUS +
 #define EMPTY
 #define f(x) =x=
 +PLUS -EMPTY- PLUS+ f(=)
         ==> + + - - + + = = =
 not
         ==> ++ -- ++ ===

一种解决方案是简单地在所有相邻令牌之间插入空格。但是,我们希望将空间插入保持在最低限度,这既是出于美学原因,又是因为它仍然会导致仍然试图滥用Fortran源和Makefile等预处理器的人员出现问题。

现在,请注意,当从原始lexed标记流添加(或删除,如EMPTY示例所示)标记时,我们需要检查意外标记粘贴。我们称之为粘贴避免。令牌添加和删除只能由于宏扩展而发生,但在许多地方可能会发生意外粘贴:每次宏替换之前和之后,每个参数替换,以及###运算符创建的每个标记。


4
投票

我不这么认为。即使宏扩展是文本处理,也不可能跨宏边界创建令牌。因此它作为-(-5),而不是--5,因为--是一个单一的标记。


4
投票

预处理器在BC的扩展之间引入了一个空间:

#define A -B
#define B -C
#define C 5
A

输出(通过cpp < test.c生成)

# 1 "test.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "test.c" 2



- -5

2
投票

在C语言中,在宏转换发生之前(阶段4),程序源代码在转换的早期阶段(阶段3)被分成所谓的预处理令牌。稍后(在阶段7),预处理令牌被转换为常规令牌,这些令牌被馈送到编译器本身的语法和语义分析器中(参见语言规范中的“5.1.1.2翻译阶段”)。

阶段3是形成未来C语言运算符和其他词汇元素的预处理标记的阶段(标识符,数字,标点符号,字符串文字等)。在早期阶段形成了像-->>=等多字符标点符号。为了最终在第7阶段获得--算子的令牌,你需要尽早将--作为第3阶段的完整标点符号。在第7阶段从预处理标记转换为常规标记时,不会发生额外的标点符号连接,这意味着相邻的两个标记符号在第3阶段检测到的-标点符号将不会在第7阶段成为单个标记--。编译器本身将永远不会有机会看到这两个相邻的-和单个标记--

换句话说,在C中,您不能使用预处理器通过将它们放在一起来连接它们。这就是为什么预处理器具有##等专用功能以便于连接的原因。而##是你必须用来将两个令牌连接成一个令牌的东西。

顺便说一句,通过声称预处理器将在你的-字符之间放置一个空格字符来解释这种行为是不正确的。语言规范中没有类似的东西。真正发生的是,在编译器的内部结构中,你的-标记永远保持为两个独立的标记。预处理器和编译器如何实现这是它们的内部实现细节。在具有松散耦合的预处理器和编译器本身的实现中(例如,通过中间文本表示进行通信的完全独立的模块),在相邻的标点符号之间注入空间绝对是实现所需的令牌分离的自然方式。

© www.soinside.com 2019 - 2024. All rights reserved.