2016-11-15 11 views
1

간단한 콘솔 팩맨 게임을 만들려고하고 있으며 다음 원본 코드에서 나오는이 희미한 인쇄물이 발생합니다.C++ 코드는 동일한 문자열 배열을 두 번 연속 인쇄하려고 시도 할 때 매우 다른 두 개의 출력을 흐리게 처리합니다.

#include <iostream> 
#include <fstream> 
#include <string> 
#include <cstdlib> 
int main(){ 
    std::ifstream map_file; 
    int map_width, map_height; 
    try{ 
     map_file.open("map.txt"); 
    } 
    catch(int e){ 
     std::cout << "An exception occured." << std::endl; 
    } 
    map_file >> map_width; 
    map_file >> map_height; 
    char* map[map_height]; 
    for(int i = 0; i < map_height; i++){ 
     std::string temp_line; 
     getline(map_file, temp_line); 
     map[i] = (char*)temp_line.c_str(); 
     std::cout << map[i] << std::endl; 

    } 
    system("pause"); 
    for(int i = 0; i < map_height; i++){ 
     std::cout << map[i] << std::endl; 

    } 
    return 0; 
} 

내가 여기 다시 코드에서 호출 표준 : COUT의 2 실점을 복사하여 콘솔에 출력 무엇의 스크린 샷 첨부합니다

for(int i = 0; i < map_height; i++){ 
     std::string temp_line; 
     getline(map_file, temp_line); 
     map[i] = (char*)temp_line.c_str(); 
     std::cout << map[i] << std::endl; 

    } 

다른 인쇄 실행 :

,691을
system("pause"); 
    for(int i = 0; i < map_height; i++){ 
     std::cout << map[i] << std::endl; 

    } 

다음은 스크린 샷과 같습니다 : 시스템 앞에있는 텍스트 블록 ("일시 중지")은 입력 map.txt 파일의 내용이며 map.txt에 작성된 방법과 정확히 일치하지만 두 번째 인쇄 실행은 다음과 같습니다. 전혀 예상하지 못했던

obscure printout screenshot

내 질문이 원인이 될 수있는 것을 단순히.

편집 : 나는

map[i] = (char*)temp_line.c_str(); 

깊은 복사 얕은, 그리고 수행 실현, 따라서 내가 대신 동적

map[i] 

에서

char[map_width + 1] 

을 할당하여 문제를 해결 및 수행하는 것

나는 당신의 문자열 temp이 범위를 벗어나 그것으로 가고 있기 때문이다 가능성이

ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
ystem32\cmd.exe 
+0

편집하려면 null 종결 자의 충분한 공간 (예 : temp.length() + 1 space)도 저장해야합니다. 그래도 문제가 해결되지 않으면 최신 코드를 제공하십시오. – SenselessCoder

+0

완료, 감사합니다! – tgudelj

+0

나중에 참조 할 수 있도록 paddy의 제안을 사용하는 것이 질문에 C++로 태그가 붙여져 있으므로 더 좋으며 이러한 방법을 사용하면 이러한 문제를보다 쉽게 ​​처리하는 데 도움이됩니다. – SenselessCoder

답변

4

정의되지 않은 동작입니다.당신은 더 이상 유효하지 않은 포인터를 저장하는 :

map[i] = (char*)temp_line.c_str(); 

포인터 대신하여 map 저장 std::string 값,이 할 괜찮을 것입니다 경우

map[i] = temp_line; 

나는 또한 당신이 변수를 사용하고주의 사항 길이의 배열. 하지마. 대신 std::vector을 사용하십시오. 초보자를위한 가장 쉬운 방법은 다음과 같이 그것을 할 것 :

당신은 아마 의심으로
std::vector<std::string> map(map_height); 
for(int i = 0; i < map_height; i++) 
{ 
    getline(map_file, map[i]); 
} 
3

기록 된 원래의 프로그램을 수있는 방법으로 여전히 관심이 있어요, 그와 관련된 포인터 (c_str는)뿐만 아니라 간다. 따라서 map[i]은 가비지 데이터를 가리 킵니다. strcpy과 같은 내용으로 내용을 깊게 복사해야합니다. 이에 대해 strcpy를 사용하는 방법은 strcpy을 참조하십시오. (힌트, 널 종료 자뿐만 아니라 소스 문자열에 실제로 메모리를 할당해야 함)

이것은 UB (Undefined Behavior)이기도합니다. 당신은 temp_line 변수의 내부 C 문자열을 저장하고

+0

'strcpy'는 내용을 딥 복사하지 않습니다. – paddy

+0

@paddy "단독으로"얘기하는 경우, 대상 문자열에 충분한 공간을 제공하는 계약이 제공됩니다. 그래서, 그런 의미에서 그렇지는 않겠지 만, 나는 여전히 당신이 깊은 복사를 할 수 있다고 말하고 싶습니다. – SenselessCoder

+0

문자열을 참조 할 때 "딥 복사본"이라는 용어를 사용하는 경우 필요한 메모리 할당도 수행되었음을 의미합니다. 불법 _reads_ 형태로 UB를 대체하는 것이 좋습니다. UB는 대신 불법 _writes_을 수행합니다. – paddy

2
for(int i = 0; i < map_height; i++){ 
    std::string temp_line; 
    getline(map_file, temp_line); 
    map[i] = (char*)temp_line.c_str(); 
    std::cout << map[i] << std::endl; 

} 

(즉, 손실 후 포인터를 인쇄하려고)하지만 temp_line 변수는 상기 루프의 각 반복 후에 파괴된다. 기본적으로 char * 변수 배열은 무작위로 쓰레기를 가리 킵니다.

std::string map[map_height]; 
for(int i = 0; i < map_height; i++){ 
    getline(map_file, map[i]); 
    std::cout << map[i] << std::endl; 

} 
system("pause"); 
for(int i = 0; i < map_height; i++){ 
    std::cout << map[i] << std::endl; 

} 
1

, 당신이보고있는 "임의 쓰레기"정말 무작위로하지 않습니다. 그렇습니다. 정의되지 않은 동작의 결과이며 실제로 고려하지 않아야하지만, 왜 이러한 특정 문자 시퀀스가 ​​필요합니까?

나는 argv[0]의 내용으로 생각됩니다. OS가 응용 프로그램을 호출 할 때 main을 호출하고 argc (인수의 수) 및 argv (명령 줄 인수가 들어있는 배열)의 두 매개 변수를 제공합니다. 은 응용 프로그램의 이름입니다. C : \ Windows \ System32 \ cmd.exe는 명령 프롬프트이고 종종 응용 프로그램의 부모 프로세스이기 때문에 OS가 해당 문자열을 응용 프로그램의 처음 몇 가지 메모리에 썼다고 생각할 수 있습니다.

코드가 int main(int argc, char* argv[]) 정의 대신 int main() 정의를 사용하기 때문에 코드가 해당 메모리 블록에 액세스하도록 설계되지는 않았지만 사용자의 문자열 중 하나가 임의의 위치에있는 (임의의) 스택 및 초기에 프로그램의 메모리 레지스터에 저장됩니다.