STR这个实现是否安全且可移植?



我正在考虑在我们的项目中提出以下内容作为sprintf/snprintf的替代方案。

动机是消除考虑缓冲区大小的需要,并尽可能多地保留原始的便利性。

std::string strprintf(const char *format, ...)
{
std::string s;
s.resize(128); // best guess
char *buff = const_cast<char *>(s.data());
va_list arglist;
va_start(arglist, format);
auto len = vsnprintf(buff, 128, format, arglist);
va_end(arglist);
if (len > 127)
{
va_start(arglist, format);
s.resize(len + 1); // leave room for null terminator
buff = const_cast<char *>(s.data());
len = vsnprintf(buff, len+1, format, arglist);
va_end(arglist);
}
s.resize(len);
return s; // move semantics FTW
}

此代码是否有任何固有问题?

使用示例:

auto s = strprintf("Hello %d world", 777);

目前建议的实现似乎至少在安全性和可移植性方面存在以下问题:

  1. 不要使用const_cast。如果您使用的是 C++17,那么您可以省略const_cast,就像在 C++17 中一样,data(( 为非常量字符串返回一个 char*。在 C++11 和 C++14 中,可以使用 buf=&s[0] 作为替代方法。在 C++11 之前,您必须使用 new[] 分配一个 char[] 数组并使用 delete[] 将其删除,因为无法保证 std::string 的字符将按顺序存储。
  2. vsnprintf 可以在错误时返回负值,例如格式字符串错误。根据返回的负值(实现定义(,resize(len+1( 调用会将字符串调整为新的长度 0 并隐藏错误,或调整为非常大的新长度(例如 size_t(-1((,这可能会触发内存不足条件。您可能希望向函数添加错误处理以避免两者。
  3. 如果第一个 vsnprintf 返回 len==INT_MAX,则计算 (len+1( 具有未定义的行为。通过检查来避免。
  4. vsnprintf 函数的行为和返回值可能与 C99 中为 Microsoft 编译器和某些 C++11 之前的 C++ 编译器定义的行为和返回值不同。当需要可移植性时,请务必检查文档并对边界案例进行测试。
  5. 关于安全性:现代编译器可以根据格式字符串检查 printf 类型函数的参数的数据类型,并在检测到参数的数据类型与格式字符串不匹配的用法时发出警告。通过提供自己的 printf 类型函数,您首先会丢失此编译器检查。某些编译器支持向函数添加__attribute__s,以便可以为自定义函数启用 printf 格式检查。应为面向并支持此功能的所有编译器启用此功能。

事实证明,这里已经有很多关于这个问题的讨论:

std::字符串格式,如 sprintf

主观上,我的版本看起来仍然比那里的大多数东西更简洁,但它在功能上与该线程上提出的一些解决方案相同。

相关内容

  • 没有找到相关文章

最新更新