【问题标题】:Seg. fault resizing array C++赛格。错误调整数组大小 C++
【发布时间】:2025-12-29 01:05:06
【问题描述】:

我有一个优先级队列数组,其中填充了“作业”(名称 + 优先级)。除了调整大小(如果它已满)之外,我已经能够让所有与队列相关的工作。这是我认为导致我无法弄清楚的分段错误的位。

编辑:

这里还有一些可以编译的代码,我保留了其余的函数,以防它们有任何帮助。现在初始容量设置为 5,当您尝试将作业添加到完整列表时,它将使阵列容量翻倍,并允许您在 SEG 之前添加更多作业。过错。

pq.h

#ifndef PQ_H
#define PQ_H
#include "interface.h"
#include <string>
using namespace std;

class Job {
    public:
        int getPriority();
        string getTaskName();
        void setPriority(int val);
        void setTaskName(string tname);
        Job();
    private:
        int priority;
        string taskName;
};

class PriorityQueue {

    public:
        PriorityQueue();
        ~PriorityQueue();
        int size();
        bool isEmpty();
        void clear();
        void enqueue(string value, int priority);
        string dequeue();
        string peek();
        int peekPriority();
        PriorityQueue(const PriorityQueue & src);
        PriorityQueue & operator=(const PriorityQueue & src);

    private:
        static const int INITIAL_CAPACITY = 5;  
        Job *array;
        int count;
        int capacity;

    void expandCapacity() {
        Job *oldArray = array;
        capacity *= 2;
        array = new Job[capacity];
        for (int i = 0; i < count; i++) {
            array[i] = oldArray[i];
        }
        delete[] oldArray;
    }
};

#endif

pq.cpp

#include <iostream>
#include <cstring>
using namespace std;
//#include "job.h"
#include "pq.h"

Job::Job() // Constructor
 {
    priority= 0;
    taskName = "There are no items in the list.";
}

int Job::getPriority(){ // returns the prority of the job
    return priority;
}
string Job::getTaskName(){ // returns the name of the job
    return taskName;
}
void Job::setPriority(int val){ // sets the priority of a newly created job
    priority = val;
}
void Job::setTaskName(string tname){ // sets the name of a new job
    taskName = tname;
}

PriorityQueue::PriorityQueue() // constructor
    {
        count = 0;
        capacity = INITIAL_CAPACITY - 1;
        array = new Job[INITIAL_CAPACITY];
        }

PriorityQueue::~PriorityQueue() { // destructor
   delete [] array;

}

int PriorityQueue::size() { // returns the number of jobs in the queue
   return count;  
}

bool PriorityQueue::isEmpty() { // returns true if queue is empty
   if (count != 0){
       return false;
   }else{
   return true;
   }
}

void PriorityQueue::clear() { // clears queue of all jobs
   count = 0;
   // need to make it remove and delete the items
}

void PriorityQueue::enqueue(string value, int priority) { 
   // tests size to see if Queue is a max capacity
   if(count == capacity){
       expandCapacity();
       cout << "\tList was full and has been expanded\n";
   }
   array[++count].setPriority(priority);
   array[count].setTaskName(value);

   // upheap operations
   Job v = array[count];
   int tempcount = count;
   while (array[tempcount/2].getPriority() >= v.getPriority()){
       array[tempcount] = array[tempcount/2];
       tempcount = tempcount/2;
   array[tempcount] = v;
   }

}
string PriorityQueue::dequeue() { 
    // removes the job with the highest priority from the queue and returns the name

    if(this->isEmpty()){ // make sure the queue isnt empty
        string empty = "The queue is empty";
        return empty;   
    }else{
   Job remove = array[1];
   array[1] = array[count--];

   int j;
   Job v;
   int k = 1;
   v = array[k];
   while(k <= count/2){
       cout << "dequeuewhile"; //  test
       j = k + k;
       if(j < count && array[j].getPriority() > array[j+1].getPriority()){
           j++;
           cout << "dequeueloop if1"; // test
       }
       if(v.getPriority() <= array[j].getPriority()){
           cout << "dequeueloop if2"; //test
           break;
       }
       array[k] = array[j];
       k = j;
   }
   array[k] = v;

   return remove.getTaskName(); //  returns the name of the removed job
    }
}
string PriorityQueue::peek() { // returns the name of the highest priority job without removing it from the queue
    if(count == 0){
        return array[0].getTaskName();
    }
   return array[1].getTaskName();
}

int PriorityQueue::peekPriority() { // returns the priority from the highest priority job without removing it from the queue
        if(count == 0){
        cout << "\tThere are no items in the list.\n";
        return array[0].getPriority();
    }
   return array[1].getPriority();
}

