【问题标题】:Is this __VA_ARGS__ expansion valid c99?这是 __VA_ARGS__ 扩展有效的 c99 吗?
【发布时间】:2018-06-18 18:35:29
【问题描述】:

我正在尝试编写一个采用可变参数的函数。它有以下原型:

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 c-preprocessor variadic-functions variadic-macros


    【解决方案1】:

    根据 C 标准的6.10.3.1 Argument substitution, paragraph 2

    出现在替换列表中的标识符__VA_ARGS__ 应 被视为一个参数,并且变量参数应该 形成用于替换它的预处理标记

    因此,__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。)

    【讨论】:

    • "在 foo() 内部发生的事情可能仍然是 UB。" - 哈哈是的,但这是另一天。谢谢你的回答!
    • 我记错了参数...这里一切正常。但是,如果 foo 尝试访问上述任何可变参数,则会出现 UB。
    • @Kamajii - 我可以在这些可变参数上调用 va_* 系列函数吗?还是会产生 UB?
    • 调用va_startva_end 是合法的,这几乎就是printf() 对不包含占位符的格式字符串所做的事情。但是你显然不能使用va_arg 提取东西。
    • @Kamajii 好的,完美。再次感谢你们俩的时间!我很感激!
    【解决方案2】:

    作为也与可变参数相关的侧节点,C 要求的可变部分必须至少提供一个参数(原始问题中就是这种情况)。

    来自 ISO/IEC 9899:TC2 §6.10.3:4:

    如果宏定义中的标识符列表不以 省略号,参数的数量(包括那些参数 不包含预处理标记)在调用 类函数宏应等于宏中的参数数量 定义。否则,调用中应该有更多参数 比宏定义中有参数(不包括...)

    还有一个解决这个问题的草案:http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2034.htm

    幸运的是,大多数现代编译器要么抑制尾随逗号,要么提供某种解决方法。但是,这是编译器特定的行为。

    在原始帖子中,FOO 宏的可变参数部分被赋予字符串文字 "Hello Stack Overflow"。在扩展宏时,foo 函数可能会调用如下:

    foo("Example", "someFile.c", 42 "Hello Stack Overflow");
    

    这很好,只要 foo 函数通过某种方式知道在最后一个固定参数之后没有更多参数可以期待。

    【讨论】:

    • 所以只有 2 个宏参数的扩展函数调用导致 4 个参数被传递给 foofoo 在它的原型中有 4 个参数,不包括 ...。我是否理解这会导致正确的行为?
    猜你喜欢
    • 2012-08-01
    • 2015-11-30
    • 2011-07-05
    • 1970-01-01
    • 1970-01-01
    • 2017-04-25
    • 2015-12-02
    • 1970-01-01
    相关资源
    最近更新 更多