Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert const char** to void*? [duplicate]

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?

like image 752
stijn Avatar asked Mar 16 '15 11:03

stijn


1 Answers

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:

  1. You are allowed to change caps.
  2. You are allowed to change *caps (the char* that caps points to).
  3. You are not allowed to change **caps (the char that *caps points to (which caps points to)) via this chain pointers.
  4. 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 chars (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.

like image 79
Damon Avatar answered Sep 29 '22 17:09

Damon