Я пытаюсь сделать простую консольную игру pacman, и я испытываю этот непонятный вывод на печать из следующего исходного кода:
#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;
}
Я снова скопирую два прогона вызова std :: cout из этого кода и приложу скриншот того, что было выведено в консоли:
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;
}
Вот снимок экрана: блок текста перед системой («пауза») является содержимым входного файла map.txt и отображается точно так, как он записан в map.txt, но второй запуск печати совершенно неожиданный.
Мой вопрос просто, что может быть причиной этого.
РЕДАКТИРОВАТЬ: я понял
map[i] = (char*)temp_line.c_str();
выполняет поверхностную, а не глубокую копию, поэтому я исправил проблему, вместо этого динамически выделяя
char[map_width + 1]
в
map[i]
и выполнение
strcpy(map[i], temp_line.c_str());
Я все еще интересуюсь тем, как можно было написать оригинальную программу
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
Неопределенное поведение. Вы храните указатели, которые больше не действительны:
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] );
}
Это потому, что ваша строка temp
выходит из области видимости и вместе с ним указатель (c_str
) связанный с этим идет также. Так что ваши map[i]
указывает на мусорные данные. Вам нужно глубоко копировать содержимое с чем-то вроде strcpy
, Увидеть зЬгсру о том, как вы можете использовать strcpy для этого. (Подсказка, вам нужно фактически выделить память для исходной строки, а также для нулевого терминатора)
Это также UB (неопределенное поведение). (Пытаясь напечатать указатели после потери, то есть)
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;
}
Вы сохраняете внутреннюю строку c переменной temp_line, но переменная 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;
}
Как вы, вероятно, подозреваете, «случайный мусор», который вы видите, на самом деле не случайный. Да, это следствие неопределенного поведения, и вы не должны это учитывать, но почему именно эта конкретная последовательность символов?
Я подозреваю, что это происходит из-за содержания argv[0]
, Когда ОС вызывает приложение, оно вызывает main
и дает два параметра: argc
(количество аргументов) и argv
(массив, содержащий аргументы командной строки). argv[0]
это название вашей заявки. C: \ Windows \ System32 \ cmd.exe является командной строкой и часто является родительским процессом приложения, поэтому я мог представить, что ОС записала эту строку в первые несколько частей памяти приложения.
Потому что ваш код использует int main()
определение вместо int main(int argc, char* argv[])
По определению, ваш код не предназначен для доступа к этому блоку памяти, но (не так) произвольный доступ указал одну из ваших строк на то место памяти, которое находится в стеке и в начале регистра памяти программы.