20

我一直在查看代码高尔夫,并想尝试一下这段代码:

#define D #define添加此行后,一切正常,但是我将其扩展为:

#define D #define
D VALUE

在这里我得到了 5 个编译错误。如果我改变D一切#define都很好,有人可以解释一下,为什么这个代码是非法的?

注意: 我使用了 VS2008 编译器。

编辑: 在一些答案之后,我发现我需要提供编译错误列表:

  1. 错误 C2121:“#”:无效字符:可能是宏扩展的结果
  2. 错误 C2146:语法错误:缺少“;” 在标识符“VALUE”之前
  3. 错误 C4430:缺少类型说明符 - 假定为 int。注意:C++ 不支持默认整数
  4. 错误 C2144: 语法错误: 'void' 应该以 ';' 开头
  5. 错误 C4430:缺少类型说明符 - 假定为 int。注意:C++ 不支持默认整数

第一个错误表明,D不仅define包括#.

4

6 回答 6

31

C 2011 (N1570) 6.10.3.4 3:“生成的完全用宏替换的预处理令牌序列不会作为预处理指令处理,即使它类似于…”</p>

C++ 2010 (N3092) 16.3.4 [cpp.rescan] 3 具有完全相同的文本。

于 2013-08-27T20:28:32.640 回答
10

看起来你的预处理器正在做你想要的替换,但你可能不会得到你想要的行为——预处理器通常只是一个单遍操作。示例(使用 clang,但您应该能够通过使用适当的 VS2008 标志来重现):

$ cat example.c 
#define D #define
D VALUE
$ cc -P -E example.c 

 #define VALUE

#define VALUE直接进入编译器,它不知道如何处理它——毕竟它是一个预处理器指令。供参考的 Clang 的错误与您的错误相似:

$ cc -c example.c 
example.c:2:1: error: expected identifier or '('
D VALUE
^
example.c:1:11: note: expanded from macro 'D'
#define D #define
          ^
1 error generated.
于 2013-08-27T20:20:58.570 回答
10

此代码是非法的,因为语言规范说它是非法的。根据 C 和 C++ 预处理器规范,您使用预处理器构建的任何代码都不会被解释为另一个预处理器指令。简而言之,您不能使用预处理器构建预处理器指令。时期。

(此外,您不能使用预处理器构建注释。)

于 2013-08-27T20:29:25.577 回答
6

这是行不通的,因为预处理是一次性执行的。例如,考虑下一个代码:

#define MYDEFINEWEIRD #define

MYDEFINEWEIRD N 6

int main() {

  return 0;
}

预处理后,您的代码将如下所示:

 #define N 6
int main() {

  return 0;
}

并且“#define”不是 C 或 C++ 上的有效语法。此外,由于不会处理生成的预处理器指令,因此它不会解析代码中对“N”宏的后续引用。

只是为了好玩,您可以使用 g++/gcc 从命令行调用预处理器两次。考虑下一个代码(define.cpp):

#include <iostream>

#define MYDEFINEWEIRD #define
MYDEFINEWEIRD N 6

using namespace std;

int main() {
  cout << N << endl;
  return 0;
}

然后你可以这样做:

$ g++ -E define.cpp | g++ -o define -x c++ - && ./define

并将输出:

6
于 2013-08-27T20:26:02.170 回答
1

查看 16 [cpp] 第 1 段中的语法,替换列表pp-tokens组成,其中可能包括在同一段的第 2 段中描述的产生式 # no-directive

非指令不应以列表中出现的任何指令名称开头。

也就是说,某种形式的东西

#define NAME # define

碰巧是违法的!另请注意,#在此上下文中,不会将下一个单词转换为字符串:a 后面的引用#仅发生#在函数样式宏中紧跟宏参数名称的情况下。

于 2013-08-27T20:37:16.913 回答
1

预处理器眼中的代码行要么是预处理器语句(因此没有对它们进行任何替换),要么是普通的文本语句(并且已经完成了替换)。您不能两者兼而有之,因此一旦替换了“ D”,它只会查看是否还有要替换的宏。由于没有,它只是在 C++ 代码中保留“#define”,然后 C++ 编译器在看到它时会出错(因为“#define”不是有效的 C++ 代码)。

因此,请进一步说明我的观点,这是预处理器的无效代码:

#define D define
#D value

因为预处理器不会对预处理器语句进行任何宏替换,并且“#D”不是公认的预处理器命令。和这个:

#define D #define
D value

此 C++ 代码中的结果:

#define value

这是无效的,因为预处理器已经运行完毕。

于 2013-08-27T20:30:10.007 回答