1

我正在做一个项目,最近开始遇到一个特别讨厌的段错误。这里有一些背景:

1 -- 我有一个“订单”队列,我在其中测试了以下内容: a -- 填充(0 个订单、1 个订单和许多订单),删除直到 isEmpty() 返回 true,并用这些填充其他队列结果(这复制了程序的行为)

2 -- 从 gdb 中找到这个:

(gdb) run
Starting program: /Users/Nicholas M. Iodice/Dropbox/tufts/noz2/noz2/a.out 
Reading symbols for shared libraries ++......................... done
order added 1

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: 13 at address: 0x0000000000000000
0x0000000100003a66 in Queue::remove (this=0x7fff5fbffaa0) at Queue.cpp:85
85      ElementType retVal = front->order;
(gdb) where
#0  0x0000000100003a66 in Queue::remove (this=0x7fff5fbffaa0) at Queue.cpp:85
#1  0x0000000100002c08 in Packer::packItem (this=0x7fff5fbffaa0, time=1) at Packer.cpp:88
#2  0x0000000100002e3f in Packer::update (this=0x7fff5fbffaa0, time=1) at Packer.cpp:131
#3  0x0000000100002e9c in PackerManager::update (this=0x7fff5fbffaa0, i=1) at Packer.cpp:295
#4  0x0000000100001ffa in Manager::run (this=0x7fff5fbff830, careAboutSupreme=false) at Manager.cpp:126
#5  0x000000010000511a in main (argc=0, argv=0x7fff5fbffb48) at main.cpp:173
(gdb) 

“order added 1”是我添加的一些日志记录,它发生在我的“Fetcher”类向“PackerManager”类发送订单时,该类确定要向哪个“Packer”对象添加订单。我修改了我的代码,以便所有订单都通过一个“Packer”对象进行路由,以进行调试。

