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

Big*_*lla 19 c++ string stdstring c++11

在下面的代码中

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

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

son*_*yao 23

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

在另一方面,在c=c+"#"+a[i];c+"#"在第一被评估,其附加"#"c并返回一个新的std::string(通过operator+std::string),在其上a[i]被附加,并将结果指定给c

但是 a += b 等价于 a = a + b

如果你把b集成,即加括号的("#"+a[i]),那么这两个c+=("#"+a[i]);c=c+("#"+a[i]);产生同样的结果,即使它不是你所期望的。


Ala*_*les 7

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

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

  • 我认为拼写错误,`c+=std::string("#")+a[i];` (2认同)

plu*_*ash 5

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

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

std::string (严格来说是 std::basic_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 的沙箱错误。它检测到您的代码做了不该做的事情并拒绝让它继续。

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

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

using namespace std::string_literals;

然后"#"改为"#"s


* 请注意,一般来说,c++ 中的重载运算符“+”和“+=”是单独的运算符,没有什么会强制类的实现者使它们在功能上等效。理智的班级设计师通常会。

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