std::string 使用 c+=expression 和 c=c+expression 时的输出差异

std::string difference in output when use c+=expression and c=c+expression

在下面的代码中

#include<iostream>
#include<string>
using namespace std;
int main()
{
string a,c="!";
cin>>a;
int l=a.size();
for(int i=0;i<l;i++)
{   
    c=c+"#"+a[i];                                   
}
cout<<c;
}

如果我将 c=c+"#"+a[i] 替换为 c+="#"+a[i],我会得到意外的输出。 第二种情况的输出是 !boxboxbox,与 https://www.onlinegdb.com/ 上的输入无关。 在“dev c++”上,输出是 -

但是 a += b 等同于 a = a + b 。那么造成输出差异的原因是什么?

给定c+="#"+a[i];,首先计算"#"+a[i]"#"const char[2] 类型,可以衰减为指针,如 const char*a[i]char 类型,这是一个整数类型,然后是 "#"+a[i]只是执行指针运算,不会像您预期的那样连接字符串。 (并且指针运算的结果可能会超出数组的边界,然后导致UB。)

另一方面,在 c=c+"#"+a[i]; 中,首先计算 c+"#",它在 c 和 returns 上附加 "#" 一个新的 std::string(by operator+ for std::string),在其上追加 a[i] 并将结果分配给 c.

But a += b is equivalent to a = a + b

如果您将 b 放入积分中,即添加括号作为 ("#"+a[i]),那么 c+=("#"+a[i]);c=c+("#"+a[i]); 都会产生相同的结果,即使这不是您所期望的.

c=c+"#"+a[i] 表达式右侧的所有运算符相同,因此表达式从左到右处理,第一个元素是 std::stringconst char* 添加创建一个新的 std::string 然后添加一个 char 创建另一个 std::string 最后分配给 c.

使用 c+="#"+a[i] 表达式的右侧以 const char* 开头,您可以向其添加 char,这会调用指针算法生成一个无效地址,然后将其附加到字符串c 这是未定义的行为。要修复它,您必须强制第一个参数为 std::string: c+=std::string("#")+a[i]

从根本上说,因为 C++ 以“具有 classes 的 C”开始了它的生命。多年来,添加了大量新功能并收紧了一些规则,但 C++ 作为扩展 C 的背景仍然清晰可见。特别是

  • 该语言没有正确区分字符和整数。 “char”类型只是该语言中最小的整数类型。
  • 常规字符串文字计算为包含空终止字符串的常量数组中第一个字符的指针,而不是现代字符串类型。

std::string(严格来说是 std::basic_string 模板,但现在让我们忽略该细节)尽力帮助您。它为(再次忽略右值引用的细节)定义了合理的重载。

  • std::string + std::string
  • std::string + 字符*
  • std::string + 字符
  • 字符* + std::string
  • 字符 + std::string

但它对两个参数都不是 std::string 的运算符无能为力。这些在 C++ 中的工作方式与在 C 中的工作方式相同。

  • char* + char* --> 错误
  • char + char --> 整数加法
  • char* + char --> 指针运算
  • char + char* --> 指针运算

这样做的结果是操作顺序变得非常重要。

c=c+"#"+a[i]; 等同于 c=((c+"#")+a[i]);。这工作正常,在最里面的操作中,一个参数是 std::string,因此重载运算符做正确的事情并将参数连接起来产生另一个 std::string。当我们将最内层操作的结果连接到 a[i]

时,同样适用

c+="#"+a[i]; 在功能上等同于* c=(c+("#"+a[i])); 所以现在我们尝试在计算结果为 char * 的字符串文字和计算结果为 char 的操作之间使用 + 运算符。因此,我们将 a[i] 处字符的字符代码添加到指向字符串“#”的指针。

因为“#”是一个相当短的字符串,这几乎肯定会导致指针超出字符串的末尾。这是语言规范未定义的行为。

我猜测“!boxboxbox”是来自 onlinegdb 的沙箱错误。它检测到您的代码正在做一些不应该做的事情,并拒绝让它继续。

许多 compilers/linkers 将不同的字符串数据放在一起,因此在常规编译器上显示(部分)来自可执行文件(或它使用的库)的另一个字符串是 运行 关闭的可能结果字符串的结尾。

C++11 确实添加了对 std::string 文字的支持,因此一种解决方法是添加

using namespace std::string_literals;

然后把"#"改成"#"s


* 请注意,通常在 C++ 中重载运算符时,“+”和“+=”是独立的运算符,没有任何强制 class 的实现者使它们在功能上等效。理智的 class 设计师通常会。

此外,+= 可能更有效,因为它可以就地执行串联,而不是创建新字符串。