c++ char * initialization in constructor

22,551

Solution 1

Why won't Test(const char * c) : name(c) {} work? Because char * name isn't const?

Correct.

how does this initialization work: char * name = "Peter";

A C++ string literal is of type char const[] (see here, as opposed to just char[] in C, as it didn't have the const keyword1). This assignment is considered deprecated in C++, yet it is still allowed2 for backward compatibility with C.

Test(char * c) : name(c) { c[0] = 'a'; } crashes the program. Why?

What are you passing to Test when initializing it? If you're passing a string literal or an illegal pointer, doing c[0] = 'a' is not allowed.


1 The old version of the C programming language (as described in the K&R book published in 1978) did not include the const keyword. Since then, ANSI C borrowed the idea of const from C++.
2 Valid in C++03, no longer valid in C++11.

Solution 2

A conversion to const is a one-way street, so to speak.

You can convert from T * to T const * implicitly.

Conversion from T const * to T * requires an explicit cast. Even if you started from T *, then converted to T const *, converting back to T * requires an explicit cast, even though it's really just "restoring" the access you had to start with.

Note that throughout, T const * and const T * are precisely equivalent, and T stands for "some arbitrary type" (char in your example, but could just as easily be something else like int or my_user_defined_type).

Initializing a char * from a string literal (e.g., char *s = "whatever";) is allowed even though it violates this general rule (the literal itself is basically const, but you're creating a non-const pointer to it). This is simply because there's lots of code that depends on doing this, and nobody's been willing to break that code, so they have a rule to allow it. That rule's been deprecated, though, so at least in theory some future compiler could reject code that depends on it.

Since the string literal itself is basically const, any attempt at modifying it results in undefined behavior. On most modern systems, this will result in the process being terminated, because the memory storing the string literal will be marked at 'read only'. That's not the only possible result. Just for example, back in the days of MS-DOS, it would often succeed. It could still have bizarre side-effects though. For one example, many compilers "knew" that string literals were supposed to be read-only, so they'd "merge" identical string literals. Therefore if you had something like:

char *a = "Peter"; a[1] = 'a';

char *b = "Peter";

cout << b;

The compiler would have "merged" a and b to actually point at the same memory -- so when you modified a, that change would also affect b, so it would print out "Pater" instead of "Peter".

Note that the string literals didn't need to be entirely identical for this to happen either. As long as one was identical to the end of another, they could be merged:

char *a = "this?";
char *b = "What's this?";

a[2] = 'a';
a[3] = 't';

cout << b; // could print "What's that?"

Mandating one behavior didn't make sense, so the result was (and is) simply undefined.

Solution 3

First of all this is C++, you have std::string. You should really consider using it.

Regarding your question, "Peter" is a char literal, hence it is unmodifiable and surely you can't write on it. You can:

  • have a const char * member variable and initialize it like you are doing name(c), by declaring "Peter" as const
  • have a char * member variable and copy the content, eg name(strdup(c)) (and remember to release it in destructor.
Share:
22,551
tuks
Author by

tuks

Updated on September 25, 2020

Comments

  • tuks
    tuks over 3 years

    I'm just curious, I want to know what's going on here:

    class Test
    {
    char * name;
    public:
    Test(char * c) : name(c){}
    };
    

    1) Why won't Test(const char * c) : name(c){} work? Because char * name isn't const? But what about this:

    main(){
    char * name = "Peter";
    }
    

    name is char*, but "Peter" is const char*, right? So how does that initialization work?

    2) Test(char * c) : name(c){ c[0] = 'a'; } - this crashes the program. Why?

    Sorry for my ignorance.