C++: сбой программы при попытке печати массива const char*
У меня есть программа, в которой пользователь вводит свое имя, возраст и классы, которые он посещал. Эти классы хранятся в функции main() как двумерный массив символов, а затем передаются в функцию внутри класса Student, которая копирует этот массив в открытый член const char* array с именем m_CourseNames. С помощью отладчика в CLion я проверил, что этот процесс успешно завершен.
Тем не менее, я получаю exit code 11
и программа вылетает всякий раз, когда я пытаюсь перебрать массив m_CourseNames и вывести его содержимое на экран.
Я попытался распечатать массив на экране следующими способами:
В основной функции:
for (int count = 0 ; count < 9 ; count++) cout << student.m_CourseNames[count] << " ";
В функции члена студенческого класса, позвонив через
student.getCourses();
:for (int count = 0 ; count < 9 ; count++) cout << m_CourseNames[count] << " ";
Внутри перегруженной операторной функции с тем же методом, что и в 1) (см. Код, чтобы понять, почему я попробовал это здесь)
Все три способа вывести массив const char * на экран привели к следующему коду ошибки до завершения программы:
Если массив является публичной переменной-членом, я не знаю, почему он не будет повторяться. Отладчик проверил, что все классы правильно хранятся в нем, поэтому он не отвечает за функцию addCourses().
См. Всю программу ниже (все, что закомментировано, является попыткой добиться вывода массива на экран):
--main.cpp--
#include <iostream>
#include "Student.h"
using namespace std;
int main()
{
char input[10][128] = {(0),(0)};
char name[128] = {0}, student_check = ' ';
int age = 0, count = 0;
cout << "\nPlease state your name and age:\n\n";
cout << "Name: ";
cin.getline(name, 128);
cout << "Age: ";
cin >> age;
cin.clear();
cin.ignore();
cout << "\n\nThanks!\n\nAre you a student? (Y/N): ";
cin.get(student_check);
switch (student_check)
{
case 'y':
case 'Y':
{
Student student;
student.setName(name);
student.setAge(age);
char course_check = ' ';
cout << "\n\nWhat course(s) are you taking?"
<< " (Enter the course prefix and number without any spaces): \n\n";
while (tolower(course_check) != 'n') {
cin.clear();
cin.ignore();
cout << "Course #" << count + 1 << ": ";
cin.getline(input[count], 128);
student.addCourse(input[count], count);
if (student.addCourse(input[count], count))
{
cin.clear();
cout << "Do you want to enter another course? (Y/N): ";
cin.get(course_check);
count++;
}
else
{
cout << "You have exceeded the number of courses you are allowed to enter" << endl << endl;
course_check = 'n';
}
cout << student;
student.getCourses();
//for (int count = 0 ; count < 9 ; count++)
// cout << student.m_CourseNames[count] << " ";
}
}
default:
break;
}
}
--Student.h---
#ifndef PA2_STUDENT_H
#define PA2_STUDENT_H
#include "Person.h"
#include <ostream>
class Student : public Person
{
public:
Student();
Student(const char* []);
bool addCourse(const char*, int);
void getCourses();
friend std::ostream& operator <<(std::ostream& os, const Student& student);
const char* m_CourseNames[10];
};
#endif
--student.cpp--
#include "Student.h"
#include <iostream>
using namespace std;
Student::Student() {}
Student::Student(const char* m_CourseNames[])
{
m_CourseNames[10] = {0};
}
bool Student::addCourse(const char* course, int index)
{
if (index < 9)
{
m_CourseNames[index] = course;
return true;
}
if (index >= 9)
return false;
}
void Student::getCourses()
{
cout << ", Courses: ";
for (int count = 0 ; count < 9 ; count++)
cout << m_CourseNames[count] << " ";
}
std::ostream &operator<<(std::ostream& os, const Student& student) {
os << "Name: " << student.m_Name << ", Age: " << student.m_Age;// << ", Courses: " << Student::m_CourseNames;
//cout << ", Courses: ";
// for (int count = 0 ; count < 9 ; count++)
// cout << m_CourseNames[count] << " ";
return os;
}
3 ответа
Вы перебираете записи в массиве, который вы не заполнили. Вы хотите что-то вроде:
void Student::getCourses()
{
cout << ", Courses: ";
for (int count = 0 ; count < index ; count++)
cout << m_CourseNames[count] << " ";
}
Кроме того, как указал Арун, вы получаете доступ к массиву за пределами. Вы не можете получить доступ к одиннадцатому элементу массива из десяти записей, и запись 10 является одиннадцатым элементом (так как 0 является первым).
Ваш код действительно плох, хотя по многим причинам. Основная проблема заключается в том, что ваш Person
класс хранит указатели в памяти, которой он не владеет, что делает класс очень сложным в использовании. Почему бы не использовать std::string
?
student.m_CourseNames
это массив указателей на char. Это означает, что вы получите кучу указателей. У вас нет места для хранения, на которое можно указывать, поэтому, если указатель не указан на какое-то действительное хранилище, программа уйдет в сорняки. Там, кто знает, что произойдет. Может быть, найти потерянное пиратское сокровище. Возможно быть съеденным драконами. Вероятно, сбой программы.
student.m_CourseNames
устанавливается student.addCourse(input[count], count);
который предоставляет указатель (который будет перезаписан следующим студентом, так что это плохая идея) и индекс, по которому будет обновляться m_CourseNames. Это тоже плохая, но не фатальная идея. Одной из целей занятий является то, что они контролируют свои собственные данные, поэтому студент должен поддерживать этот индекс.
Допустим, мы добавили три курса: A, B и C. for (int count = 0 ; count < 9 ; count++)
будет пытаться напечатать 9 (индексы с 0 по 8), так что это означает, что есть не менее шести выстрелов в неопределенное поведение, чтобы завершить программу.
Решения:
Не используйте массивы символов. Используйте std::string.
Если вам нужно использовать массивы символов, предоставьте хранилище и скопируйте источник в хранилище. Таким образом, вы знаете, что это безопасно и не будет перезаписано, если вы не захотите или не сделаете что-то глупое.
Не используйте массивы указателей. Используйте std::vector (и для реальных данных, а не для указателей на данные). Вектор изменяется по мере добавления и всегда знает, сколько элементов в нем содержится. Если вы не можете использовать векторы, используйте массивы, но не массивы указателей. Если возможно, используйте массивы std::string.
Следите за количеством элементов, хранящихся в ваших списках, чтобы вы не выходили за пределы. Вектор делает это для вас.
Инкапсулируйте данные вашего класса. Студент должен знать и управлять всем, что студент. Никто не должен говорить Студенту, где его курсы, только что у него есть курсы. Если вы хотите распечатать данные ученика, попросите ученика распечатать их для вас.
В Student::Student(const char* m_CourseNames[])
, вы переступаете через массив m_CourseNames[10] = {0}; //Index is in [0..9] range
Кроме того, где вы выделяете место для массива const char *
m_CourseNames
указатели?
Пожалуйста, рассмотрите возможность использования std::array
а также std::string
вместо массива и указателей С-типа.