Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 370
You Know Who Avatar asked Jul 17 '20 07:07

You Know Who


People also ask

How does a string type string in C++ differ from a C type string?

std::string is compatible with STL algorithms and other containers. C strings are not char * or const char * ; they are just null-terminated character arrays. Even string literals are just character arrays.

How does a string type string differ from a C type string?

Neither C or C++ have a default built-in string type. C-strings are simply implemented as a char array which is terminated by a null character (aka 0 ). This last part of the definition is important: all C-strings are char arrays, but not all char arrays are c-strings.

Can you point out the main difference between C C++ strings and Java strings?

In C++, a string is typically just an array of (or a pointer to) chars, terminated with a NULL (\0) character. You can process a string by indexing also as you would process any array. But in Java , a strings are not arrays. Java strings are objects of type java.

What is the equivalent of string in C?

There is no string type in C . You have to use char arrays.


3 Answers

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.

like image 66
songyuanyao Avatar answered Oct 10 '22 12:10

songyuanyao


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]

like image 36
Alan Birtles Avatar answered Oct 10 '22 10:10

Alan Birtles


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.

like image 42
plugwash Avatar answered Oct 10 '22 11:10

plugwash