5

我正在定义一个宏,它评估为一个常量字符串,保存文件名和行号,用于记录目的。

它工作得很好,但我只是无法弄清楚为什么需要 2 个额外的宏 -STRINGIFY并且TOSTRING,当直觉简单地建议时__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__

4

2 回答 2

7

因为扩展顺序。GCC 文档说:

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

因此,如果参数将被字符串化,则不会首先对其进行扩展。您在括号中得到文字。但是如果它被传递给另一个宏,它就会被扩展。因此,如果要对其进行扩展,则需要两级宏。

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

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

您希望消息是:

Assertion MIN(width, height) >= 240 failed

而不是 MIN 宏扩展为一些疯狂的东西(在 gcc 中,它使用了几个 gcc 特定的扩展,并且 IIRC 很长)。

于 2011-09-12T12:56:21.610 回答
2

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

__FILE__ ":" STRINGIFY(__LINE__)可以与其他文本一起使用(例如 __FILE__ ":" STRINGIFY(foo),但在与另一个宏(实际上都是)一起使用时不起作用__LINE__)作为参数;否则该宏不会被替换。

于 2011-09-12T12:27:00.437 回答