내 질문은 삶의 문자열 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());
두 문자열은 여기서 동일한 버퍼를 공유합니다. 그중 하나를 변경하면 버퍼가 복사되고 각각이 별도의 사본을 보유합니다. 그러나 다른 문자열 구현은 다르게 작동합니다.