6

Interfacing C++11 code to some C callbacks, and I have to pass const char * const *, i.e. an array of strings. Here is a cut-down version of my code:

int main(int,char**){
  const int cnt = 10;
  const char * const * names =
    static_cast<const char * const *>(malloc( sizeof(char*) * cnt));
  //... allocating names[0], etc. coming soon ...
  the_c_function(names);
  free(names);
  return 0;
}

So I worked out how to use malloc in C++, but I'm stuck on free, as it tells me: "invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]"

My first reaction was "Eh? Why do you care, all you have to do is free the pointer." Second reaction was to just cast it away. But this gets rejected by the compiler:

free( const_cast<void*>(names) );

And this does too:

free( static_cast<void*>(acctnames) );

E.g. "invalid static_cast from type ‘const char* const*’ to type ‘void*’".

What does work is a good 'ole C cast:

free( (void*)(acctnames) );

Is that safe, or am I missing something here? (valgrind tells me "All heap blocks were freed -- no leaks are possible", which is some comfort!)

P.S. Using g++ 4.8.1, on Linux.

UPDATE: explanation of why free() wants a non-const pointer is here: Unable to free const pointers in C (though I found barak manos's answer below clearer on that).

Community
  • 1
  • 1
Darren Cook
  • 27,837
  • 13
  • 117
  • 217
  • 2
    The thing is, it doesn't actually matter how you allocate a pointer. All that matters is that a pointer is a pointer is a pointer. So you can safely allocate the pointer(s) with `new[]` or `new`, and pass it to any C-library function. – Some programmer dude May 09 '14 at 09:56
  • I think you have too many `const` specifiers. How are you going to assign a value to the names arrays? `const` dynamically allocated memory makes no sense. The error on the `free` is a clue it's not quite right. – Skizz May 09 '14 at 10:16
  • The "duplicate" covers one aspect (why `free()` takes a non-const pointer), but not the others; I also just modified the title to match the accepted answer better :-) – Darren Cook May 14 '14 at 12:38

2 Answers2

10

const_cast can only remove const and volatile qualifiers; it can't change the pointer type. Conversely, static_cast can change the type, but can't remove top-level qualifiers. However, conversion to void* from a non-const object pointer type is implicit, so this should work:

free( const_cast<const char**>(names) );

More tricky conversions, would require both a const_cast and a static_cast (or even reinterpret_cast in weird cases).

The evil C cast can combine both const_cast and static_cast, so could be used as a shortcut for both. However, that's a bad idea since it's more dangerous: if you specify the wrong type, then it will force the conversion via reinterpret_cast, potentially causing weird runtime behaviour where safer casts would fail at compile time.

However, there's no need for any cast here: the conversion from X* to X const* can be done implicitly, so just change the type of the local pointer:

const char ** names = static_cast<const char **>(malloc( sizeof(char*) * cnt));
the_c_function(names); // OK - adds const
free(names);           // OK - no const to remove

Finally, since this is C++, there's no need to muck around with malloc and pointers at all (unless the C API is evil enough to require that it be given memory from malloc):

std::vector<const char*> names(cnt);
the_c_function(names.data());
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • 1
    +1 for pointing out the alternative, `std::vector`'s contiguous memory is the perfect fit for traditional C API's expecting an array. – Matthieu M. May 09 '14 at 10:27
  • Thanks, both for the explanation, and the `std::vector` `data()` suggestion. I followed the same idea and filled the vector with the `data()` of the `std::string` items. Thereby avoiding not just `malloc` and `free` but also memory copying. And not a cast in sight :-) – Darren Cook May 14 '14 at 12:40
3

In order to support dynamic-memory, a heap is implemented as a linked list of memory blocks, where each block in the list stores the address of the next block in the list.

When you call free(p), it sets the value at address p to the address of the first block in the list, and then sets the pointer of the first block in the list to the value of p.

For example:

static void* _head;
...
void free(void* p)
{
    *(int*)p = (int)_head;
    _head = p;
}

So obviously, the first assignment cannot be applied on const void* p.

Depending on the implementation of the heap, this may be applied either on the first block (_head) or on the last block (_tail). But the principle remains the same, and p cannot be assumed const (meaning that it might be pointing to a read-only memory segment).

barak manos
  • 29,648
  • 10
  • 62
  • 114
  • Thanks, that was helpful to understand what was going on. (It feels like an implementation detail, that should be dealt with by a cast inside the `free()` function, rather than in the interface, but if that is the C standard I'll live with it.) – Darren Cook May 14 '14 at 12:45
  • @Darren Cook: You're welcome :) – barak manos May 14 '14 at 13:14