开发者

Strange Way to Initialize std::string with C string

while I was reading nVidia CUDA source code, I stumbled upon these two lines:开发者_如何学Python

    std::string stdDevString;

    stdDevString = std::string(device_string);

Note that device_string is a char[1024]. The question is: Why construct an empty std::string, then construct it again with a C string as an argument? Why didn't they call std::string stdDevString = std::string(device_string); in just one line?

Is there a hidden string initialization behavior that this code tries to evade/use? Is to ensure that the C string inside stdDevString remains null terminated no matter what? Because as far as I know, initializing an std::string to a C string that's not null terminated will still exhibit problems.


Why didn't they call std::string stdDevString = std::string(device_string); in just one line?

No good reason for what they did. Given the std::string::string(const char*) constructor, you can simply use any of:

std::string stdDevString = device_string;
std::string stdDevString(device_string);
std::string stdDevString{device_string}; // C++11 { } syntax

The two-step default construction then assignment is just (bad) programmer style or oversight. Sans optimisation, it does do a little unnecessary construction, but that's still pretty cheap. It's likely removed by optimisation. Not a biggie - I doubt if I'd bother to mention it in a code review unless it was in an extremely performance sensitive area, but it's definitely best to defer declaring variables until a useful initial value is available to construct them with, localising it all in one place: not only is it less error prone and cross-referenceable, but it minimises the scope of the variable simplifying the reasoning about its use.

Is to ensure that the C string inside stdDevString remains null terminated no matter what?

No - it made no difference to that. Since C++11 the internal buffer in stdDevString would be kept NUL terminated regardless of which constructor is used, while for C++03 isn't not necessarily terminated - see dedicated heading for C++03 details below - but there's no guarantees regardless of how construction / assignment is done.

Because as far as I know, initializing an std::string to a C string that's not null terminated will still exhibit problems.

You're right - any of the construction options you've listed will only copy ASCIIZ text into the std::string - considering the first NUL ('\0') the terminator. If the char array isn't NUL-terminated there will be problems.

(That's a separate issue to whether the buffer inside the std::string is kept NUL terminated - discussed above).

Note that there's a separate string(const char*, size_type) constructor that can create strings with embedded NULs, and won't try to read further than told (Constructor (4) here)

C++03 std::strings were not guaranteed NUL-terminated internally

Whichever way the std::string is constructed and initialised, before C++11 the Standard did not require it to be NUL-terminated within the string's buffer. std::string was best imagined as containing a bunch of potentially non-printable (loosely speaking, binary in the ftp/file I/O sense) characters starting at address data() and extending for size() characters. So, if you had:

std::string x("help");
x[4];  // undefined behaviour: only [0]..[3] are safe
x.at(4); // will throw rather than return '\0'
x.data()[4]; // undefined behaviour, equivalent to x[4] above
x.c_str()[4]; // safely returns '\0', (perhaps because a NUL was always
              // at x[4], one was just added, or a new NUL-terminated
              // buffer was just prepared - in which case data() may
              // or may not start returning it too)

Note that the std::string API requires c_str() to return a pointer to a NUL-terminated value. To do so, it can either:

  • proactively keep an extra NUL on the end of the string buffer at all times (in which case data[5] would happen to be safe on that implementation, but the code could break if the implementation changed or the code was ported to another Standard library implementation etc.)
  • reactively wait until c_str() is called, then:

    • if it has enough capacity at the current address (i.e. data()), append a NUL and return the same pointer value that data() would return
    • otherwise, allocate a new, larger buffer, copy the data over, NUL terminate it, and return a pointer to it (typically but optionally this buffer would replace the old buffer which would be deleted, such that calling data() immediately afterwards would return the same pointer returned by c_str())


I would say that it's equivalent of writing:

std::string stdDevString = std::string(device_string);

Or, even simpler:

std::string stdDevString = device_string;

Once the std::string has been created, it contains a private copy of the data in the C string.


I think it is ignorant to dismiss this as poor coding. If we assume that this string was allocated at file scope or as a static variable, it could be good coding.

When programming C++ for embedded systems with non-volatile memory present, there are many reasons why you wish to avoid static initialization: the main reason is that it adds lots of overhead code in the beginning of the program, where all such variables much be initialized. If they are instances of classes, constructors will be called.

This will lead to a delay peak at the beginning of the program execution. You don't want this workload peak there, because there are much more important tasks to do when starting up the program, like setting up various hardware.

To avoid this, you typically enable an option in the compiler which removes such static initialization, and then write your code in such a manner that no static/global variables are initialized, but instead set them in runtime.

On such a system, the code posted by the OP is the correct way to do it.


Looks like an artefact to me. Perhaps there was some other code in between, then it got removed, and someone was too lazy to join those two remaining lines into a single one.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