分段错误和神秘的循环行为

时间:2012-04-12 16:13:16

标签: c++ segmentation-fault

我正在完成一项具有一些特定要求的家庭作业。必须有一个名为TestScores的类,它将一组分数作为参数。如果任何分数为负或大于100,则抛出异常。最后,它必须具有返回所有分数的平均值的成员函数。我不够聪明,找不到只将数组传递给构造函数的方法,所以我还添加了一个告诉数组大小的int。

运行代码(我还没有尝试过测试异常),我不断收到分段错误。 Valgrind和gdb一直没有用,输出的信息如下:

==9765== Jump to the invalid address stated on the next line
==9765==    at 0x2200000017: ???

更神秘的是(至少对我来说),在客户端代码的for循环中,我的增量器,i,在创建TestScores对象后,不知何故从0变为看似随机的两位数字。在以前的版本中,在我开始使用rand()来填充数组之前,我只是从不增加并做了无限循环的事情。

这是TestScores.cpp的内容:

#include <iostream>
using std::cout;
using std::endl;
#include "TestScores.h"
#include <stdexcept>
using std::runtime_error;

// Constructor.
TestScores::TestScores(int a[], int s): 
_SIZE(s), _scores()
{
   // Look at each item in a[], see if any of them are invalid numbers, and
   // only if the number is ok do we populate _scores[] with the value.
   for (int i = 0; i < _SIZE; ++i)
   {
      if (a[i] < 0)
      {
         throw runtime_error ("Negative Score");
      }
      else if (a[i] > 100)
      {
         throw runtime_error ("Excessive Score");
      }
      _scores[i] = a[i];
      cout << _scores[i] << " ";
   }
   cout << endl;
}

// Finds the arithmetic mean of all the scores, using _size as the number of
// scores.
double TestScores::mean() 
{
   double total = 0;
   for (int i = 0; i < _SIZE; ++i)
   {
      total += _scores[i];
   }
   return total / _SIZE;
}

// median() creates an array that orderes the test scores by value and then
// locates the middle value.
double TestScores::median() 
{
   // Copy the array so we can sort it while preserving the original.
   int a[_SIZE]; 
   for (int i = 0; i < _SIZE; ++i)
   {
      a[i] = _scores[i];
   }

   // Sort the array using selection sort.
   for (int i = 0; i < _SIZE; ++i)
   {
      int min = a[i];

      for (int j = i + 1; j < _SIZE; ++j)
      {
         if (a[j] < min)
         {
            min = a[j];
            a[j] = a[i];
            a[i] = min;
         }
      }
   }

   // Now that array is ordered, just pick one of the middle values.
   return a[_SIZE / 2];
}

这是客户端代码:

#include <iostream>
#include "TestScores.h"
#include <stdexcept>
#include <cstdlib>
#include <ctime>
using std::exception;
using std::cout;
using std::endl;

int main()
{
   const int NUM_STUDENTS = 20,
             NUM_TESTS = 4;
   int test [NUM_TESTS][NUM_STUDENTS];

   // Make random seed to populate the arrays with data.
   unsigned seed = time(0);
   srand(seed);

   // Populate the scores for the individual tests graded for the semester.
   // These will all be values between 0 and 100.
   for (int i = 0; i < NUM_TESTS; ++i)
   {
      for (int j = 0; j < NUM_STUDENTS; ++j)
      {
         test[i][j] = rand() % 100;
         cout << test[i][j] << " ";
      }
      cout << endl;
   }

   // Now we have the data, find the mean and median results for each test.
   // All values should be valid, but we'll handle exceptions here.
   for (int i = 0; i < NUM_TESTS; ++i)
   {
      cout << "For Test #" << i + 1 << endl;
      try
      {
         cout << "i = " << i << endl;  // i = 0 here.
         TestScores results(test[i], NUM_STUDENTS);  
         cout << "i = " << i << endl;  // i = some random number here.
         cout << "Mean: " << results.mean() << endl;
         cout << "Median:" << results.median() << endl << endl;
      }
      catch (exception &e)
      {
         cout << "Error, invalid score: " << e.what() << endl;
      }
      cout << "For Test #" << i + 1 << endl;
   }

   return 0;
}

编辑: 标题也被要求:

#ifndef TEST_SCORES_H
#define TEST_SCORES_H

class TestScores
{
   private:
      const int _SIZE;
      int _scores[];

   public:
      // Constructor
      TestScores(int a[], int);

      double mean() const,
             median() const;
};
#endif

我玩弄阵列是动态的,并没有将数组初始化为空,这解决了我的问题,所以这就是我最后进入的地方。这引出了一些关注 - 问题。

在开始动态之前,我通过尝试给它应该已经初始化的大小值来初始化数组_scores。这导致了编译器问题。我和老师谈过这个问题,他说你不能为数组分配空间,除非有硬连线的全局常数。也就是说,您无法在构造函数中传递大小值来初始化数组。这是真的,如果是的话,为什么?

稍微退一步,在我看来,如果你需要很多值,动态数组会更好,因为那时你不需要在内存中连续的空间块。因此,如果您正在制作小型数组,那么创建动态数组似乎浪费了空间和时间。这是不真实的吗?我应该从现在开始做所有数组动态吗?这种体验肯定改变了我对常规数组效用的看法,至少它们与类有关。

此外,虽然我对作业充分肯定,但我觉得我通过传递大小的参数来违反精神(因为文字问题陈述读取:&#34;类构造函数应该接受一系列测试分数作为它的论点&#34;)。除了硬连线的全局常量或具有大小参数之外,有没有办法只传递数组?我发誓我花了一个小时试图想办法做到这一点。

2 个答案:

答案 0 :(得分:0)

如果没有TestScores.h,我必须猜测,但是如果您在创建i对象的循环中对TestScores被损坏的内容有所说明,则指向您的_scores成员变量未正确初始化,当您尝试加载它时,实际上是在破坏内存。

一旦TestScores.h可见,我会重新考虑这个答案,并考虑到该文件。


现在已更新TestScores.h可用。

问题是您没有初始化_scores。您实际上并没有分配任何内存来保存数组,更不用说将指针设置为指向该内存。因此,当您尝试将内容存储到数组中时,您只是在某处丢弃内存。

构造函数中的第一行应为:

_scores = new int[_SIZE];

这将分配内存以保存_SIZE int并将_scores设置为指向该内存。然后,您对_scores[i]的分配将实际进入属于您的程序的已定义内存。

当然,当TestScore的实例被破坏时,您还必须释放此内存(C ++不会为您执行此操作)。因此,您需要为TestScores定义和实现析构函数,析构函数需要包含该行:

delete [] _scores;

这将释放_scores指向的内存块。您可以阅读delete操作上的文档,了解[]在这种情况下必须存在的原因。

答案 1 :(得分:0)

您似乎根本没有初始化_scores。您需要在构造函数的顶部_scores = new int[s];(以及析构函数中的delete[] s;)。

在不初始化_scores的情况下,您可以将内容写入未定义的内存位置。