Converting QString to char * fails....
-
I need to convert QString to char * and getting this error .
What am I doing wrong ?
Many thanks for any help.
char * command = text.toStdString().c_str(); text = ProcessCommand_Raw(command, text); qDebug() << text;
/mnt/RAID_124/PROJECTS_MAR15_REG_EXP/CCC_SOURCE/Bluetoothctl/mainwindow_bluetoothctl.cpp:316: error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *'
mainwindow_bluetoothctl.cpp:316:14: error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *'
char * command = text.toStdString().c_str();
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~ -
Christian Ehrlicher Lifetime Qt Championreplied to Anonymous_Banned275 on last edited by Christian Ehrlicher
@AnneRanch said in Converting QString to char * fails....:
What am I doing wrong ?
You're working on a temporary object returned by
text.toStdString()
See also https://forum.qt.io/topic/124936/convert-qstring-pointer-to-char-pointer/9
-
Actually the problem is that OP is trying to implicitly cast away const.
using the temporary pointer value should be OK as long as the owning object exists and isn't modified. thus the reason the returned value needs to be const. It's a protection to keep you from trying to modify the memory it points to.
-
@Kent-Dorfman It will compile when it's const but will crash afterwards or eat kittens...
-
void StrTest() { QString testQStr("Hello World!"); const char* ptr = testQStr.toStdString().c_str(); std::cout << "["; while (*ptr != '\0') { std::cout << *(ptr++); } std::cout << "]" << std::endl; }
Respectfully,
This works fine, unless you modify or destroy testQStr. Only way this should break is if testQStr object is implicitly modified behind the scenes before its destructor is executed, and you then try to use ptr without again resetting ptr by calling toStdString.c_str().If I do testQStr.clear() then reference its data thru ptr then all bets are off though.
The larger argument is whether it is a good idea to access the QString data via raw pointer then pass that pointer to a subfunction...It is not, as it would be better to pass QString by reference to Process_Raw_Command, for the sake of code safety, thus negating the need to return "text" from that function in the first place.
-
@Kent-Dorfman said:
This works fine, unless you modify or destroy testQStr
This is not fine. It's undefined behavior. This is not a question of if
testQStr
exists or not or is modified or not.toStdString()
creates a temporary unnamed object and copies the data fromtestQStr
. Remember that QString holds UTF-16 data andtoStdString()
creates a copy of that data converted to UTF-8, so the finalc_str()
does not return pointer to the original data but to the temporary copy. The temporary std::string is destroyed at;
, so the resultingptr
points to released memory of that temporary.So again -
ptr
does not point to data oftestQStr
. It points to data of the temporary std::string, which is not the same. Accessing it past;
is accessing released memory. Always, no matter if it's const or not. -
@Chris-Kawa said in Converting QString to char * fails....:
So again - ptr does not point to data of
testQStr
. It points to data of the temporary std::string, which is not the same. Accessing it past;
is accessing released memory. Always, no matter if it's const or not.+1. This is what I wanted to say. I don't see what @Kent-Dorfman's comment about
const
has to do with it. It's not just to do with not writing to the data, whereconst
would be relevant, it's that the address returned points to an area of memory which is "no longer valid" after the expression/statement in whichtoStdString()
is used concludes.You can copy the data from the pointer to your own storage or you can pass it as a parameter from the line where it's called, but that's all. OOI, I presume the address it returns/the temporary area is actually on the stack, right? So if it survives after the statement that's just because nothing else has yet re-used that stack area, but it will happen soon.
-
It's all in the thread linked by Christian.
Basically that's ok:someFunction(text.toStdString().c_str());
and that's ok:
const std::string text = text.toStdString(); someFunction(text.c_str());
but this is not ok:
const char* text = text.toStdString().c_str(); someFunction(text); //undefined behavior reading released memory
-
@Kent-Dorfman
I looked at how your code would behave with:QString testQStr1("Hello World!"); const char* ptr1 = testQStr1.toStdString().c_str(); QString testQStr2("Goodbye Universe!"); const char* ptr2 = testQStr2.toStdString().c_str(); ...
Notice the compiler warnings. When run I found it printed the "Hello world" from
ptr1
but the output fromptr2
was empty, just the[]
was printed.Actually, it's funny, I tested further. Remove my two strings/variables, go back to your original code. Instead of your highly fortuitous choice of
Hello World!
I found that"123456789012345"
outputs correctly but"1234567890123456"
prints as empty! The first string is 16 bytes long (including the\0
terminator) while the second one is 17. Ubuntu 22.04, gcc 11.3.0. So something like it "works" up to 16 bytes for the temporary but fails beyond that... :) -
@JonB said in Converting QString to char * fails....:
So something like it "works" up to 16 bytes for the temporary but fails beyond that... :)
No, it compiles on the compiler options, your current memory layout, the os and the moon phase - it's undefined
-
@Christian-Ehrlicher
I am aware of this, as I wrote earlier! I am just trying to illustrate why maybe it worked for @Kent-Dorfman on his original string but demonstrate a simple case where it does not.... I find it "interesting" what something happens around 16 bytes in practice under my OS/gcc, even if you do not :) -
The language spec says it's undefined and that's it.
Consider this as one of gazillion possible scenarios - applications allocate memory in pages and let's say it just so happens that this temporary string is allocated somewhere in the middle of a page used by other things too. The string is deleted, memory is freed, but the page stays resident, because it's still used by that other stuff. Nothing else happens to write there for a while. Cool, seems like you can still read the string and it "works". The OS is not even giving you hard time about it because it's your page after all. But next time your string happens to be allocated at the beginning of a, otherwise empty, page and when the string is deleted the page becomes empty again and gets unmapped. The memory manager gives it to something else, say Mario64 is running in the background and takes that page. Now you have an access violation on the OS level, because you're trying to read an address out of mapped memory space, so hard crash. That's undefined behavior for you. You can probe it however you like and think you have a pattern, but you don't, you just stumbled upon a locally stable transient state, with plethora of variables in the system attached, that can change it.
As for the "works up to 16 bytes" - that's most probably the size of SSO buffer (small string optimization). The strings up to that size use internal buffer, and larger go to the heap.
-
@Chris-Kawa said in Converting QString to char * fails....:
@JonB No experiments you make, even if they are reproducible on your machine, are meaningful. The language spec says it's undefined and that's it.
Why is everyone blasting me?? I am just playing to find a case to illustrate to @Kent-Dorfman which he might try and might show him how it could go wrong even though his test did work Ok for him. Does everyone what to shoot me?!
As for the "works up to 16 bytes" - that's most probably the size of SSO buffer (small string optimization). The strings up to that size use internal buffer, and larger go to the heap.
Exactly this! :)
-
@JonB Come on. You know you like it this way :)
Sorry, I wasn't trying to blast you. Just gave another example. We're on the same page here I think (not in address space sense :P ). -
OK guys. I see your point now. My interprettation of toStdString was that it was an accessor, not a copier: iow, referencing the data directly from within QString. I was mistaken.
I think I'll just stick with std::string...I like it better. ;^)
-
@Kent-Dorfman The problem with std::string is that it's terrible for stuff like paths or any user input. You can technically store an UTF-8 string in it, but it's kinda clunky, because
size()
returns number of elements and iterators go over them too, but some UTF-8 characters will consist of multiple of those, up to 4, so thesize()
method for some UTF-8 strings would return up to 4x the actual length of the string. Searching for multibyte characters in it is also painfull.
For this stuff something like std::wstring could be better, as it could naturally hold most of the useful UTF16 subset, but it would still have the same issue with surrogate characters. Also wchar_t on Windows and Linux has different size so std::wstring is pretty useless, especially for serialization. In C++17 we havestd::filesystem::path
I guess, but strings in C++ are a bit of a mess to be honest. I like QString because it "just works" most of the time. Sure, a bit bloated due to 16bit storage, but very convenient.