c ++ std :: string изменился неожиданно. Я думаю, что эта проблема о глубоком и мелком копировании - PullRequest
2 голосов
/ 13 мая 2019

Я создал простые значения C ++ std::string.

Но значение имеет неожиданные результаты.

Я тестировал этот код с помощью компилятора g ++ (Linux) и Visual Studio (Windows) и обоих компиляторовпоказать ту же проблему.

Обычный код результата

/* This code results are Normal */

#include <bits/stdc++.h>

int main() {
    std::string a1 = "a1";
    std::string a2 = "a2";

    std::string b1("b1");
    std::string b2("b2");

    const char *c1 = std::string("c1").c_str();
    const char *c2 = std::string("c2").c_str();

    std::cout << "Expected [a1], real [" << a1 << "]\n";
    std::cout << "Expected [a2], real [" << a2 << "]\n";
    std::cout << "Expected [b1], real [" << b1 << "]\n";
    std::cout << "Expected [b2], real [" << b2 << "]\n";
    std::cout << "Expected [c1], real [" << c1 << "]\n";
    std::cout << "Expected [c2], real [" << c2 << "]\n";
}

Результат консоли:

Expected [a1], real [a1]
Expected [a2], real [a2]
Expected [b1], real [b1]
Expected [b2], real [b2]
Expected [c1], real [c1]
Expected [c2], real [c2]

Неверный код результата

/* This code results has some problem. */

#include <bits/stdc++.h>

int main() {

    const char *c1 = std::string("c1").c_str();
    const char *c2 = std::string("c2").c_str();

    std::string a1 = "a1";
    std::string a2 = "a2";

    std::string b1("b1");
    std::string b2("b2");

    // const char *c1 = std::string("c1").c_str();
    // const char *c2 = std::string("c2").c_str();

    std::cout << "Expected [a1], real [" << a1 << "]\n";
    std::cout << "Expected [a2], real [" << a2 << "]\n";
    std::cout << "Expected [b1], real [" << b1 << "]\n";
    std::cout << "Expected [b2], real [" << b2 << "]\n";
    std::cout << "Expected [c1], real [" << c1 << "]\n"; // c1 = b2?
    std::cout << "Expected [c2], real [" << c2 << "]\n"; // b2 = b2?
}

Результат консоли:

Expected [a1], real [a1]
Expected [a2], real [a2]
Expected [b1], real [b1]
Expected [b2], real [b2]
Expected [c1], real [b2]
Expected [c2], real [b2]

Я обычно использую только string str = "", но мне было интересно, когда я тестировал.

Ожидается, что у конструктора возникнет проблема.

Какя могу понять это ненормальные результаты с std :: string?

1 Ответ

6 голосов
/ 13 мая 2019

Проблема заключается в этом

const char *c1 = std::string("c1").c_str();
const char *c2 = std::string("c2").c_str();

В каждой строке вы создаете временный объект std::string, получаете указатель на его содержимое и назначаете этот указатель напеременная.В конце каждой строки временный std::string будет уничтожен, и указатель, полученный через .c_str(), станет, таким образом, висящим указателем

В общем, обаверсии вашего кода имеют неопределенное поведение .Это просто означает: вы написали код, который нарушает основные предположения, с которыми разрешено работать компилятору (например, если вы решите разыменовать указатель, этот указатель фактически указывает на допустимый объект), и, таким образом, компилятор не может бытьОжидается, что каким-то волшебным образом создаст программу, которая ведет себя значимым образом.Точное объяснение того, почему первая версия «работает» (т. Е. Кажется, работает), в то время как вторая версия не имеет отношения к тому, в какой машинный код компилятор преобразовал ваш код C ++.В общем, обратите внимание, что во второй версии ваши две временные строки являются первыми строками, которые будут построены.Как только эти временные строки уничтожены, любая память, которая может быть выделена для них, может быть повторно использована для строк, созданных впоследствии.В вашем первом примере, с другой стороны, ваши временные строки являются последними строимыми строками.После того, как временные строки будут уничтожены, никакие другие локальные объекты не будут построены, для которых потребуется память.Таким образом, не исключено, что содержимое памяти, на которое укажут ваши два указателя, просто не будет перезаписано.Таким образом, хотя ваши указатели больше не будут действительными указателями, и доступ к объектам, на которые они указывали, не будет разрешен (поскольку объекты больше не существуют), простое выполнение этого в любом случае, вероятно, все равно даст ожидаемый результат.

Поскольку вы не указали точную версию компилятора и использованные параметры компиляции, трудно сказать точно, что делал ваш компилятор.Но давайте посмотрим, что будет делать последний GCC с уровнем оптимизации -O2 (мне не удалось воспроизвести проблему только с настройками по умолчанию).Стандартная библиотека, используемая текущими версиями GCC по умолчанию, выполняет оптимизацию коротких строк .Каждая из рассматриваемых строк состоит всего из двух символов.Таким образом, внутренний буфер объектов std::string и результат .c_str() будут фактически находиться внутри объекта std::string в стеке.Рассматривая сборку для first и second версии вашего кода выше, мы видим, что компилятор действительно помещает временные строки в два отдельных места в стеке в первой версии,в то время как он помещает их в то же место, в котором он позже строит строку b2 во второй версии…

...