【问题讨论】:

  • 请发帖minimal reproducible example。就目前而言,您的expandCapacity 有几个缺陷,一个是您在调用new[] 之前更改了成员变量(capacity)。如果new[] 抛出异常,您的PriorityQueue 对象将被损坏。另一个问题是您的 PriorityQueue 类不遵循 3 的规则。如果在程序中的任何位置复制了 PriorityQueue,则行为未定义。为什么不直接使用std::vector&lt;Job&gt; array 而不是Job* array
  • 我在示例中尽了最大努力,我真的不知道出了什么问题。容量发生变化,因此用 2 倍大小的新阵列替换旧阵列。我无法为此使用向量。
  • 在调用 expandCapacity 之前,您可能已经损坏了内存。你真的应该按照说明做并发布minimal reproducible example
  • 假设capacity 为1,假设您将count 初始化为0,因此数组具有capacity 元素。自count &lt; capacity 以来容量未增加。所以你增加count,然后你尝试访问array[1],但是array[1]超出了范围。因此,除非您向我们展示 count 的设置,否则就是错误。我知道您将初始容量设置为 5,但将其设置为 1,您应该会看到代码失败的地方。
  • 我已经编辑了我希望是一个可以接受的例子。

标签: c++ arrays resize fault


【解决方案1】:

我认为当你使用++count 时,下次使用count 将超出数组的范围。

array[++count].setPriority(priority);
// SEGMENTATION FAULT HERE
array[count].setTaskName(value); 

如果数组的容量是 5,而count 是 4,那么您只是将 count 递增到 5,并尝试访问元素 5,这是越界的。

 array = new Job[capacity];
 for (int i = 0; i < count; i++) {
     array[i] = oldArray[i];
 }

让我们假设 capacity 是 10,所以你有一个包含 10 个元素的数组,范围从元素 0 到 9。 count告诉我们正在使用多少元素。 如果count 恰好是9,那么当您将count 加一时,它现在是10。然后,当您标记为产生段故障的行来时,您正在尝试访问元素10,在我们的示例中。在长度为 10 的数组中没有元素 10,所以你超出了范围。

array[++count].setPriority(priority); // array[10], but last element is 9!
// SEGMENTATION FAULT HERE
array[count].setTaskName(value); // array[10], but last element is 9!

当然,这部分之后的所有内容都会导致同样的问题,因为您继续使用array[count]

【讨论】:

  • 计数会跟踪其中包含数据的最高索引,因此事先增加它应该只说明下一个优先级的放置位置,然后是名称。此外,它会在此之前检查容量并将其加倍。我只是在尝试增加容量后才收到错误消息。
  • @Drakorex 我们不知道count 被初始化为什么。它可以是您(到目前为止)发布的任何代码。
  • @Drakorex 你检查一下 count == capacity,对吧?据我了解,“容量”是数组的长度。如果“count”只比容量小一,那么你检查count是否==容量(增加容量)之后,你增加“count”使其相等到容量。因此,在我看来,如果数组长度为 5,那么您正在尝试访问元素 5。但是长度为 5 的数组中没有元素 5。最后一个元素是元素 4。告诉我如果我我是个白痴,在这里。 ://
  • 用户“PaulMcKenzie”似乎同意我的观点,我明白了,因为他的想法完全一样。
  • @Drakorex 该错误正是您给出的答案所描述的。如果您不相信,为什么不在此行之后打印出 count 的值:array[++count].setPriority(priority); -- 在调用 expandCapacity() 之后,您应该清楚地看到 count 超出了数组的范围。请注意,数组从索引 0 开始并转到 n - 1,其中 n 是项目总数。我认为您忘记了本期的- 1 部分。
【解决方案2】:

您的原始代码与@antiHUMAN 之前给出的答案完全相同。

您遇到的问题是混合或错误地使用基于 0 和基于 1 的概念。

您的第一个错误是将capacity 设为从0 开始的数字。 capacity 应该表示数组中的最大项目数,因此您不应该从中减去 1。如果数组可以容纳 5 个项目,那么 capacity 应该是 5,而不是 4。

PriorityQueue::PriorityQueue() // constructor
{
    count = 0;
    capacity = INITIAL_CAPACITY;  // this remains 1-based.
    array = new Job[INITIAL_CAPACITY];
}

或使用初始化列表:

PriorityQueue::PriorityQueue() : count(0), 
                                 capacity(INITIAL_CAPACITY), 
                                 array(new Job[INITIAL_CAPACITY]) {}

在您的情况下,从 0 开始的数字应该是 count,而不是 capacity。鉴于此,由于 count 是从 0 开始的,而 capacity 是从 1 开始的,所以您在 enqueue 中的测试需要更改:

   if(count + 1 == capacity){
        expandCapacity();
        cout << "\tList was full and has been expanded\n";
    }

请注意,将 1 添加到 count 以说明 count 是从 0 开始的,capacity 是从 1 开始的。

【讨论】:

  • 非常感谢您的帮助,使用 if(count + 1 == capacity) 可以工作,但似乎它会导致数组比必要的更快扩展。如果initialCapacity = 2 数组最终在第一个条目之前而不是在第二个条目之前展开。
  • 好吧,请注意,当您向数组中添加项目时,您永远无法填充项目 0。array[++count].setPriority(priority); -- count 递增为 1,因此 array[0] 永远不会被填充或改变。这又是一个基于 0、基于 1 的问题,capacity 的真正含义(除了第 0 项之外的所有内容,还是包括在内?)您需要解决。