tags:

views:

187

answers:

5

Okay, I am trying to integrate some C code into a C++ project, and have run into a few problems. I will detail the first one here.

I keep running into this error:

error: cannot convert 'char*' to 'char**' in assignment|

here is the offending code (with the breakpoint marked):

char** space_getFactionPlanet( int *nplanets, int *factions, int nfactions )
{
   int i,j,k;
   Planet* planet;
   char **tmp;
   int ntmp;
   int mtmp;

   ntmp = 0;
   mtmp = CHUNK_SIZE;
   tmp = malloc(sizeof(char*) * mtmp); <--- Breakpt

The malloc function is derived from a C header. Here is the declaration:

_CRTIMP void* __cdecl __MINGW_NOTHROW    malloc    (size_t) __MINGW_ATTRIB_MALLOC;

I am using codeblocks, which is set to use MinGW. The above syntax is totally foreign to me.

I am totally stumped, since this code works fine in the C program I took it from.

Any Ideas?

EDIT 1:

Oops, just realized that the declaration is from stdlib.h.

EDIT 2:

I tried:

tmp = static_cast<char **>(malloc(sizeof(char*) * mtmp));

As suggested, but not I get error: invalid static_cast from type 'char*' to type 'char**'.

EDIT 3:

Okay, reinterpret_cast works, but the solution to replace mallocs seems much more elegantly simple, so I am going with that.

However, there is no free(tmp) at the end of the function. Is this a problem if I don't put in a delete tmp[]?

EDIT 4: I should add that tmp is returned by the function, so it is neccesary to delete tmp, or is this automatic?

Okay I am marking this solved. Thanks for your help.

+5  A: 

C++ is not so free with pointer type conversions. You will have to do something like this:

tmp = static_cast<char **>(malloc(sizeof(char *) * mtmp));

That will work if your malloc() returns a void*. However, the errors you're getting indicate that your malloc() is declared to return a char*, and in this case you'd have to use reinterpret_cast instead:

tmp = reinterpret_cast<char **>(malloc(sizeof(char *) * mtmp));

This casts the return type of malloc() to a type suitable for assignment into tmp. You can read more about the different types of casts in C++ at Type Casting.

Note that if this code must still compile in C, you can use a C-style cast instead:

tmp = (char **)malloc(sizeof(char *) * mtmp);
Greg Hewgill
I don't really care about backward compatibility.
Biosci3c
Also, what exactly is that code doing? Is it changing the pointer to a 'char' into a pointer to a pointer to a char?
Biosci3c
+1 nice. (more characters)
JoshD
Tried the first block of code, and it now says invalid static_cast from type 'char*' to type 'char**'
Biosci3c
+1 that's the answer
Alexander Rafferty
@Biosci3c: try `reinterpret_cast` instead of `static_cast`. I'll change the answer.
Greg Hewgill
Bingo, works fine. However, replacing mallocs seems like a more elegant solution.
Biosci3c
`static_cast` should work fine for converting from `void*` (and should be preferred). It sounds like Biosci3c's `malloc` has a non-standard prototype that returns `char*` instead of `void*`, however.
jamesdlin
+1  A: 

Perhaps a cast is in order:

tmp = static_cast<char **>(malloc(sizeof(char*) * mtmp));
JoshD
`static_cast` is preferable to `reinterpret_cast` when converting to/from `void*`.
jamesdlin
@jamesdlin: Thank you for pointing that out. I've changed the answer to avoid spreading bad information.
JoshD
+4  A: 

If you're converting to C++, then let's do away with the malloc shall we?

tmp = new char*[mtmp];

But later on, you will probably find something like this:

free(tmp);

Which you need to change to this:

delete [] tmp;

If tmp is returned from the function, do not delete it. You need to trace the pointer. Somewhere, perhaps in multiple locations, free is going to be called. You need to replace that with delete if you are going with this solution. However, an even better solution would be to ditch the pointers all together, and replace it with a

vector<string>
PigBen
Bless you! Malloc seems unnecessarily annoying.
Biosci3c
Actually, in this function, there is no free(tmp), but there is: tmp = realloc(tmp, sizeof(char*) * mtmp);
Biosci3c
Thanks. I have a question about realloc, but I'll save that for another post.
Biosci3c
+2  A: 

malloc(3) returns void*, which could be assigned to any other pointer type in C, but not in C++. This is one of the places where C++ is not backward-compatible with C. You can either do what other posters said - cast it to char**, or go all the way to proper C++ free store usage with operators new[] and delete[]:

char** tmp = new char*[mtmp]; // was malloc(sizeof(char*) * mtmp);
// ... use tmp
delete [] tmp; // was free( tmp );
Nikolai N Fetissov
+1  A: 

Change tmp = malloc(sizeof(char*) * mtmp); to:

 tmp = (char**)malloc(sizeof(char*) * mtmp);

And don't go around changing malloc's to new's, as some have wrongly suggested, because of two reasons:

1) its not broken, so don't fix it (!)

2) you could introduce subtle or flat out horrible bugs that will eat away hours of your life later on.

Do spend your time fixing broken code, not code that works...

/edit: reply to comment:

I don't care :) Because I make a living fixing other people's buggy "modern" OOP code. All their arguments are moot; if they were right I wouldn't have a job. I'm sure someone will come along and educate me how I should have used this or that other type of cast; fine, whatever, I'll give you that. But advising people to change a crucial part of your code (how you allocate memory) based on a tiny snippet is grounds for declaring that person insane. You said yourself that the function doesn't free that pointer, so what if another function realloc's or free's that pointer later on? You would have to go trough a huge chunk of the codebase making changes to the code, introducing new subtle bugs, and generally breaking code that works, all in the name of fanboyism, as if there is something wrong with malloc, as if your computer will randomly segfault when you call malloc... it is beyond ridiculous, and only a naive developer who has no touch with the real world would suggest something like that. It is like throwing away your perfectly good car because its color is no longer popular. If you need constructors/destructors called, use new/delete, otherwise it's best to keep things simple. So let them flame, I don't care.

raicuandi
My friend, I think you are going to get flamed, considering the above answers :P
Biosci3c
I understand that I could "make it work," but the vector approach seems more streamlined.
Biosci3c
What do you mean? You just wanted to get the code to compile, all you need is a cast. If that code resizes the array up, though, and works with a high enough array, something like vector might be better. Personally, I would just add the cast and call it a day.
raicuandi
Even though I suggested switching to use new/delete, that was before I knew that he returned the array from the function. So I would actually agree with you on that. I would however, approve an update to use a vector of strings, even though it might be a lot of work. Since the compiler obviously won't allow him to call free or realloc on a vector, the bugs should be easy to find.
PigBen
I'm overly aggresive from quitting smoking recently... sory :P
raicuandi