TransWikia.com

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

Stack Overflow Asked by Big Gorilla on November 4, 2021

In the below code

#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;
}

If I replace c=c+"#"+a[i] with c+="#"+a[i] i get unexpected output.
Output in the second case is !boxboxboxbox irrespective of input on https://www.onlinegdb.com/ .
On "dev c++" the output is –

enter image description here

But a += b is equivalent to a = a + b . Then what is the reason for the difference in output?

3 Answers

Fundamentally because C++ started its life as "C with classes". Over the years a bunch of new functionality was added and some rules were tightened but C++'s background as an extended C is still clearly visible. In particular.

  • The language doesn't make a proper distinction between characters and integers. Type "char" is simply the smallest integer type in the language.
  • A regular string literal evaluates to a pointer the first character in a constant array containing a null-terminated string, not to a modern string type.

std::string (strictly the std::basic_string template but lets ignore that detail for now) does it's best to help you. It defines sensible overloads for (again ignoring the detail of rvalue references).

  • std::string + std::string
  • std::string + char*
  • std::string + char
  • char* + std::string
  • char + std::string

But it can't do anything about operators where neither argument is a std::string. Those work in C++ in the same way they work in C.

  • char* + char* --> error
  • char + char --> integer addition
  • char* + char --> pointer arithmetic
  • char + char* --> pointer arithmetic

The result of this is that order of operations becomes very important.

c=c+"#"+a[i]; is equivalent to c=((c+"#")+a[i]);. This works fine, in the innermost operation one argument is a std::string so the overloaded operators do the right thing and concatenate the arguments to produce another std::string. The same applies when we concatenate the result of that innermost operation to the a[i]

c+="#"+a[i]; is functionally equivalent* to c=(c+("#"+a[i])); so now we are trying to use the + operator between a string literal which evaluates to a char * and an operation which evaluates to a char. So we add the character code for the character at a[i] to the pointer to the string "#".

since "#" is a rather short string, this will almost certainly result in a pointer that is past the end of the string. This is undefined behaviour by the language spec.

I would guess that "!boxboxbox" is a sandbox error from onlinegdb. It has detected your code doing something it shouldn't and refused to let it go ahead.

Many compilers/linkers put different string data together, so on a regular compiler displaying (part of) another string from the executable (or libraries that it uses) is a likely outcome of running off the end of a string.

C++11 did add support for std::string literals, so one fix could be to add

using namespace std::string_literals;

Then change "#" to "#"s


* Note that in general with overloaded operators in c++ "+" and "+=" are separate operators and nothing forces the implementer of the class to make them functionally equivalent. Sane class designers generally will though.

Also += may be more efficient as it may be able to perform the concatenation in-place rather than creating a new string.

Answered by plugwash on November 4, 2021

With c=c+"#"+a[i] all the operators in the right of the expression are the same so the expression is processed from left to right, the first element is a std::string to which a const char* is added creating a new std::string then add a char creating another std::string which is finally assigned to c.

With c+="#"+a[i] the right of the expression starts with a const char* to which you add a char, this invokes pointer arithmetic producing an invalid address which is then appended to the string c which is undefined behaviour. To fix it you have to force the first argument to be a std::string: c+=std::string("#")+a[i]

Answered by Alan Birtles on November 4, 2021

Given c+="#"+a[i];, "#"+a[i] is evaluated at first. "#" is of type const char[2] and could decay to pointer as const char*, a[i] is of type char which is an integral type, then "#"+a[i] just performs pointer arithmetic and won't concatenate strings as you expected. (And the result of pointer arithmetic might get out of the bound of the array and then leads to UB.)

On the other hand, in c=c+"#"+a[i];, c+"#" is evaluated at first, it appends "#" on c and returns a new std::string (by operator+ for std::string), on which a[i] is appended and the result is assigned to c.

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

If you put b in integration, i.e. add parentheses as ("#"+a[i]), then both c+=("#"+a[i]); and c=c+("#"+a[i]); yields the same result, even it's not what you expected.

Answered by songyuanyao on November 4, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP