【问题标题】:C++ - printing objects in statistically allocated array causes segmentation faultC++ - 在统计分配的数组中打印对象会导致分段错误
【发布时间】:2021-01-29 16:06:39
【问题描述】:

所以我正在创建一个程序,它实现了代表一所学校及其学生和课程的几个类。当我尝试打印出 studentCoursePairs[] 数组中的所有 Taken 对象时出现分段错误,该数组表示参加特定课程的学生对象。我认为我的分段错误来自 School.cc 中的 addTaken() 函数,它的工作是查找具有给定学生编号和课程 ID 的学生对象和课程对象,然后使用找到的学生和课程创建一个新的 Taken 对象对象和等级。然后我尝试将这个新对象添加到 Taken 集合的后面,即 studentCoursePairs。

当我注释掉 studentCoursePairs[i]->print() 时,分段错误就消失了。我不确定自己做错了什么,希望能得到一些帮助。

我不确定是否需要除 School.cc 之外的其他课程,但我还是将它们包括在内以帮助理解。

学校.cc:

#include <iostream>
#include <iomanip>
using namespace std;
#include <string.h> 

#include "School.h"

School::School(string s1) : name(s1){ 
    numTaken = 0;
}

void School::addTaken(string number, int code, string grade){
    Student* s = nullptr;
    Course* c = nullptr;
    for(int i = 0; i < numTaken; ++i){
        if((studentsCollection->find(number, &s)) && (coursesCollection->find(code, &c))){
        Taken* taken = new Taken(s, c, grade);
          studentCoursePairs[i] = taken;            
          ++numTaken;
        }
    }
}

void School::printTaken(){
    cout << name << " === TAKEN: "<< endl;
    for(int i = 0; i < sizeof(studentCoursePairs)/sizeof(studentCoursePairs[0]); ++i){
        studentCoursePairs[i]->print(); //seg fault
    }   
}

其他文件:

StudentCollection.cc

bool StudentCollection::find(string num, Student** s){
    for(int i = 0; i < size; ++i){
        if(students[i]->getNumber() == num){ //find student number
            *s = students[i];
        }
    }
}

CoursesCollection.cc

bool CoursesCollection::find(int id, Course** c){
    for(int i = 0; i < numCourses; ++i){
        if(courses[i]->getId() == id){ //find course id
            *c = courses[i];
        }
    }
}

我还有一个 Student 类和 Course 类,它们只是声明和初始化学生的姓名、课程、gpa 以及课程代码、讲师、姓名、课程年份等信息。

【问题讨论】:

  • 不要猜测,而是在调试器下运行程序并研究崩溃时的程序状态。
  • 既然我已经阅读了您的代码,请尝试回答以下问题:如果只有 3 个学生-课程对,studentCoursePairs[10] 中的内容是什么?对studentCoursePairs[10]-&gt;print()调用有什么影响?
  • 你为什么要使用拥有指针和new 来处理所有事情?默认情况下使用值和集合,必要时使用智能指针。
  • for 循环仅打印 studentCoursePairs 数组具有的元素数。因此,如果只有 3 对,则 sizeof(studentCoursePairs)/sizeof(studentCoursePairs[0]) 不会让它打印到前 3 对之后。
  • 数组不是这样工作的。您将其声明为Taken* studentCoursePairs[MAX_PAIRS];,因此表达式将始终返回MAX_PAIRS。您将需要另一种方法来跟踪占用的插槽数,或者将插槽标记为空(然后在遍历数组时检查该标记)。

标签: c++ arrays pointers printing segmentation-fault


【解决方案1】:

您的 School 对象有两个主要问题。让我们从您在问题中发布的内容开始:

void School::printTaken(){
    cout << name << " === TAKEN: "<< endl;
    for(int i = 0; i < sizeof(studentCoursePairs)/sizeof(studentCoursePairs[0]); ++i){
        studentCoursePairs[i]->print(); //seg fault
    }   
}

此 for 循环将始终准确运行 MAX_PAIRS 次,因为此变量被定义为

Taken* studentCoursePairs[MAX_PAIRS];

所以sizeof(studentCoursePairs) === MAX_PAIRS * sizeof(studentCoursePairs[0]).

相反,您只想在前几个实际包含有效指针的插槽上循环。你有一个变量:numTaken。因此,将条件更改为i &lt; numTaken,您的打印循环将起作用。

第二个大问题在addTaken

void School::addTaken(string number, int code, string grade){
    Student* s = nullptr;
    Course* c = nullptr;
    for(int i = 0; i < numTaken; ++i){
        if((studentsCollection->find(number, &s)) && (coursesCollection->find(code, &c))){
        Taken* taken = new Taken(s, c, grade);
          studentCoursePairs[i] = taken;            
          ++numTaken;
        }
    }
}

让我们玩一下电脑,如果传入的数字和代码有效,会发生什么:

  • 如果 numTaken 为 0,则循环立即停止(因为 0 &lt; 0 为假)并且 numTaken 不递增。您可以随意拨打addTaken,它永远不会改变numTaken
  • 假设您解决了这个问题,让我们假设 numTaken = 5。在第一次迭代中,您检查条件并同意这是一个有效的数字代码组合。因此,您创建了一个新的 Taken 对象并 .. 用新对象覆盖 studentCoursePairs[0]。在第二次迭代中,您执行相同操作并使用等效对象覆盖 studentCoursePairs[1]

这可能不是预期的行为。 相反,您可能想在studentCoursePairs[numTaken] 中放置一个新对象并碰撞numTaken

void School::addTaken(string number, int code, string grade){
    Student* s = nullptr;
    Course* c = nullptr;
    if((studentsCollection->find(number, &s)) && (coursesCollection->find(code, &c))){
        Taken* taken = new Taken(s, c, grade);
        studentCoursePairs[numTaken] = taken;            
        ++numTaken;
    }
}

弄清楚如何处理传递的组合无效或超过 MAX_PAIRS 组合的情况留给您作为练习。

编辑:CoursesCollection 中存在第三个主要问题:您为一个对象new Course() 分配空间,同时将其视为一个数组,并将结果存储在局部变量而不是成员中。您的构造函数应该如下所示:

CoursesCollection::CoursesCollection(){
    courses = new Course*[MAX_COURSES];
    numCourses = 0;
}

或者,使用成员初始化列表:

CoursesCollection::CoursesCollection() 
  : courses(new Course*[MAX_COURSES]), numCourses(0) {}

【讨论】:

  • Course* courses = new Course(); -- 你忘了这个明显的问题吗?
  • 我正在写答案,而 cmets 正在发现其他问题。
  • 当然,另一个主要问题是所有这些对象都会泄漏内存。
  • 嗨!所以我在 School.cc 中编辑了 add 和 print 函数,但由于某种原因 numTaken 永远不会增加。该值始终保持为 0,因此实际上没有打印任何 Taken 对象。
  • 在调试器下运行您的代码并在 addTaken 上放置一个断点,然后单步执行。一旦您确认addTaken 正确地递增numTaken,请检查您实际上是在调用 same School 对象上的 add 和 print。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多