I am not sure which book covers exactly those things. There is mention in the literature, but my info on some aspects is built from variety of sources, including courses I had, books I read quite a while ago, etc.
Here is getVal1
1 2 3 4 5
|
char* getVal1()
{
char c[] = "1234";
return c;
}
|
What the compiler does under the hood is to allocate some static storage that will be filled with the initialization string. Then, when the function is called, space for the c array is locally allocated on the stack and this space is filled with copy of this initialization string.
It looks like this after compilation:
1 2 3 4 5 6 7
|
char* getVal1_compiler_output()
{
static const char c_init[] = "1234";
char c[sizeof(c_init)];
memcpy(c, c_init, sizeof(c_init));
return c;
}
|
The problem is part syntax, part semantics. The syntax part is that when the name of some array (in this case c) is used in a context where a pointer is required, the name is converted to pointer to the first array element. So, you return pointer to the storage allocated for the array c on the stack. This storage is destroyed when the function returns, but the pointer to the storage is returned anyways. So, now you have a dangling pointer that you can use to access some memory that can be overwritten by the next function call. This is the bug. You access memory that was re-assigned to some other object that lives there now, and stores who-knows-what there.
About getVal2:
1 2 3 4 5
|
const char* getVal2()
{
const char* c = "1234";
return c;
}
|
Again, under the hood, the compiler uses some static memory for the initializing string. But the string is not used to fill the contents of some array allocated on the stack. It is simply used to point some pointer to it:
1 2 3 4 5 6
|
const char* getVal3()
{
static const char c_init[] = "1234";
const char* c = c_init;
return c;
}
|
Character arrays are the objects themselves. Character pointers are just used to refer to those objects. In getVal1, the static array is used as prototype for all stack objects. It is used as their initial filler. In getVal2, the static array is directly referred to using a pointer. It is not a prototype value then, it is something the pointer simply refers to. Because the static array object will never "die" (because it has static storage duration and will exist until the program terminates), the returned pointer will remain valid. It is copy of the local pointer, but this is irrelevant. Once you copy a pointer, you can destroy the original pointer, so long as the pointed object remains alive. This is not the case with getVal1, where the returned pointer is to the stack object.
EDIT: In resume of the above paragraph. getVal1 returns pointer to the first element of the array c allocated on the stack. c is destroyed and the pointer is dangling. getVal2 returns pointer to the first element of c_init. c_init is not destroyed and the pointer can be used as long as it has to be.
Regards