3 -- 这意味着当“Packer”对象调用packItem(它将从我的队列中调用remove()时,该队列中只有一个订单。我已经在这些类之外进行了测试,它工作正常。

  1. 以下是一些相关代码:

来自“队列”

Queue::Queue()
{
    front = NULL;
    back = NULL;

    //head = NULL;
    //tail = NULL;
    count = 0;
}

//instert to back of queue
void Queue::insert(ElementType order)
{
    if(isEmpty()){
        back = new Node;
        back->order = order;
        back->next = NULL;
        front = back;
    }else{
        Node* tmp = new Node;
        tmp->order = order;
        back->next = tmp;
        back = back->next;
        back->next = NULL;
    }
    count++;
}

//checks if head & tail = NULL
bool Queue::isEmpty()
{
    if(front == NULL && back == NULL){
        return true;
    }
    return false;
}

在这一行抛出段错误:“ElementType retVal = front->order;”

ElementType Queue::remove()
{
    if(isEmpty()){
        cout << "\n\tYou cannot remove from an empty Queue... Exiting.\n\n";
        exit(1);
    }

    ElementType retVal = front->order;
    //delete front;
    if(front == back){
        front = NULL;
        back = NULL;
    }else{
        front = front->next;
    }

    count --;
    return retVal;
}

此代码调用从队列中删除。它调用的是“regularOrders”

//retreives next order to begin packing
void Packer::packItem(int time)
{
    if( careAboutSupreme && !supremeOrders.isEmpty() ){
        orderBeingPacked = supremeOrders.remove();
        orderBeingPackedStartTime = time;

    }else if( careAboutSupreme && isDelayOrderWaiting ){
        orderBeingPacked = orderBeingDelayed;
        orderBeingPackedStartTime = orderBeingDelayedStart + delayDuration;
        delayDuration = 0;
        isDelayOrderWaiting = false;

    }else if( !regularOrders.isEmpty() ){
        orderBeingPacked = regularOrders.remove();
        orderBeingPackedStartTime = time;
    }
}

此外,队列通过值 i = 1 且 i > 1 的测试:

#if Q
    Queue q1;
    Queue q2;
    for(int i=0 ; i<5 ; i++){
        Order o;
        q1.insert(o);
    }

    while(!q1.isEmpty()){
        q2.insert(q1.remove());
    }

    sortQueueByOrderNumber(q2);

    while(!q2.isEmpty()){
        q2.remove();
    }
    cout << "Expecting to exit now" << endl;
    q2.remove();
#endif

void sortQueueByOrderNumber(Queue q)
{
    if(q.getCount() == 0){
        return;
    }
    int cnt = q.getCount();
    Order* orderArray = new Order[cnt];
    Order tmpOrder;

    //put orders into array -- after this they are in order, but in an array
    while(q.getCount() != 0){
        tmpOrder = q.remove();
        orderArray[tmpOrder.orderNum-1] = tmpOrder;
    }

    //now we stuff em back into the queue, in order!
    for(int i=0 ; i<cnt ; i++){
        tmpOrder = orderArray[i];
        q.insert(tmpOrder);
    }
    delete[] orderArray;
}

请让我知道我还能提供什么。

编辑:看起来空指针不是这里的问题:我尝试添加:

ElementType retVal;
if(front == NULL){
    cout <<"oops!";
}else{
    retVal = front->order;
}

代码仍然属于分配 retVal = front->order。所以 front != NULL 但是,它认为 front->order 是一个错误的内存位置。

编辑2: 好的,这很奇怪。使用 clang++ 编译时,它实际上可以工作(对不起,如果我之前另有说明),而不是 g++。什么?

编辑 3 好的,所以我已将这些文件 scp'd 到另一台机器并尝试运行它。现在我遇到了 g++ 和 clang++ 的分段错误,但是,在完全与队列无关的不同区域中。

还有什么我应该看的可能会导致更广泛的内存问题的东西吗?

现在尝试更新其中一个打包程序对象时会发生段错误。代码如下,它无法更新时间(this->time = time)——我认为这不应该发生,这就是为什么我认为手头可能存在更大的问题。另外,我检查了 GDB,但 this->time 不是 NULL。

void Packer::update(int time)
{
    this->time = time;

    //if orderNum = 0 we havent gotten a real order yet. This is the default from the constructor for Order
    if(orderBeingPacked.orderNum == 0){
        packItem(time);
    }
4

2 回答 2

3

isEmpty()返回 false 并不能保证front不是NULL.

像这样放置断言isEmpty()

bool Queue::isEmpty()
{
    if(front == NULL || back == NULL){
        assert(front == NULL);
        assert(back == NULL);
        return true;
    }
    return false;
}

另外,您是否在构造函数中同时初始化和front初始化?backNULL


编辑: 要检查的另一件事是您的订单号是否不超过临时数组sortQueueByOrderNumber

void sortQueueByOrderNumber(Queue q)
{
    ...
    int cnt = q.getCount();
    Order* orderArray = new Order[cnt];
    Order tmpOrder;

    //put orders into array -- after this they are in order, but in an array
    while(q.getCount() != 0){
        tmpOrder = q.remove();

        assert(tmpOrder.orderNum >= 1);       // <<<<<<<<<<<<<<<<<<<<<
        assert(tmpOrder.orderNum-1 < cnt);    // <<<<<<<<<<<<<<<<<<<<<

        orderArray[tmpOrder.orderNum-1] = tmpOrder;
    }
    ...
}

EDIT2:另一个潜在的陷阱:确保在复制队列结构的地方实现赋值运算符和复制构造函数:

Queue::Queue(const Queue &other) :
  front(NULL),
  back(NULL),
  count(0)
{
  Node *node = other.front;
  while (node != NULL)
  {
    insert(node->order);
    node = node->next;
  }
}

否则,例如,在调用时sortQueueByOrderNumber(Queue q)(假设您没有像 Maciek B 指出的那样将其作为参考),您将共享(因为等待默认复制构造函数工作)多个 Queue 实例之间的队列节点,或者更糟糕的是,您将调用 delete在一个对象上的所有节点上超出范围(例如,在 结束时sortQueueByOrderNumber),而另一个Queue实例仍然指向(已删除)Node对象。

于 2013-03-09T14:18:56.460 回答
2

这个删除被注释掉了吗?

ElementType retVal = front->order;
//delete front;
if(front == back){
    front = NULL;
    back = NULL;
}else{
    front = front->next;
}

你不应该删除front然后使用front->next. 试试这个:

ElementType retVal = front->order;
Node* x = front;
if(front == back){
    front = NULL;
    back = NULL;
}else{
    front = front->next;
}
delete x;

编辑:

方法也按值sortQueueByOrderNumber接受Queue参数,因此任何更改frontback不会存储在原始Queue对象中。将签名更改为:

void sortQueueByOrderNumber(Queue& q)

如果仍然有问题,请使用valgrind. 它应该会发现那些内存问题,或者至少为您指明正确的方向。

于 2013-03-09T15:11:24.540 回答