Будет ли std::string всегда заканчиваться нулем в C++11?

В публикации 2008 года на своем сайте Херб Саттер утверждает следующее:

Существует активное предложение еще более ужесточить это в C++0x и потребовать нулевого завершения и, возможно, запретить реализации копирования при записи по причинам, связанным с параллелизмом. Вот статья: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2534.html. Я думаю, что одно или оба предложения в этом документе, вероятно, будут приняты, но мы увидим на следующем заседании или два.

Я знаю, что C++11 теперь гарантирует, что содержимое std::string хранится непрерывно, но приняли ли они вышеупомянутое в окончательном варианте?

Будет ли теперь безопасно использовать что-то вроде &str[0]?

3 ответа

Решение

Да. Согласно C++0x FDIS 21.4.7.1/1, std::basic_string::c_str() должен вернуться

указатель p такой, что p + i == &operator[](i) для каждого i в [0,size()],

Это означает, что данная строка s, указатель возвращается s.c_str() должен совпадать с адресом начального символа в строке (&s[0]).

&str[0] безопасно использовать - если вы не предполагаете, что он указывает на строку с завершающим нулем.

Начиная с C++11, требования включают (section [string.accessors]):

  • str.data() а также str.c_str() указывают на строку с завершающим нулем.
  • &str[i] == str.data() + i , для 0 <= i <= str.size()
    • обратите внимание, что это означает, что хранилище непрерывно.

Однако нет требования, чтобы &str[0] + str.size() указывает на нулевой терминатор.

Соответствующая реализация должна размещать нулевой ограничитель в хранилище непрерывно, когда data(), или же operator[](str.size())называются; но нет необходимости помещать его в какую-либо другую ситуацию, например, при вызове operator[] с другими аргументами.


Чтобы уберечь вас от чтения длинного обсуждения в чате ниже: было высказано возражение, что если c_str()было бы написать нулевой терминатор, это вызвало бы гонку данных в res.on.data.races#3 ; и я не был согласен с тем, что это будет гонка данных.

Хотя c_str() возвращает версию std :: string с завершающим нулем, при смешивании C ++ std :: string со строками C char * могут возникнуть сюрпризы.

Нулевые символы могут оказаться внутри C ++ std :: string, что может привести к незначительным ошибкам, поскольку функции C будут видеть более короткую строку.

Ошибочный код может перезаписать нулевой терминатор. Это приводит к неопределенному поведению. Функции C затем будут читать за пределами строкового буфера, что может вызвать сбой.

      #include <string>
#include <iostream>
#include <cstdio>
#include <cstring>

int main()
{
    std::string embedded_null = "hello\n";
    embedded_null += '\0';
    embedded_null += "world\n";

    // C string functions finish early at embedded \0
    std::cout << "C++ size: " << embedded_null.size() 
              << " value: " << embedded_null;
    printf("C strlen: %d value: %s\n", 
           strlen(embedded_null.c_str()), 
           embedded_null.c_str());

    std::string missing_terminator(3, 'n');
    missing_terminator[3] = 'a'; // BUG: Undefined behaviour

    // C string functions read beyond buffer and may crash
    std::cout << "C++ size: " << missing_terminator.size() 
              << " value: " << missing_terminator << '\n';
    printf("C strlen: %d value: %s\n", 
           strlen(missing_terminator.c_str()), 
           missing_terminator.c_str());
}

Выход:

      $ c++ example.cpp
$ ./a.out
C++ size: 13 value: hello
world
C strlen: 6 value: hello

C++ size: 3 value: nnn
C strlen: 6 value: nnna�
Другие вопросы по тегам