|
Memory leak with const char* arrays |
Posted by Medic89 on Apr-19-2023 00:16 |
|
Hello Peter,
in my App, i have const char* arrays to hold the data for my charts, I use vector const char* arrays.
But when I push_back some new data, everytime I have a memory leak, one example:
my vector array with data type const char* is names TableTot. I have data in the CString format, that I want to push_back into the vector array. So I use:
CString Data = L"Test";
TableTot.push_back(WCHARtoUTF8(Data));
obviously this doesn't work, because WCHARtoUTF8() retrieves a pointer and not the data itself. But when I use the strdup() function like this:
TableTot.push_back(strdup(WCHARtoUTF8(Data)));
I get a memory leak. Do you have any suggestions on how to fix that? |
Re: Memory leak with const char* arrays |
Posted by Peter Kwan on Apr-19-2023 02:03 |
|
Hi Medic89,
Whenever you use strdup, you must free the data. To fix the problem, the easiest way is to free the data after use.
for (int i = 0; i < TableTot.size(); ++i)
free(TableTot[i]);
Best Regards
Peter Kwan |
Re: Memory leak with const char* arrays |
Posted by Medic89 on Apr-20-2023 03:01 |
|
In this case, this doesn't work, with your solution I get an error message in VC (const char* argument does not fit in here because *void is expected), apart from that, I want to use the the array TableTot later in my function to fill my chart with data. |
Re: Memory leak with const char* arrays |
Posted by Peter Kwan on Apr-21-2023 00:41 |
|
Hi Medic89.
You should free the data only when the data in TableTot is no longer needed. If you still need the data in TableTot in the future, of course you should not free it. It is inevitable that after some time the TotalTot will not longer be needed. For example, if the application shuts down, the TotalTot will no longer be needed. In any case, you should free the data when TotalTot is no longer needed.
In many of our sample code, we free the data in the destructor of the class that contains the data. For example, in the "Simple Zooming and Scroliing" sample code, we free the memory in ~CSimpleZoomScrollDlg.
https://www.advsofteng.com/doc/cdcpp.htm#simplezoomscroll.htm
For the "const char*" error, simply cast the pointer to "void *". For example:
for (int i = 0; i < TableTot.size(); ++i)
free((void*)(TableTot[i]));
The "const char*" occurs because your TableTot is declared to hold "const char*". The "const" means you declare that the data cannot be change. That is why the compiler cannot cast it to "void*" to free it. But you know the data can be freed when TableTot is no longer needed. So you can cast it to "void*".
Best Regards
Peter Kwan |
Re: Memory leak with const char* arrays |
Posted by Medic89 on Apr-21-2023 18:00 |
|
Thank you for your response Peter!
I tried to Free TableTot, but without success. In my App, the chart gets build every ten seconds, at the beginning of the building I clear the TableTot vector using the clear() function, later in the code, I build TableTot again with the updated data. I tried to use free() bevore calling clear(), but I get an error message (access violation at 0x000000000). |
Re: Memory leak with const char* arrays |
Posted by Peter Kwan on Apr-21-2023 22:03 |
|
Hi Medic89,
The 0x000000000 is the null pointer. One possible reason for the error message is that your table contains null pointer. The code tries to free a null pointer and cause the error.
After you build the table, please check if it contain null pointers. I suggest something like:
#include <assert.h> // put this at the top of your code so you can use assert
// assume the table is built here
TableTol.clear();
for (int i = 0; i < TableTot.size(); ++i)
{
.... get your Data for the string ....
// Confirm the string is not null
assert(Data != 0);
TableTol.push_back(WCHARtoUTF8(Data));
}
// check if there is any null pointer
for (int i = 0; i < TableTot.size(); ++i)
assert(TableTot[i] != 0);
In "Debug" mode, the assert statement will cause the code to crash immediately if the condition is not true. This is a useful debugging tool, the code stops right at the point of verification. The assertion is also useful code documentation, as it describes what you think the code should be doing.
The immeidately before freeing the memory, check again.
// check if there is any null pointer
for (int i = 0; i < TableTot.size(); ++i)
assert(TableTot[i] != 0);
// check if there is any null pointer
for (int i = 0; i < TableTot.size(); ++i)
free(TableTot[i]);
// can clear the table now
TableTot.clear();
Please let me know if you see any assertion.
Best Regards
Peter Kwan |
Re: Memory leak with const char* arrays |
Posted by Medic89 on Apr-25-2023 00:15 |
|
Hi Peter,
I get an instant assertion fail at the beginning of the chart building process. Maybe I'm using the vector array not properly:
The data for my chart goes from the dialog window into a vector array and from there my TableTot vector gets filled with information through the chart building process, after I cleared TableTot at the beginning of the chart building. |
Re: Memory leak with const char* arrays |
Posted by Peter Kwan on Apr-25-2023 05:29 |
|
Hi Medic89,
Sorry. For the code in my last email, the line:
TableTol.push_back(WCHARtoUTF8(Data));
should be:
TableTol.push_back(strdup(WCHARtoUTF8(Data)));
For testing, please do not use your actual labels. Instead, replace all the labels with L"Test". Whenever you need a label, please use:
CString TestStr = L"Test";
const wchar_t *Data = (const wchar_t*)TestStr;
assert(Data != 0);
TableTot.push_back(strdup(WCHARtoUTF8(Data)));
If it works, all your labels should become "Test". In this case, please try to use your actual labels. If the "Test" label works, but your actual labels do not, the issue may be is with the way to build the actual labels. For example, the actual labels may contain null pointers.
Regards
Peter Kwan |
Re: Memory leak with const char* arrays |
Posted by Medic89 on Apr-26-2023 19:44 |
|
OK interesting, I always use the WCHARtoUTF8 function, to transfer my CString directly to the const char* array. |
Re: Memory leak with const char* arrays |
Posted by Peter Kwan on Apr-27-2023 00:56 |
|
Hi Medic89,
WCHARtoUTF8 can only accepts "const wchar_t*" as input. See:
https://www.advsofteng.com/doc/cdcpp.htm#WCHARtoUTF8.htm
The CString has implicit conversion operators to and from TCHAR*. TCHAR* is equivalent to wchar_t* if your code is compiled to use Unicode MFC (as opposed to MBCS). That's why you can assign L"ABC" to CString without error. That is also why you can pass CString to WCHARtoUTF8 without error as the CString will implicitly convert the text string to wchar_t* before passing the text string to WCHARtoUTF8.
In the code I suggested, I put the implicit conversion in a separate line so that we can verify if the converted text string is valid.
const wchar_t *Data = (const wchar_t*)TestStr;
assert(Data != 0);
Note that WCHARtoUTF8 is not a text string. It is an object. (See the documentation link above.) It too has an implicit conversion operator to "const char*". That's why you can add it to TotalTot, which requires "const char *". However, like CString, the converted "const char*" is valid only if the object WCHARtoUTF8 is not destroyed. If it is used like in your code "TableTot.push_back(strdup(WCHARtoUTF8(Data)));", the WCHARtoUTF8 is inline and will be destroyed immediately after executing the line. The pointer will then become invalid. That's why you need to use strdup to create a new text string pointer and copy the text string to that pointer.
The "TableTot.push_back(strdup(WCHARtoUTF8(Data)));" is equivalent to the following lines:
// Create the object
WCHARtoUTF8 myString(Data);
// Invoke the implicit conversion operator to convert to "const char*".
const char *str = (const char *)myString;
// The "str" is still valid here as the myString still exists.
TableTot.push_back(strdup(str));
Best Regards
Peter Kwan |
|