这个 __VA_ARGS__ 扩展有效 c99 吗?
Is this __VA_ARGS__ expansion valid c99?
我正在尝试编写一个采用可变参数的函数。它有以下原型:
void foo(const char *name, const char *file, uint32_t line, const char *fmt, ...);
我用下面的宏调用它:
#define FOO(name, ...) \
foo(name, __FILE__, __LINE__, __VA_ARGS__);
据我了解,以下内容有效:
FOO("Example, "Hello %s", "Stack Overflow");
但是以下会导致符合标准的 c99 编译器出现未定义的行为吗?
FOO("Example", "Hello Stack Overflow");
我担心的是,因为 foo
需要 *fmt
和 ...
,所以当只传递两个参数时,将添加尾随 ,
到宏。
谁能告诉我上面的 c99 是否有效?
编辑: 当我 运行 使用 gcc 和 std=c99
它工作时,但我担心有无声的 UB
谢谢!
作为也与可变参数相关的侧节点,C 要求 宏 的可变部分必须至少提供一个参数(原始问题中就是这种情况) ).
来自 ISO/IEC 9899:TC2 §6.10.3:4:
If the identifier-list in the macro definition does not end with an
ellipsis, the number of arguments (including those arguments
consisting of no preprocessing tokens) in an invocation of a
function-like macro shall equal the number of parameters in the macro
definition. Otherwise, there shall be more arguments in the invocation
than there are parameters in the macro definition (excluding the ...).
还有一个解决这个问题的草案:http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2034.htm
幸运的是,大多数现代编译器要么禁止尾随逗号,要么提供某种解决方法。但是,这是编译器特定的行为。
在原始 post 中,FOO
宏的可变参数部分被赋予字符串文字 "Hello Stack Overflow"
。在扩展宏时,foo
函数可能被调用如下:
foo("Example", "someFile.c", 42 "Hello Stack Overflow");
这很好,只要 foo
函数通过某种方式知道在最后一个固定参数之后没有更多的参数可以期待。
根据 C 标准的 6.10.3.1 Argument substitution, paragraph 2:
An identifier __VA_ARGS__
that occurs in the replacement list shall
be treated as if it were a parameter, and the variable arguments shall
form the preprocessing tokens used to replace it.
因此,__VA_ARGS__
将替换为作为参数传递给宏的所有标记。
因此,在您的情况下,给定宏:
#define FOO(name, ...) \
foo(name, __FILE__, __LINE__, __VA_ARGS__);
调用
FOO("Example", "Hello Stack Overflow");
和函数声明
void foo(const char *name, const char *file, uint32_t line, const char *fmt, ...);
结果类似于
foo( "Example", "asdf.c", 1234, "Hello Stack Overflow" );
并且在对 foo
的调用中没有未定义的行为。 (foo()
里面发生的事情可能仍然是 UB。)
我正在尝试编写一个采用可变参数的函数。它有以下原型:
void foo(const char *name, const char *file, uint32_t line, const char *fmt, ...);
我用下面的宏调用它:
#define FOO(name, ...) \
foo(name, __FILE__, __LINE__, __VA_ARGS__);
据我了解,以下内容有效:
FOO("Example, "Hello %s", "Stack Overflow");
但是以下会导致符合标准的 c99 编译器出现未定义的行为吗?
FOO("Example", "Hello Stack Overflow");
我担心的是,因为 foo
需要 *fmt
和 ...
,所以当只传递两个参数时,将添加尾随 ,
到宏。
谁能告诉我上面的 c99 是否有效?
编辑: 当我 运行 使用 gcc 和 std=c99
它工作时,但我担心有无声的 UB
谢谢!
作为也与可变参数相关的侧节点,C 要求 宏 的可变部分必须至少提供一个参数(原始问题中就是这种情况) ).
来自 ISO/IEC 9899:TC2 §6.10.3:4:
If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal the number of parameters in the macro definition. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...).
还有一个解决这个问题的草案:http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2034.htm
幸运的是,大多数现代编译器要么禁止尾随逗号,要么提供某种解决方法。但是,这是编译器特定的行为。
在原始 post 中,FOO
宏的可变参数部分被赋予字符串文字 "Hello Stack Overflow"
。在扩展宏时,foo
函数可能被调用如下:
foo("Example", "someFile.c", 42 "Hello Stack Overflow");
这很好,只要 foo
函数通过某种方式知道在最后一个固定参数之后没有更多的参数可以期待。
根据 C 标准的 6.10.3.1 Argument substitution, paragraph 2:
An identifier
__VA_ARGS__
that occurs in the replacement list shall be treated as if it were a parameter, and the variable arguments shall form the preprocessing tokens used to replace it.
因此,__VA_ARGS__
将替换为作为参数传递给宏的所有标记。
因此,在您的情况下,给定宏:
#define FOO(name, ...) \
foo(name, __FILE__, __LINE__, __VA_ARGS__);
调用
FOO("Example", "Hello Stack Overflow");
和函数声明
void foo(const char *name, const char *file, uint32_t line, const char *fmt, ...);
结果类似于
foo( "Example", "asdf.c", 1234, "Hello Stack Overflow" );
并且在对 foo
的调用中没有未定义的行为。 (foo()
里面发生的事情可能仍然是 UB。)