Search code examples
c++arrayscinitializationc-strings

Is it safe to put '\0' to char[] one after the last element of the array?


I'm interested for some practical reasons. I know C++ adds '\0' after the last element, but is it safe to put it manually ? I heard about undefined behavior, however I'm interested if NULL character is actually the next symbol in the memory?

UPD: I understood, my question is not clear enought without code snippets. So, I'm asking if this is actually save and won't lead to undefined behavior?

const char* a = "Hello";
a[5] = '\0'; //this is wrong, will not compile on most compilers

char* b = new char[5];
b[5] = '\0';

char c[5];
c[5] = '\0';

Solution

  • The snippet

    const char* a = "Hello";
    a[5] = '\0';
    

    does not even compile; not because the index 5 is out of bounds but because a is declared to point to constant memory. The meaning of "pointer to constant memory" is "I declare that I don't want to write to it", so the language and hence the compiler forbid it.

    Note that the main function of const is to declare the programmer's intent. Whether you can, in fact, write to it depends. In your example the attempt — after a const cast — would crash your program because modern compilers put character literals in read-only memory.

    But consider:

    #include <iostream>
    using namespace std;
    
    int main()
    {
        // Writable memory. Initialized with zeroes (interpreted as a string it is empty).
        char writable[2] = {};
    
        // I_swear_I_wont_write_here points to writable memory 
        // but I solemnly declare not to write through it.
        const char* I_swear_I_wont_write_here = writable; 
        cout << "I_swear_I_wont_write_here: ->" << I_swear_I_wont_write_here << "<-\n";
    
        // I_swear_I_wont_write_here[1] = 'A'; // <-- Does not compile. I'm bound by the oath I took.
    
        // Screw yesterday's oaths and give me an A.
        // This is well defined and works. (It works because the memory
        // is actually writable.)
        const_cast<char*>(I_swear_I_wont_write_here)[0] = 'A';
    
        cout << "I_swear_I_wont_write_here: ->" << I_swear_I_wont_write_here << "<-\n";
    }
    

    Declaring something const simply announces that you don't want to write through it; it does not mean that the memory concerned is indeed unwritable, and the programmer is free to ignore the declaration but must do so expressly with a cast. The opposite is true as well, but no cast is needed: You are welcome to declare and follow through with "no writing intended here" without doing any harm.