【问题标题】:Undefined behavior when converting char to std::string c++将 char 转换为 std::string c++ 时的未定义行为
【发布时间】:2021-05-10 11:34:14
【问题描述】:

我的程序接受一个向量std::vector<std::string> vector 和一个字符char separator 并返回一个字符串,其中所有字符串在分隔符之间加在一起。概念是:vector[0] + separator + vector[1] + separator

守则

std::string VectorToString(std::vector<std::string> vector, char separator)
{
    std::string output;
    for(std::string segment : vector)
    {  
        std::string separator_string(&separator);
        output += segment + separator_string;
    }
    return output;
}

int main()
{
    std::vector<std::string> vector = {"Hello",  "my",  "beautiful", "people"};
    std::cout << VectorToString(vector, ' ');
}

我的预期输出是Hello my beautiful people 但是输出是:

Hello �����my �����beautiful �����people �����

我发现这个字符有问题,特别是它的指针:std::cout &lt;&lt; &amp;separator; -> �ƚ��。但是,如果我这样做:std::cout &lt;&lt; (void*) &amp;separator; -> 0x7ffee16d35f7。虽然我真的不知道(void*) 做了什么。

问题:

1.发生了什么?

2.为什么会这样?

3.如何解决?

4.如何防止它在未来的项目中发生?

【问题讨论】:

  • ...这没有解释为什么它是未定义的行为...
  • 这并没有解决问题,但是每次通过循环创建 separator_string 对象会浪费时间。在循环之前创建一次。或者,更好的是,根本不创建它。只需将字符附加到字符串:output += segment; output += separator;。 (是的,字符串对象知道如何附加单个字符)对于循环,创建对每个字符串的引用而不是复制:for (const std::string&amp; segment : vector)
  • @PeteBecker 为什么我应该使用引用而不是复制。是更快还是有其他原因?
  • @ViktorSkarve -- 它更快,使用更少的资源。对于像这样的简单程序并不重要,但是当您进入更复杂的程序时,它可以在速度和内存使用方面产生很大的不同。它可以在成功运行和内存不足之间产生差异。一般来说,使用const 引用而不是复制大的东西是一个好习惯。这就是为什么这么多函数需要const std::string&amp;

标签: c++ string c++11 char undefined-behavior


【解决方案1】:

这一行

std::string separator_string(&separator);

尝试从一个以 0 结尾的 C 字符串构造一个字符串。

但是&amp;separator 不是以0 结尾的,因为它取决于separator 之后的其他内存字节是否有0 字节(可能没有)。所以你得到了未定义的行为。

你可以做的是使用其他构造函数:

std::string separator_string(1, separator);

这个通过重复 separator 字符 1 次来创建一个字符串。

【讨论】:

  • 所以 std::string separator_string(&separator);应该与 c-string 而不是 char 一起使用?
  • 是的,因为前。这个可以工作: separator_string(" ");因为“”是一个以零结尾的 C 字符串。
  • +1,我完全没有看到&amp;。顺便说一句,你不需要先构造一个字符串,std::string 重载operator+ 也是为了 char。所以output += segment + separator; 应该足够了。
【解决方案2】:

按照标准,如下:

basic_string( const CharT* s,
              const Allocator& alloc = Allocator() );

意思是:

用 s 指向的 null-terminated 字符串的副本初始化内容构造字符串。字符串的长度由第一个空字符确定。如果 [s, s + Traits::length(s)) 不是有效范围,则行为未定义。

因此,std::string separator_string(&amp;separator); 会导致未定义行为,因为 separator 不是以空值终止的。

为防止这种情况,您可能需要使用以下重载:

basic_string( const CharT* s,
              size_type count,
              const Allocator& alloc = Allocator() );

喜欢std::string separator_string(&amp;separator, 1); 甚至更简单(正如其他答案指出的那样)std::string separator_string(1, separator);

【讨论】:

    猜你喜欢
    • 2010-11-14
    • 1970-01-01
    • 2013-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-10
    • 2011-08-02
    • 2014-07-23
    相关资源
    最近更新 更多