我正在定义一个宏,它计算为一个常量字符串,包含文件名和行号,用于记录目的。
它工作正常,但我只是无法弄清楚为什么需要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__
都不起作用?
答案 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__
确实是)一起使用时不起作用;否则宏不会被取代。