views:

841

answers:

8

Why does name misbehave in the following C++ code?

string name =  "ab"+'c';

How would the equivalent code behave in Java/C#?

A: 

You can concatenate strings and chars in C# - it's not as strict as C++ I guess.

This works just fine in C#:

string test = "foo" + 'b';
Jon Tackabury
It's not strictness, it's just that strings don't technically exist in C++
Ed Woodcock
Thanks Ed - good point.
Jon Tackabury
+11  A: 

Try

std::string name = "ab" "c";

or

std::string name = std::string("ab") + c;

In C++, "ab" is not a std::string, but rather a pointer to a string of chars. When you add an integral value to a pointer, you get a new pointer that points farther down the string:

char *foo = "012345678910121416182022242628303234";
std::string name = foo + ' ';

name gets set to "3234", since the integer value of ' ' is 32 and 32 characters past the begining of foo is four characters before the end of the string. If the string was shorter, you'd be trying to access something in undefined memory territory.

The solution to this is to make a std:string out of the character array. std:strings let you append characters to them as expected:

std::string foo = "012345678910121416182022242628303234";
std::string name = foo + ' ';

name gets set to "012345678910121416182022242628303234 "

Eclipse
That's not the same thing. There's nothing whatsoever in the original about I/O. He could want to catenate them for creating a name for a semaphore, or something.
T.E.D.
The question has changed significantly from the original question. I've edited to reflect the change.
Eclipse
I was just now going to edit the original question back in so answers using std::cout would make sense, but it looks like this was the last one.
Michael Myers
+2  A: 

Java:

public class Main
{
    public static void main(String[] args)
    {
        System.out.println("AB" + 'c');
    }
}

Output is:

ABc

Edit:

Actually the compiler hard codes the String ABc...

If you do "AB" + argv[0].charAt(0); to make it use a variable then the compiler does this (basically):

StringBuilder b = new StringBuilder;
b.append("AB");
b.append(argv[0].charAt(0));
System.out.println(b.toString());
TofuBeer
you forgot the "\n" in the output. It is println() not print() :)
OTisler
And what actually is going on here is that the char literal is casted to string literal before the + operation, which then generates a new string from the "AB" and (String)'c' string literals.
Joonas Pulakka
@OTisler what is a few extra newlines between friends? :-P
TofuBeer
Updated to include the newline :-P and to show what is going on at compile time
TofuBeer
Removed my earlier misleading comment.
Michael Myers
+5  A: 

In C++, the compiler is looking for a function with this prototype:

T operator+ (const char*, char);

Since there isn't one, it can't figure out what T is and can't resolve the operator<< call, so it falls back to the only solution left: pointer addition. There is no problem with catenating to a string as in Josh's answer, because a function exists for it.

greyfade
It finds one, it's just not the one you'd hope for. You end up getting pointer arithmetic instead of string concatenation.
Eclipse
Good point. But the question has changed so many times now, I've lost track of what he's asking.
greyfade
+8  A: 

The problem is that "ab" is not a C++ std::string, but a const char[3]. So the + operator it's looking for is operator+ (const char[3], char). That doesn't exist, so the compiler tries letting the array decay to a pointer, so it looks for operator+ (const char[3], char). That exists, so the compiler picks that, but it does the wrong thing. Adding an integral value (the char) to a pointer (the const char*) is a common enough operation, and obviously, that's what this operator+ does. The key to understanding this is to realize that the first argument is 1) an array, and 2) a pointer whenever array doesn't make sense. It was used as a string in C as well, yes, but it is not a string. It is a pointer (or occasionally, an array)

There is a operator+ (const std::string&, char) which concatenates, but the compiler won't even look for it, because the first argument isn't a std::string.

So a solution is to manually create the string:

string name = std::string("ab")+'c';

Now the compiler can figure out the correct operator+ to call.

jalf
A minor remark: "ab" is not const char*, but const char [3].
Nemanja Trifunovic
You're right, of course. Edited my post.
jalf
Voted up, although I think I liked your original better. In C there essentially is no difference between an array and a pointer (except perhaps for allocation/deallocation issues). So its not like it "tries both".
T.E.D.
+1  A: 

A C++ compiler doesn't automatically concatenate string-literals with character-literals. But it will concatenate string-literals with each other. The syntax is like this:

const char * cs = "ab" "c"; // append string-literals

As others have mentioned, string is not a built-in C++ language type. But there is a string type in the C++ Standard Library. Here are some usage examples:

#include <string>
const char * cs = "ab" "c";
std::string s1( cs );
std::string s2( "ab" "c" );
std::string s3 = "ab" "c";
jwfearn
+2  A: 

Given the C++ code:

std::string name =  "ab"+'c';

The equivalent in Java is:

String name = "ab".substring('c');

Both promote char to int. Of course in Java it gets range checked and hence throws an exception. In C++ you just get undefined behaviour (or somesuch).

Tom Hawtin - tackline
It's not undefined. It simply adds the integer value of 'c' to the pointer that points to the statically allocated character array {'a', 'b'}. Of course, the result is undefined, but only because you end up pointing past the array. The actual + operation is perfectly well defined
jalf
I believe the act of having a pointer beyond the array+1 is undefined.
Tom Hawtin - tackline
+1  A: 

Well, what I usually would do in C++ is

string name = string("ab") +'c';

Remember that the literal "ab" is not of type string. What you were doing was hoping that there was no "+" that works between char arrays and chars, and then hoping the compiler could somehow notice that you really want the result to be a std::string, and then go parse your expression on the right hand side for some combination of implicit conversions that could combine with the operator(s) to produce a result of that type. Seems like a rather tall order to me.

Regardless, it doesn't matter. You see, in C the only difference between an array and a pointer is how their memory is allocated. Once you have one, you essentially have an "array/pointer thing". Thus "+" is an operator defined on all arrays and pointers, which takes another argument of any integer type and does pointer math, returning a pointer to that many elements past that spot. Also, in C "char" is really just another kind of integer type. These C design decisions were both useful hacks, but as often happens with hacks, they combine with intuitively unexpected results. So all "ab" + 'c' does for you is return an address 99 bytes past wherever the "ab" literal happens to be stored in memory.

Sometimes you can rely on implicit conversions, but you really have to be prepared to help your compiler out a bit at other times.

T.E.D.