Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Unsolved Converting QString to char * fails....

    C++ Gurus
    5
    16
    124
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • A
      AnneRanch last edited by

      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 1 Reply Last reply Reply Quote 0
      • Christian Ehrlicher
        Christian Ehrlicher Lifetime Qt Champion @AnneRanch 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

        Qt has to stay free or it will die.

        1 Reply Last reply Reply Quote 2
        • Kent-Dorfman
          Kent-Dorfman last edited by

          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.

          Christian Ehrlicher 1 Reply Last reply Reply Quote 0
          • Christian Ehrlicher
            Christian Ehrlicher Lifetime Qt Champion @Kent-Dorfman last edited by

            @Kent-Dorfman It will compile when it's const but will crash afterwards or eat kittens...

            Qt has to stay free or it will die.

            1 Reply Last reply Reply Quote 2
            • Kent-Dorfman
              Kent-Dorfman last edited by

              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.

              Chris Kawa JonB 2 Replies Last reply Reply Quote 0
              • Chris Kawa
                Chris Kawa Moderators @Kent-Dorfman last edited by Chris Kawa

                @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 from testQStr. Remember that QString holds UTF-16 data and toStdString() creates a copy of that data converted to UTF-8, so the final c_str() does not return pointer to the original data but to the temporary copy. The temporary std::string is destroyed at ;, so the resulting ptr points to released memory of that temporary.

                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.

                JonB 1 Reply Last reply Reply Quote 2
                • JonB
                  JonB @Chris Kawa last edited by

                  @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, where const 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 which toStdString() 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.

                  Chris Kawa 1 Reply Last reply Reply Quote 0
                  • Chris Kawa
                    Chris Kawa Moderators @JonB last edited by

                    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
                    
                    1 Reply Last reply Reply Quote 0
                    • JonB
                      JonB @Kent-Dorfman last edited by JonB

                      @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();
                          ...
                      

                      Screenshot from 2023-03-19 08-03-00.png

                      Notice the compiler warnings. When run I found it printed the "Hello world" from ptr1 but the output from ptr2 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... :)

                      Christian Ehrlicher Chris Kawa 2 Replies Last reply Reply Quote 0
                      • Christian Ehrlicher
                        Christian Ehrlicher Lifetime Qt Champion @JonB last edited by

                        @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

                        Qt has to stay free or it will die.

                        JonB 1 Reply Last reply Reply Quote 1
                        • JonB
                          JonB @Christian Ehrlicher last edited by JonB

                          @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 :)

                          1 Reply Last reply Reply Quote 1
                          • Chris Kawa
                            Chris Kawa Moderators @JonB last edited by Chris Kawa

                            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.

                            JonB 1 Reply Last reply Reply Quote 0
                            • JonB
                              JonB @Chris Kawa last edited by JonB

                              @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! :)

                              Chris Kawa 1 Reply Last reply Reply Quote 0
                              • Chris Kawa
                                Chris Kawa Moderators @JonB last edited by

                                @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 ).

                                1 Reply Last reply Reply Quote 0
                                • Kent-Dorfman
                                  Kent-Dorfman last edited by

                                  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. ;^)

                                  Chris Kawa 1 Reply Last reply Reply Quote 0
                                  • Chris Kawa
                                    Chris Kawa Moderators @Kent-Dorfman last edited by Chris Kawa

                                    @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 the size() 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 have std::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.

                                    1 Reply Last reply Reply Quote 1
                                    • First post
                                      Last post