Consider this code:
#include <memory.h>
#include <stdlib.h>
void Foo()
{
const char ** caps = malloc( sizeof( char * ) );
memset( caps, 0, sizeof( char * ) );
}
Compiles fine with gcc 4.9.2 -pedantic but cl 18 (the one from VS2013) with default options says warning C4090: 'function' : different 'const' qualifiers
on the memset line.
Now caps
is a pointer to a pointer to const char? So the pointer itself is not const hence it should convert tovoid*
without problems, I think, but cl seems to automatically make const void*
from it which generates the warning. Is that a correct explanation of what is going on? And this is non-standard behaviour, right?
The compiler is overzealous (read as: compiler bug).
const char** caps
means that caps
is a pointer (which is not constant) to a different pointer (which is not constant either) to a char
which is constant. That is, you promise not to modify that char
via indirections that go over caps
.
This means that you formally make the following contract with the compiler:
caps
.*caps
(the char*
that caps
points to).**caps
(the char
that *caps
points to (which caps
points to)) via this chain pointers.Nothing is said about anyone else (e.g. aliasing pointers) changing the value of that character.
const char **caps = malloc( sizeof( char * ) );
initializes caps
with a value, which is legal. In case malloc
fails, this value is a null pointer, but this, too, is generally perfectly legal from the point of view of the language (though it would cause the following memset
to crash). In C++ you would be required to explicitly cast the void*
returned by malloc
, but C allows that kind of thing just fine.
memset(caps, 0, sizeof(char*));
makes my hair stand up (me being a C++ programmer), but it's nevertheless a perfectly legal thing from the point of view of the C language.
What it does is overwrite the so far allocated but uninitialized (and pointed-to by caps
) block of memory which contains the second pointer with a number of zero bytes equal to the size of a pointer (a char
pointer, as it happens).
The library function memset
, which takes a non-const void*
, simply fills the number of bytes you ask (here sizeof(char*)
) with the value you provide (here: zero). It does not, and does not need to care, about the contract that you made with the compiler. But even so, it isn't violating any rules. It overwrites the pointer, not the pointed-to constant value.
Yes, it writes a couple of char
values into something that isn't an array of char
s (which is why my hair stands up), but well... that's... legal. It's just what memset
is supposed to do, after all, and it will most probably "work as expected". It will set the pointer to a zero bit pattern, which -- except on some very rare exotic architectures -- corresponds to a null pointer.
At no point was the memory location **caps
changed (or even accessed), so all of this is perfectly legal, you did not break any of your promises.
The warning is therefore wrong.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With