【问题标题】:is it safe to use va_copy on windows在 Windows 上使用 va_copy 是否安全
【发布时间】:2020-03-06 04:00:58
【问题描述】:

我已经通过链接https://devblogs.microsoft.com/oldnewthing/20131114-00/?p=2663 了解使用 va_list 的潜在陷阱

下面来自同一链接的代码段指定不要以显示的方式使用 va_list,因为 va_list 不能直接复制。

BOOL FormatWithFallbackLanguage(
    DWORD dwMessageId, PCTSTR pszBuffer, SIZE_T cchBuffer, va_list ap)
{
 va_list apOriginal = ap;
 // Format from the user's preferred language.
 DWORD cchResult = FormatMessage(
               FORMAT_MESSAGE_FROM_HMODULE,
               g_hinst, dwMessageId, g_preferredLangId,
               pszBuffer, cchBuffer, &ap);
 // If that didn't work, then use the fallback language.
 if (cchResult == 0) {
  cchResult = FormatMessage(
               FORMAT_MESSAGE_FROM_HMODULE,
               g_hinst, dwMessageId, g_fallbackLangId,
               pszBuffer, cchBuffer, &apOriginal);
 }
 return cchResult != 0;
}

链接中的文章建议将 va_copy 用于上述编码场景。但是在 Windows 上,在头文件 stdarg.h 中, va_copy 似乎没有做任何事情

#define va_copy(destination, source) ((destination) = (source))

我的问题是在 windows 中使用 va_copy 是否安全 va_list ?如果是,为什么,如果不是,那么前进的方向是什么?

【问题讨论】:

  • 在我看来这个问题更多的是关于 FormatMessage 所以也许会在标题中提到这一点
  • 既然你已经用 C 和 C++ 标记了它,有必要指出 C 要求你使用 va_copy() 来复制参数列表(不需要宏除此以外)。此外,在从函数返回(或重用复制的列表等)之前,您必须在复制的列表上使用va_end()。见 C11§7.16 Variable arguments <stdarg.h>
  • va_copy 可以从 Windows 的stdarg.h 获得,这意味着它可以工作;否则他们不会费心去定义它。
  • @jamesdlin C++ 也需要这些东西(它在这个主题上遵循 ISO C)
  • @M.M 这个回复是给@Jonathan Leffler 的吗?我不确定它是否与 C++ 的 C 一致性有关。与完全不提供va_copy 相比,提供一个损坏的va_copy 实现实际上并不符合标准(无论哪种标准)......

标签: c++ c c++11 variadic-functions variadic-macros


【解决方案1】:

你不应该往里面看香肠是怎么做的。 :-)

好的,现在你已经看过了。而且您已经看到,Windows 不是那些复杂的调用约定需要在va_list 中分配内存的平台之一。事实证明,您实际上并不需要使用va_copy 而不是简单的赋值,而且va_end 实际上什么也没做。那很糟糕。你不应该看的。现在你必须忘记这一切。

因为明天一切都可能改变。一个新的库或编译器优化逻辑的更新,或者谁知道呢,突然需要va_copyva_end 可以防止内存泄漏,而你的有罪知识突然变成了一个危险的错误。

标准规定您必须使用va_copy 来复制va_list。标准规定您必须为每个初始化的va_list 调用va_end必须这样做。因为如果不窥探您没有业务的地方,您就无法知道您的平台不需要它。

标准库不必遵守这些规则。它可以完全了解其平台和自己的内部结构,因为它只需要在自己的环境中工作。如果有什么变化,维护标准库的好人会意识到这一点,他们会确保新版本在他们的平台上与他们的编译器的新版本一起工作

这就是交易:实施将保证,如果您遵守规则,一切都会奏效。规则的存在是为了让实现者可以处理他们平台的特殊性,并且编译器可以挤出尽可能多的优化周期。

有时似乎偷工减料和打破规则不会受到伤害,至少现在和现在不会。但不要屈服于这种诱惑。因为如果你这样做了,你就在你的代码中留下了一个定时炸弹,它迟早会爆炸。

【讨论】:

    【解决方案2】:

    如果您使用的编译器符合 ISO C++,那么va_copy 可以安全使用(这是一个重言式)。

    可以想象 FormatMessage 函数会破坏列表,但我认为不会。如果确实如此,那么链接文章中有关重新启动列表的其他建议也将不起作用。

    您尝试过建议的代码吗? va_copy 的正确用法是:

    va_list ap2;
    va_copy(ap2, ap);
    
    DWORD cchResult = FormatMessage(
               FORMAT_MESSAGE_FROM_HMODULE,
               g_hinst, dwMessageId, g_preferredLangId,
               pszBuffer, cchBuffer, &ap);
    
    if (cchResult == 0) 
        cchResult = FormatMessage(
               FORMAT_MESSAGE_FROM_HMODULE,
               g_hinst, dwMessageId, g_fallbackLangId,
               pszBuffer, cchBuffer, &ap2);
    
    va_end(ap2);   
    

    【讨论】:

    • 到目前为止,我实际上在 Windows 上没有遇到任何问题,但上面的文章对它的用法有疑问。此外,在 stdarg.h 中定义 va_copy 的方式增加了混乱。所以想知道将 va_list 与 va_copy 一起使用是否对 Windows 来说是一种安全的方式?
    • @J.Snow 操作系统与这个问题没有任何关系,它与您的编译器是否符合 C++ 标准有关。我显然无法回答,但如果您发现没有,您可以提交错误报告
    • 我可能看起来有点困惑,但只是为了添加更多内容,我正在使用 Visual Studio 2017 在 Windows 上编译代码。还有什么可以检查我的编译器是否符合 C++ 标准?
    • @J.Snow 不是真的;如果它看起来有效,那么我不会担心它
    猜你喜欢
    • 2015-01-13
    • 1970-01-01
    • 1970-01-01
    • 2015-01-03
    • 1970-01-01
    • 2012-01-09
    • 2014-06-15
    • 2016-04-13
    • 2017-10-22
    相关资源
    最近更新 更多