何时可以将变量的值传递给宏以进行分类?
例如,从this post提取的代码可与常量定义的宏一起使用。
#define MAX_STRING_LENGTH 20
#define STRINGIFY(x) STRINGIFY2(x)
#define STRINGIFY2(x) #x
{
...
char word[MAX_STRING_LENGTH+1];
scanf("%" STRINGIFY(MAX_STRING_LENGTH) "s", word);
...
}
但是我不能将其与诸如以下变量一起使用:
{
...
int val = 20;
char word[MAX_STRING_LENGTH+1];
scanf("%" STRINGIFY(val) "s", word);
...
}
由于编译成功并显示以下警告:
warning: invalid conversion specifier 'v' [-Wformat-invalid-specifier]
scanf("%" STRINGIFY(var) "s", word);
~~~^~~~~~~~~~~~~
test2.c:4:22: note: expanded from macro 'STRINGIFY'
#define STRINGIFY(x) STRINGIFY2(x)
^
test2.c:5:23: note: expanded from macro 'STRINGIFY2'
#define STRINGIFY2(x) #x
^
<scratch space>:466:2: note: expanded from here
"var"
^
1 warning generated
但是代码运行不会等待任何输入。
相反,在this other post中,可以将变量传递给该宏:
#define PRINT(int) printf(#int "%d\n",int)
...
int var =8;
PRINT(var);
这两种情况有什么区别?如何修改第一个,使其也接受变量?
我尝试在宏中使用%d
,但未成功。
答案 0 :(得分:3)
STRINGIFY(val)
将产生"val"
,而不是您要字符串化的值,因此您得到的最终格式字符串为"%vals"
("%" "val" "s"
)。这就是C预处理器的工作方式,它只执行文本替换,仅此而已。
PRINT
示例:
#define PRINT(int) printf(#int "%d\n", int)
PRINT(var); // to be resolved
printf(#var "%d\n", var); // intermediate result
printf("var" "%d\n", var); // final result, this is what the C compiler sees
但是为什么它可以与MAX_STRING_LENGTH
一起使用?
#define MAX_STRING_LENGTH 20
#define STRINGIFY(x) STRINGIFY2(x)
#define STRINGIFY2(x) #x
STRINGIFY(MAX_STRING_LENGTH) // to be resolved
STRINGIFY2(20) // intermediate step; STRINGIFY2 known as macro, thus:
#20 // another intermediate step
"20" // final result
答案 1 :(得分:3)
预处理器始终仅对令牌运行。
宏不是函数。您不向其传递变量(按值)。您传递令牌序列。在STRINGIFY(MAX_STRING_LENGTH)
中,令牌序列为MAX_STRING_LENGTH
,在STRINGIFY(val)
中,令牌序列为val
。
MAX_STRING_LENGTH
本身是一个宏,由于STRINGIFY
的定义工作方式,该宏将在被转换为字符串文字之前由预处理器进行扩展。因此,20
就是应用了#
的令牌,并产生"20"
作为字符串文字。
另一方面,val
不是宏,preprcosseor不会扩展它。它将令牌序列保持为val
。 val
是具有某些值的变量的名称,对预处理器来说没什么,它只关心令牌。因此val
转换为文字"val"
。
您从另一篇文章中摘录的示例之所以有用,是因为它扩展到了这一点:
printf("var" "%d\n", var);
#int
中的变量名称变成了文字,没有任何魔术可以使预处理器读取变量的值。打印var 8
的事实仅仅是因为将var
作为参数传递给printf
! %d
说明符在运行时打印。
最后,当尝试使用preprcoessor时,在完成prpeprocessing之后但在编译文件之前查看源文件总是有帮助的。 gcc -E
标志(或与您的编译器等效的标志)可以帮助您实现这一目标。