2016-11-11 9 views
-1

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

#include <iostream> 
#include <string> 
#include <string> 
#include <stdio.h> 

std::string getConstCharAndModifyItWithANewString(const char* constchar) 
{ 
    std::string stringAtStack(constchar); 
    stringAtStack += "::isModified"; 
    return stringAtStack; 
} 

int main() 
{ 
    const char* firstConstCharPointer = getConstCharAndModifyItWithANewString("Hi!").c_str(); 
    std::string firstStringObj = "Hi+"; 

    printf(" firstConstCharPointer(%s)(%p)\nfirstStringObj(%s)(%p)\n\n", firstConstCharPointer,firstConstCharPointer, firstStringObj.c_str(),  firstStringObj.c_str() ); 
} 

ВЫВОД: firstConstCharPointer (Привет +) (0x4593eb8) firstStringObj (Привет +) (0x4593eb8)

+0

Похоже, проблема оптимизации компилятора. Не происходит на gcc 4.9.2 – ilo

+2

Вы наблюдаете неопределенное поведение, поэтому не можете винить оптимизацию. Также: прочитайте документы '.c_str()' – milleniumbug

+0

Не происходит в строках большого размера. – ilo

ответ

1

Ваша функция возвращает временноstd::string объект. После назначения переменной firstConstCharPointer и завершения выражения этот временный объект уничтожается, освобождая выделенный блок памяти и оставляя переменную, указывающую на освобожденную память. Это называется болтающимся указателем.

firstStringObj затем выделяет новый блок памяти, который происходит быть повторно использовать один и тот же блок памяти, что температура std::string ранее выделяется и освобождается. Таким образом, оборванный указатель имеет, чтобы теперь снова указывать на действительную память. Вот почему ваш оператор printf() способен отображать одинаковые адреса и содержимое для обеих строк.

Но это не определено поведение.Блок памяти, который получает каждый раз, полностью зависит от строки Allocator. Второй std::string мог бы так же легко выделить совершенно другой блок памяти, а затем код с большей вероятностью приведет к сбою или, по крайней мере, распечатать мусор, когда он попытается разыменовать оборванный указатель, который все еще указывает на недопустимую память.

Для того, чтобы программа работала, вам необходимо изменить firstConstCharPointer к std::string объекта, поэтому температура std::string получает копируются должным образом, например:

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

std::string getConstCharAndModifyItWithANewString(const char* constchar) 
{ 
    std::string stringAtStack(constchar); 
    stringAtStack += "::isModified"; 
    return stringAtStack; 
} 

int main() 
{ 
    const std::string firstConstStringObj = getConstCharAndModifyItWithANewString("Hi!"); 
    std::string secondStringObj = "Hi!"; 

    std::printf(" firstConstStringObj(%s)(%p)\nsecondStringObj(%s)(%p)\n\n", firstConstStringObj.c_str(), firstConstStringObj.c_str(), secondStringObj.c_str(), secondStringObj.c_str()); 
} 
4

указатели являются одинаковыми на вашей платформе, потому что firstConstCharPointer является оборванных указатель, он указывает на освобождаться память.

Это потому, что std::string, возвращенный getConstCharAndModifyItWithANewString, уничтожается после выражения присваивания const char* firstConstCharPointer = ...;.

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

На моей платформе, например, указатели одинаковы, и их нет в Ideone.

3

У вас есть классическое неопределенное поведение. printf пытается устранить firstConstCharPointer из-за %s. firstConstCharPointer указывает на данные, которые были разрушены, потому что std::string связан со сроком службы этого указателя останавливается после выполнения задания:

const char* firstConstCharPointer = getConstCharAndModifyItWithANewString("Hi!").c_str(); 
// temporary std::string returned from getConstCharAndModifyItWithANewString destroyed, pointer becomes dangling. 
3

Как указано в documentation:

Указатель, полученный из c_str() могут быть признаны недействительными:

  • Переходя неконстантную ссылку на строку в любой стандартной библиотечной функции, или
  • Вызов несимвольных функций-членов в строке, исключая оператор [], at(), front(), back(), begin(), rbegin(), end() и rend().

Таким образом, вы используете неверный указатель, поскольку деструктор не является константным членом и не перечисленными выше.