【问题标题】:std::string difference in output when use c+=expression and c=c+expression使用 c+=expression 和 c=c+expression 时输出的 std::string 差异
【发布时间】:2020-11-06 23:02:27
【问题描述】:

在下面的代码中

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

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

【问题讨论】:

  • 运算符优先级是原因
  • 什么是"#" 才是真正的原因。是const char * 还是std::string?见C++ String concatination

标签: c++ string c++11 stdstring


【解决方案1】:

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

另一方面,在c=c+"#"+a[i]; 中,c+"#" 首先被评估,它在c 上附加"#" 并返回一个新的std::stringoperator+ for std::string),a[i] 是附加,并将结果分配给c

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

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

【讨论】:

  • @BigGorilla,接受这个答案。这个答案是正确的,正确回答了你的问题
  • 添加括号在 Visual Studio 中没有任何作用。
【解决方案2】:

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

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

【讨论】:

  • 我认为是错字,c+=std::string("#")+a[i];
【解决方案3】:

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

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

std::string (严格来说是 std::basic_string 模板,但现在让我们忽略这个细节)最好能帮助你。它定义了合理的重载(再次忽略右值引用的细节)。

  • std::string + std::string
  • std::string + char*
  • std::string + char
  • char* + std::string
  • char + 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++ 中重载运算符时,“+”和“+=”是独立的运算符,没有什么会强制类的实现者使它们在功能上等效。不过,理智的类设计师通常会这样做。

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

【讨论】:

  • 还可能值得注意的是c += expr 仅等同于c = c + expr,因为std::string 尽最大努力定义合理的运算符,而不是因为这是语言的实际特性……
  • @wrtlprnft 添加了关于此的注释。
猜你喜欢
  • 1970-01-01
  • 2010-09-13
  • 2016-04-11
  • 2010-10-13
  • 1970-01-01
  • 2011-03-18
  • 2022-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多