C预处理器字符串怪异

时间:2011-09-12 12:15:54

标签: c c-preprocessor stringification

我正在定义一个宏,它计算为一个常量字符串,包含文件名和行号,用于记录目的。

它工作正常,但我只是无法弄清楚为什么需要2个额外的宏 - STRINGIFYTOSTRING,直觉只是__FILE__ ":" #__LINE__

#include <stdio.h>

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define THIS_ORIGIN (__FILE__ ":" TOSTRING(__LINE__))

int main (void) {
  /* correctly prints "test.c:9" */
  printf("%s", THIS_ORIGIN);
  return 0;
}

这对我来说似乎是一个丑陋的黑客。

有人可以逐步详细解释会发生什么事情,以便__LINE__正确地进行字符串化,以及为什么__FILE__ ":" STRINGIFY(__LINE__)__FILE__ ":" #__LINE__都不起作用?

2 个答案:

答案 0 :(得分:7)

由于扩张的顺序。 GCC documentation说:

  

宏参数在被替换为宏体之前完全是宏扩展的,除非它们被字符串化或粘贴到其他标记。替换后,将再次扫描整个宏体(包括替换参数),以便扩展宏。结果是参数被扫描两次以扩展其中的宏调用。

因此,如果参数将被字符串化,则不会首先展开它。您将在括号中获取文字文本。但如果它被传递给另一个宏,它就会被扩展。因此,如果要扩展它,则需要两个级别的宏。

这样做是因为有些情况下想要在字符串化之前扩展参数,最常见的是assert()宏。如果你写:

assert(MIN(width, height) >= 240);

你想要的信息是:

Assertion MIN(width, height) >= 240 failed

并不是MIN宏扩展到的一些疯狂的东西(在gcc中它使用了几个特定于gcc的扩展并且是非常长的IIRC)。

答案 1 :(得分:2)

您不能简单地使用__FILE__":"#__LINE__,因为stringify运算符#只能应用于宏参数。

__FILE__ ":" STRINGIFY(__LINE__)可以正常使用其他文本(例如__FILE__ ":" STRINGIFY(foo),但在与另一个宏(实际上__LINE__确实是)一起使用时不起作用;否则宏不会被取代。