将内置数据类型转换为std :: string:memcpy vs snprintf

时间:2014-08-08 11:06:18

标签: c++ string undefined-behavior memcpy

在此之前我已经提到了relevant question和其他帖子。我也知道std::to_string()是最好的方式(但在少数平台上不可用)。

试验时,我遇到了memcpy()的一个奇怪问题。例如,假设我们始终将内置数据类型(intcharlong)传递给以下函数:

template<typename T>
std::string to_string (const T& value)
{
  std::string s(16, 0); // Max size captured
  ::memcpy(&s[0], &value, sizeof(value));
  return s;
}

在示例程序中单独运行此函数可以正常工作。但是当插入更大的代码库时,不知何故它会产生奇怪的结果!即它给出虚假的价值。 (Ubuntu 14.10,g ++ 4.9 -std = c ++ 11)

但是,如果我使用sprintf()转换上述程序,它可以正常工作。

template<typename T>
std::string to_string (const T& value)
{
  std::string s(16, 0); // Max size captured
  s[::snprintf(&s[0], "%d", value)] = 0;
  return s;
}

问题

  1. 我是否接触过memcpy()的未定义行为(甚至是 sprintf())?
  2. 字节排序是否影响此代码?

1 个答案:

答案 0 :(得分:2)

回顾一下,是的,您不想使用memcpy()。使用snprintf(),您可以避免自己将数字转换为ASCII。这样的事情可能会更好:

template<typename T>
std::string to_string (const T& value)
{
  char buf[16];
  ::snprintf(buf, sizeof(buf), "%d", value);
               // ^-- size was missing in your example
  return buf;
}

但是,你在这个功能中有很大的流量,因为你不知道T会是什么。它可能是一个双倍,"%d"将无法正常工作。同样,它可以是一个字符串(char const *)。

如果要手动将数字转换为ASCII,可以使用循环,如下所示:

template<typename T>
std::string to_string (T value)
{
  char buf[16]; // any int number is less than 16 characters
  char *s = buf + sizeof(buf);
  *--s = '\0';
  do
  {
    *--s = value % 10 + '0';  // conversion to ASCII, 1 digit at a time
    value /= 10;
  }
  while(value > 0);
  return s;
}

警告:该功能无法正确处理负数。我会把那个作为练习让你按要求处理。

现在,如果你想使用一种适用于你提到的所有系统的C ++方式,而不使用boost或C ++ 11。

template<typename T>
std::string to_string (T const& value)
{
  std::stringstream ss;
  ss << value;
  return ss.str();
}

在这种情况下,stringstream知道如何处理T,无论T是什么,数字,对象等,只要这些内容理解<< std::cout << "Hello!" << std::endl;中的std::ostream& operator << (std::ostream& out, Node const& node);

如果你看看我的一个名为as2js的项目,你会看到一个名为include / as2js / node.h的文件,它声明如下:

Node n;
std::out << n << std::endl;

这意味着您可以稍后创建节点并按以下方式打印:

{{1}}

这意味着你的to_string()函数可以用于我的Node对象。

您可以在lib / node_display.cpp

下找到所有这些的实现