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.
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);
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>
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 );
Perhaps a cast is in order:
tmp = static_cast<char **>(malloc(sizeof(char*) * mtmp));
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...
Related
I am trying to print the following using the code as:
int main() {
char p[] = "hello";
char*t = p;
char**b = &t;
std::string s = b[0];
cout<<"string="<<s<<" b[0]="<<b[0];
}
The output I get is as expected:
string=hello b[0]=hello
Now the same thing I want to achieve using a function call in which I pass a variable by reference as:
void func(char**& p) {
char *k = (char*)malloc(8*sizeof(char));
k = (char*)"abc";
p = &k;
}
int main() {
char**j = NULL;
func(j);
std::string s1 = j[0];
cout<<s1;
}
Now I am getting a null string. What could be the reason and how to solve it ?
You have (at least) two problems:
The first is that you make p point to the local variable k.
The second is more subtle, and is that you reassign k losing the original memory you allocated and make k point to.
Then the usual spiel about never using arrays of char or pointers to char for strings, when you have std::string. And that you should never use malloc in C++, only new or new[]. And if you need new[] consider using std::vector instead (except for strings of course).
There is NO WAY to write code like this that follows any sort of standards of good programming. If you are serious about learning how to program you should abandon this mess and rewrite your code to do things the correct way. This is sort of mess is never necessary, even when interfacing with legacy code.
However the following 'works'
void func(char**& p) {
p = new char*;
*p = new char[8];
strcpy(*p, "abc");
}
And take note, I didn't need to use a cast.
Context:
I try to avoid vectors and replace it with smart pointers as an exercise.
The goal is to benefit from smart pointers to avoid memory leaks without relying on vectors, because that is what I want to try now.
The codes below is just a mean to be easily understood.
I want my c++ code to work.
Update : I would like to stick as much as possible with raw c'ish style for IO: please no std::string or c++ streams in general.
The codes:
C version (working):
typedef struct{
char ** my_arr;
} MyInputs;
...
MyInputs *Doc = malloc(sizeof *Doc);
*Doc->my_arr = malloc(sizeof (*Doc->my_arr) * 2);
Doc->my_arr[0] = malloc(10);
Doc->my_arr[1] = malloc(10);
// Yes, that is stupid to alloc 10 bytes and use only 6 or 5. That is for the example.
memcpy(Doc->my_arr[0],(char*)"Hello\0",6);
memcpy(Doc->my_arr[1],(char*)"Cool\0",5);
printf("%s %s \n",Doc->my_arr[0],Doc->my_arr[1] );
...
C++ version (attempt):
typedef struct {
std::shared_ptr<char*>my_arr;
}MyInputs;
...
std::shared_ptr<MyInputs> MainDoc (static_cast<MyInputs*>(malloc(sizeof (*MainDoc))),free);
std::shared_ptr<char*> Z (static_cast<char**>(malloc(sizeof (**MainDoc->my_arr) * 10)),free);
std::shared_ptr<char> Z[0](static_cast<char*>(malloc(sizeof (char *) * 10)),free);
memcpy(Z[0].get(), (char*)"salut\0", 6);
cout << Z[0] << endl;
...
The obvious:
In the c++ version, the compiler complains about Z and Z[0] being the same and that there is not match for operator [] in the cout.
Well, well, well...
Any ideas that could make the c++ code work that way ? (and again, I know about vectors).
If you insist on using the wrong tool for the job, then you'll want each inner pointer to also be a smart pointer:
shared_ptr<shared_ptr<char>> my_arr;
You can't simply use malloc to create an array of non-trivial types like shared_ptr; use new:
my_arr.reset(new shared_ptr<char>[10], [](shared_ptr<char> * p) {delete [] p;});
Then you can assign or reset the elements. shared_ptr doesn't support [], since it's generally not used for arrays, so use get() to get a pointer to the first element:
my_arr.get()[0].reset(new char[10], [](char * p) {delete [] p;});
Or just replace this gibberish with something sensible
std::vector<std::string> my_arr {"Hello", "Cool"};
In writing a response, I wrote some code that challenged my assumptions on how const pointers work. I had assumed const pointers could not be deleted by the delete function, but as you'll see from the code below, that isn't the case:
#include <new>
#include <string.h>
class TestA
{
private:
char *Array;
public:
TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
~TestA(){if(Array != NULL){ delete [] Array;} }
char * const GetArray(){ return Array; }
};
int main()
{
TestA Temp;
printf("%s\n",Temp.GetArray());
Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
Temp.GetArray()[1] = ' ';
printf("%s\n",Temp.GetArray());
//Temp.GetArray() = NULL //This doesn't work
delete [] Temp.GetArray(); //This works?! How do I prevent this?
}
My question is, how do I pass the user access to a pointer (so they can use it like a char array), whilst making it so that the delete function cannot delete it, by preferably throwing some sort of complaint or exception?
If your users are using delete[] on pointers they didn't get from new[], hit them upside the head with a clue bat.
There are so many reasons a pointer can be dereferenced but mustn't be passed to delete:
Someone else will delete it.
It's not the beginning of the block.
It came from malloc or some other non-new allocator.
It has static, not dynamic lifetime.
If has automatic lifetime.
Some of these will manifest in exceptions at runtime. Others will cause crashes at some later time. All are "undefined behavior" according to the standard.
The assumption should be that a pointer cannot be used as the argument to delete, unless explicitly stated otherwise.
If entry-level programmers are making this mistake, educate them. If "experienced" developers are doing it, fire them for lying on their resume.
If you just want it to hurt when they do that, allocate the array one larger than necessary, and return Array + 1;. Now everything will blow up if they try to use delete[].
The practical use is that it's (more) likely to make the program crash inside the bogus call to delete, with the offending function still on the call stack. Where the original will probably continue running for a while, and finally crash in innocent code. So this helps you catch stupid users.
delete [] Temp.GetArray(); //This works?! How do I prevent this?
As long as, it returns char* or some other pointer type, you cannot prevent it; it doesn't matter if the expression in delete statement is const, because all of the following are perfectly valid in C++:
char *pc = f();
delete [] pc; //ok
const char *pc = g();
delete [] pc; //ok
char * const pc = h();
delete [] pc; //ok
const char * const pc = k();
delete [] pc; //ok
However, if you change this :
char *Array;
to this
std::vector<char> Array;
And then you could achieve what you want, by returning it as:
std::vector<char> & GetArray() { return Array; }
The bottomline is :
In C++, the default choice for dynamic array should be std::vector<T> unless you've a very strong reason not to use it.
Your const is doing no effect, you are returning a const pointer, not a pointer to const char, which is what you want. Also see Nawaz recommendation on using vectors instead.
The most you can really do is to return something that is not an acceptable operand for delete. This can be an RAII object (like std::vector or std::array) or it can be a reference (depending on your situation, either could be appropriate).
[Un]fortunately, C++ lets the programmer do all sorts of sneaky things.
You cannot block stupidity entirely, but you can stifle it a tad by using an access mechanism instead of returning the actual array pointer.
char& operator[](size_t index) { return Array[index]; }
This does not address the ability to treat it like a char array, but as has been pointed out, if you reveal that pointer, (bad) programmers a free to run delete on it all they want.
I am interested in Judy Arrays and try to use it. But i had unable to do any useful thing using it. Every time it gives me casting errors.. Sample c++ code and the error given below.
#include "Judy.h"
#include <iostream>
using namespace std;
int main()
{
int Rc_int; // return code - integer
Word_t Rc_word; // return code - unsigned word
Word_t Index = 12, Index1 = 34, Index2 = 55, Nth;
Word_t PValue; // pointer to return value
//Pvoid_t PJLArray = NULL; // initialize JudyL array
Pvoid_t JudyArray = NULL;
char String[100];
PWord_t _PValue;
JSLI( JudyArray, _PValue, (uint8_t *) String);
return(0);
} // main()
This gives me the error
m.cpp: In function ‘int main()’:
m.cpp:19: error: invalid conversion from ‘long unsigned int**’ to ‘void**’
m.cpp:19: error: initializing argument 1 of ‘void** JudySLIns(void**, const uint8_t*, J_UDY_ERROR_STRUCT*)’
Please anyone help me to figure out what is the error what i'm doing..
Thanks
According to the documentation, you have the _PValue and JudyArray parameters reversed. Make your call look like this:
JSLI( _PValue, JudyArray, (uint8_t *) String);
Also, try not compiling it as C++ code. So far, your test uses no C++ features. I bet it will compile as C code. It looks like JudyArray relies on the fact that C will do certain kinds of implicit conversions between void * and other pointer types.
If this is the case, I'm not sure what to do about it. The error messages you're getting tell me that JSLI is a macro. In order to fix the error message you have in the comments on this answer, you'd have to reach inside the macro and add a typecast.
These kinds of implicit conversions are allowed in C because otherwise using malloc would always require ugly casts. C++ purposely disallows them because the semantics of new make the requirement that the result of malloc be cast to the correct type unimportant.
I don't think this library can be used effectively in C++ for this reason.
It seems that, you pass JudySLIns(void**, const uint8_t*, J_UDY_ERROR_STRUCT*) a wrong parameter, the first one, you'b better check it!
For integer keys there is a C++ wrapper at http://judyhash.sourceforge.net/
Let's say I have a macro called LengthOf(array):
sizeof array / sizeof array[0]
When I make a new array of size 23, shouldn't I get 23 back for LengthOf?
WCHAR* str = new WCHAR[23];
str[22] = '\0';
size_t len = LengthOf(str); // len == 4
Why does len == 4?
UPDATE: I made a typo, it's a WCHAR*, not a WCHAR**.
Because str here is a pointer to a pointer, not an array.
This is one of the fine differences between pointers and arrays: in this case, your pointer is on the stack, pointing to the array of 23 characters that has been allocated elsewhere (presumably the heap).
WCHAR** str = new WCHAR[23];
First of all, this shouldn't even compile -- it tries to assign a pointer to WCHAR to a pointer to pointer to WCHAR. The compiler should reject the code based on this mismatch.
Second, one of the known shortcomings of the sizeof(array)/sizeof(array[0]) macro is that it can and will fail completely when applied to a pointer instead of a real array. In C++, you can use a template to get code like this rejected:
#include <iostream>
template <class T, size_t N>
size_t size(T (&x)[N]) {
return N;
}
int main() {
int a[4];
int *b;
b = ::new int[20];
std::cout << size(a); // compiles and prints '4'
// std::cout << size(b); // uncomment this, and the code won't compile.
return 0;
}
As others have pointed out, the macro fails to work properly if a pointer is passed to it instead of an actual array. Unfortunately, because pointers and arrays evaluate similarly in most expressions, the compiler isn't able to let you know there's a problem unless you make you macro somewhat more complex.
For a C++ version of the macro that's typesafe (will generate an error if you pass a pointer rather than an array type), see:
Compile time sizeof_array without using a macro
It wouldn't exactly 'fix' your problem, but it would let you know that you're doing something wrong.
For a macro that works in C and is somewhat safer (many pointers will diagnose as an error, but some will pass through without error - including yours, unfortunately):
Is there a standard function in C that would return the length of an array?
Of course, using the power of #ifdef __cplusplus you can have both in a general purpose header and have the compiler select the safer one for C++ builds and the C-compatible one when C++ isn't in effect.
The problem is that the sizeof operator checks the size of it's argument. The argument passed in your sample code is WCHAR*. So, the sizeof(WCHAR*) is 4. If you had an array, such as WCHAR foo[23], and took sizeof(foo), the type passed is WCHAR[23], essentially, and would yield sizeof(WCHAR) * 23. Effectively at compile type WCHAR* and WCHAR[23] are different types, and while you and I can see that the result of new WCHAR[23] is functionally equivalent to WCHAR[23], in actuality, the return type is WCHAR*, with absolutely no size information.
As a corellary, since sizeof(new WCHAR[23]) equals 4 on your platform, you're obviously dealing with an architecture where a pointer is 4 bytes. If you built this on an x64 platform, you'd find that sizeof(new WCHAR[23]) will return 8.
You wrote:
WCHAR* str = new WCHAR[23];
if 23 is meant to be a static value, (not variable in the entire life of your program) it's better use #define or const than just hardcoding 23.
#define STR_LENGTH 23
WCHAR* str = new WCHAR[STR_LENGTH];
size_t len = (size_t) STR_LENGTH;
or C++ version
const int STR_LENGTH = 23;
WCHAR* str = new WCHAR[STR_LENGTH];
size_t len = static_cast<size_t>(STR_LENGTH);