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

Posted

技术标签:

【中文标题】分段错误和神秘的循环行为【英文标题】:Segmentation fault and mysterious loop behavior 【发布时间】:2012-04-12 16:13:15 【问题描述】:

我正在完成一项具有一些特定要求的家庭作业。必须有一个名为 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,尝试给它应该已经初始化的大小值。这导致了编译器问题。我和我的老师谈过这个问题,他说你不能为数组分配空间,除非有一个硬连线的全局常量。也就是说,您不能在构造函数中传递大小值来初始化数组。这是真的吗?如果是,为什么?

退后一步,在我看来,如果您需要大量值,动态数组会更好,因为这样您就不需要内存中的连续空间块。因此,如果您正在制作小型数组,那么输入动态数组似乎是浪费空间和时间。这是不真实的吗?从现在开始我应该将所有数组都作为动态数组吗?这段经历无疑改变了我对常规数组实用性的看法,至少在它们与类有关的情况下。

另外,虽然我的作业得到了充分的评价,但我觉得我通过传递大小参数违反了精神(因为字面的问题陈述是:“类构造函数应该接受一个测试分数数组作为它的参数” )。除了硬连线的全局常量或有大小参数之外,有没有办法只传递数组?我发誓我花了一个小时想办法做到这一点。

【问题讨论】:

你也可以发TestScores.h 吗? 如果您使用 gcc,请在所有内容上添加“-g”和“-O0”标志,然后再次运行 valgrind:它可能会显示代码行号。 “_scores”的类型是什么? 【参考方案1】:

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

如果不初始化 _scores,您将内容写入未定义的内存位置。

【讨论】:

好吧,假设_scoresint *,我们还不知道。但是,是的,我还假设 _scores 存在一些初始化问题,导致了这种情况。 还要注意,由于构造函数可以抛出,析构函数可能并不总是运行。 是的,似乎使 _scores 成为动态数组,所有问题都已解决。但是在添加了我之前的 .h 之后,我有一些后续问题可以更好地理解这个问题。【参考方案2】:

如果没有TestScores.h,则必须猜测,但鉴于您所说的i 的值在您创建TestScores 对象的循环中被破坏,这指向您的_scores 成员变量不是被正确初始化,当你试图加载它时,你实际上是在浪费内存。

一旦TestScores.h 可见,我将在考虑文件的情况下重新访问此答案。


现在更新,TestScores.h 可用。

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

构造函数的第一行应该是:

_scores = new int[_SIZE];

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

当然,当TestScore 的实例被销毁时,您还必须释放此内存(C++ 不会为您这样做)。因此,您需要为TestScores 定义和实现一个析构函数,并且该析构函数需要包含以下行:

delete [] _scores;

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

【讨论】:

以上是关于分段错误和神秘的循环行为的主要内容,如果未能解决你的问题,请参考以下文章

C++ 分段错误 OpenCV

在指针迭代中使用 for 循环时出现分段错误

循环C ++中的分段错误Openmp

分段错误如何在内部(内核/硬件)工作?

循环分段错误

无法进入while循环:“分段错误”