문자열 스트림, 문자열 및 char * 변환 혼동 stringstream

내 질문은 삶의 문자열 stringstream.str().c_str()이 메모리 에서 어디로 반환 되는지, 왜 const char*?에 할당 할 수 없습니까?

이 코드 예제는 내가 할 수있는 것보다 더 잘 설명합니다.

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

int main()
{
    stringstream ss("this is a string\n");

    string str(ss.str());

    const char* cstr1 = str.c_str();

    const char* cstr2 = ss.str().c_str();

    cout << cstr1   // Prints correctly
        << cstr2;   // ERROR, prints out garbage

    system("PAUSE");

    return 0;
}

stringstream.str().c_str()할당 될 수있는 가정은 const char*추적하는 데 시간이 걸리는 버그로 이어졌습니다.

보너스 포인트의 경우, 누군가가 cout명세서를 대체하는 이유를 설명 할 수 있습니까?

cout << cstr            // Prints correctly
    << ss.str().c_str() // Prints correctly
    << cstr2;           // Prints correctly (???)

문자열을 올바르게 인쇄합니까?

Visual Studio 2008에서 컴파일 중입니다.



답변

stringstream.str()전체 표현식의 끝에서 파괴 된 임시 문자열 객체를 반환합니다. 해당 ( stringstream.str().c_str()) 에서 C 문자열에 대한 포인터를 얻으면 명령문이 끝나는 위치에서 삭제되는 문자열을 가리 킵니다. 그렇기 때문에 코드가 쓰레기를 출력합니다.

임시 문자열 객체를 다른 문자열 객체로 복사하고 C 문자열을 가져올 수 있습니다.

const std::string tmp = stringstream.str();
const char* cstr = tmp.c_str();

임시 문자열을 const변경하면 변경 사항이 다시 할당되어 cstr무효화 될 수 있기 때문에 임시 문자열을 만들었습니다 . 호출 결과를 전혀 저장하지 않고 전체 표현식이 끝날 때까지만 str()사용 하는 것이 더 안전합니다 cstr.

use_c_str( stringstream.str().c_str() );

물론 후자는 쉽지 않을 수 있으며 복사 비용이 너무 비쌀 수 있습니다. 대신 임시를 const참조 에 바인딩하는 것이 가능합니다 . 이는 수명을 참조 수명으로 연장합니다.

{
  const std::string& tmp = stringstream.str();
  const char* cstr = tmp.c_str();
}

최고의 솔루션 인 IMO. 불행히도 잘 알려져 있지 않습니다.


답변

당신이하고있는 일은 임시를 만드는 것입니다. 그 임시는 컴파일러가 결정한 범위에 존재하므로, 어디로 가고 있는지의 요구 사항을 충족시키기에 충분히 길다.

명령문 const char* cstr2 = ss.str().c_str();이 완료 되 자마자 컴파일러는 임시 문자열을 유지할 이유가 없으며, 소멸되므로 const char *메모리가 비어 있습니다.

당신의 진술 string str(ss.str()); 은 임시가 로컬 스택에 넣은 string변수 의 생성자에서 사용되며 str, 블록의 끝 또는 작성한 함수가 끝날 때까지 예상되는 한 유지됩니다. 따라서 const char *를 시도 할 때 within은 여전히 ​​좋은 메모리 cout입니다.


답변

이 줄에서 :

const char* cstr2 = ss.str().c_str();

ss.str()a를한다 사본 이제 stringstream의 내용을. c_str()같은 줄 을 호출하면 합법적 인 데이터를 참조하게되지만 그 줄 후에는 문자열이 파괴 char*되어 소유하지 않은 메모리를 가리 킵니다.


답변

ss.str ()에 의해 리턴 된 std :: string 오브젝트는 수명이 표현식으로 제한되는 임시 오브젝트입니다. 따라서 휴지통을 가져 오지 않고 임시 객체에 포인터를 할당 할 수 없습니다.

이제 한 가지 예외가 있습니다. 임시 객체를 얻기 위해 const 참조를 사용하는 경우 더 긴 수명 동안 사용하는 것이 합법적입니다. 예를 들어 다음을 수행해야합니다.

#include <string>
#include <sstream>
#include <iostream>

using namespace std;

int main()
{
    stringstream ss("this is a string\n");

    string str(ss.str());

    const char* cstr1 = str.c_str();

    const std::string& resultstr = ss.str();
    const char* cstr2 = resultstr.c_str();

    cout << cstr1       // Prints correctly
        << cstr2;       // No more error : cstr2 points to resultstr memory that is still alive as we used the const reference to keep it for a time.

    system("PAUSE");

    return 0;
}

그렇게하면 문자열을 더 오래 얻을 수 있습니다.

이제 컴파일러가 함수 호출을 통해 초기화를보고 해당 함수가 임시를 반환하면 복사를 수행하지 않고 할당 된 값을 임시로 만든다는 RVO라는 최적화가 있다는 것을 알아야합니다. . 그렇게하면 실제로 참조를 사용할 필요가 없습니다. 필요한 참조를 복사하지 않을 것입니다. 그렇게 :

 std::string resultstr = ss.str();
 const char* cstr2 = resultstr.c_str();

더 좋고 더 간단 할 것입니다.


답변

ss.str()일시적는 초기화 후 파괴 cstr2완료됩니다. 따라서로 인쇄하면 임시 cout와 관련된 c-string std::string이 오랫동안 destory되었으므로 충돌하고 주장하면 운이 좋으며 쓰레기를 인쇄하거나 작동하는 것처럼 보일 때 운이 좋지 않습니다.

const char* cstr2 = ss.str().c_str();

cstr1그러나 지시 하는 C- 문자열 은 수행 할 때 여전히 존재하는 문자열과 연관 cout되므로 결과를 올바르게 인쇄합니다.

다음 코드에서는 첫 번째 코드 cstr가 정확합니다 ( cstr1실제 코드에 있다고 가정 합니까?). 두 번째는 임시 문자열 객체와 관련된 c- 문자열을 인쇄합니다 ss.str(). 표시되는 전체 표현 평가가 끝나면 오브젝트가 삭제됩니다. 전체 표현식은 전체 cout << ...표현식이므로 c- 문자열이 출력되는 동안 관련 문자열 객체는 여전히 존재합니다. 들어 cstr2– 성공할 순수 불량입니다. 아마도 내부적으로 새 임시에 대한 동일한 저장 위치를 ​​내부에서 선택하여 초기화에 사용 된 임시로 이미 선택했습니다 cstr2. 충돌이 발생할 수도 있습니다.

cout << cstr            // Prints correctly
    << ss.str().c_str() // Prints correctly
    << cstr2;           // Prints correctly (???)

의 반환 c_str()은 일반적으로 내부 문자열 버퍼를 가리 키지 만 요구 사항은 아닙니다. 내부 구현이 연속적이지 않은 경우 문자열은 버퍼를 구성 할 수 있습니다 (잘 가능하지만 다음 C ++ 표준에서는 문자열을 연속적으로 저장해야합니다).

GCC에서 문자열은 참조 계산 및 COW (Copy-On-Write)를 사용합니다. 따라서 다음 사항이 사실임을 알 수 있습니다 (적어도 GCC 버전에서는 가능합니다)

string a = "hello";
string b(a);
assert(a.c_str() == b.c_str());

두 문자열은 여기서 동일한 버퍼를 공유합니다. 그중 하나를 변경하면 버퍼가 복사되고 각각이 별도의 사본을 보유합니다. 그러나 다른 문자열 구현은 다르게 작동합니다.


답변