【问题标题】:C++ why does va_start expect the last non-variadic function argument?C++ 为什么 va_start 期望最后一个非可变函数参数?
【发布时间】:2013-02-03 15:26:01
【问题描述】:

我正在使用 Visual Studio 2012 编译此示例代码:

#include <stdarg.h>
#include <stdio.h>

const char * __cdecl foo(const char * format, const char * requiredArgument, ...)
{
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
    return requiredArgument;
}

int main(int, char **)
{
    foo("The %s is %d pixels wide and %d pixels high.", "box", 300, 200);
    return 0;
}

程序的调试构建在打印消息“框宽 300 像素,高 200 像素。”后正常终止。

发布版本因分段错误而崩溃。

我对这种行为的解释 - 但我可能错了,如果是,请纠正我 - 是我错误地指定了一个函数参数,而不是 @ 中的最后一个非可变参数987654322@,这里唯一可接受的形式是va_start(args, requiredArgument),而不是我想要的va_start(args, format)。 换句话说,我误用了va_start,导致整个程序流程变得不可预测,所以这里的分段错误没什么问题。

如果我的假设是正确的,我现在有两个问题:

  • 如果选择其他显然是非法的,为什么还要在va_start 中指定最后正式声明的函数参数?

  • 为什么挑剔的 VC++ 编译器不针对如此容易检测和潜在的严重缺陷发出警告?

【问题讨论】:

  • Linux/Debian/x86-64 上的 GCC 4.7 编译器在使用 gcc -Wall -g fti.c -o fti 调用时会正确警告:fti.c:7:5: warning: second parameter of 'va_start' not last named argument [enabled by default]

标签: c undefined-behavior variadic-functions


【解决方案1】:

如果选择其他任何东西显然是非法的,为什么还要在 va_start 中指定最后一个正式声明的函数参数?

因为那个宏需要知道最后一个参数的地址

为什么挑剔的 VC++ 编译器不针对如此容易检测和潜在的严重缺陷发出警告?

因为它还不够“智能”。或者它的创建者决定不包括这个警告。或者也许可以,但默认情况下它是关闭的,您可以使用一些编译器标志打开它。

【讨论】:

  • 实际上,最后一个参数的地址在现代实现中完全没用,其中参数可能不在堆栈上,或者即使它们在,编译器也可能选择不使用原始的“堆栈slot”作为参数的本地版本。需要将地址传递给va_start 的惯例来自古代,当时 C 几乎是“高级汇编程序”。在现代实现中,需要一个特殊的内置编译器来提供 va_start 宏。
  • 为了澄清,但是,这个答案没有错。形式上,需要该地址来满足va_start 宏的接口要求,即使它不应该需要将该地址用于内部任何事情。传递错误的地址导致UB。
猜你喜欢
  • 2017-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-19
  • 2012-03-11
  • 2011-09-15
  • 1970-01-01
相关资源
最近更新 更多