C++: сбой программы при попытке печати массива const char*

У меня есть программа, в которой пользователь вводит свое имя, возраст и классы, которые он посещал. Эти классы хранятся в функции main() как двумерный массив символов, а затем передаются в функцию внутри класса Student, которая копирует этот массив в открытый член const char* array с именем m_CourseNames. С помощью отладчика в CLion я проверил, что этот процесс успешно завершен.

Тем не менее, я получаю exit code 11 и программа вылетает всякий раз, когда я пытаюсь перебрать массив m_CourseNames и вывести его содержимое на экран.

Я попытался распечатать массив на экране следующими способами:

  1. В основной функции:

    for (int count = 0 ; count < 9 ; count++)
        cout << student.m_CourseNames[count] << " ";
    
  2. В функции члена студенческого класса, позвонив через student.getCourses();:

    for (int count = 0 ; count < 9 ; count++)
        cout << m_CourseNames[count] << " ";
    
  3. Внутри перегруженной операторной функции с тем же методом, что и в 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 вместо массива и указателей С-типа.

Другие вопросы по тегам