You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何定位并解决双向游标链表数据结构中出现的Heisenbug?

双向链表拷贝构造随机段错误(Heisenbug)排查

我正在实现一款修改版双向链表数据结构,在对功能做单元测试时遇到了一个无法解决的Heisenbug。

问题复现代码

触发随机段错误的测试代码如下:

case CURSOR_LIST_COPY_CONSTRUCTOR:
{
  // Test to construct a cursor list by using the copy constructor.
  if (verbose) {
    os << "\nCURSOR_LIST_COPY_CONSTRUCTOR:" << std::endl <<
      "Starting the construction operation:" <<
      std::endl;
  }
  anil::cursor_list my_cursor_list;
  my_cursor_list.append(1);
  anil::cursor_list my_copied_cursor_list = my_cursor_list;
  if (&my_copied_cursor_list == nullptr &&
      my_copied_cursor_list.index() != -1 &&
      my_copied_cursor_list.size() != 1 &&
      my_copied_cursor_list.front_data() != 1 &&
      my_copied_cursor_list.back_data() != 1) {
    if (verbose) {
      os << "Copy construction unsuccessful!" << std::endl;
    }
    return false;
  } else {
    if (verbose) {
      os << "Copy construction successful!" << std::endl;
    }
    return true;
  }
  return false;
  break;
}

每运行10次约有2次会在my_copied_cursor_list.front_data() != 1这一行触发段错误。一开始无法在gdb中复现该bug,参照同类问题解决方案,在gdb中执行set disable-randomization off命令后成功复现了相同的bug。

相关函数源码

front_data 函数

int anil::cursor_list::front_data() {
  if (this != nullptr && this->is_empty() == false) {
    return this->front->data;
  }
}

曾尝试新增this->front非空判断,修改后依然触发段错误,修改后的代码如下:

int anil::cursor_list::front_data() {
  if (this != nullptr && this->is_empty() == false) {
    if (this->front != nullptr) {
      return this->front->data;
    }
  }
}

append 函数

void anil::cursor_list::append(int new_data) {
  if (this != nullptr) {
    cursor_list_node* new_node = new cursor_list_node;
    new_node->data = new_data;
    new_node->next = nullptr;
    new_node->previous = this->back;
    if (this->is_empty() == false) {
      this->back->next = new_node;
    } else {
      this->front = new_node;
    }
    this->back = new_node;
    ++this->m_size;
  }
}

类声明代码

#ifndef ANIL_CURSOR_LIST_H
#define ANIL_CURSOR_LIST_H

#include <cstddef>
#include <iostream>

namespace anil {
  class cursor_list_node {
    private:
      int data;
      cursor_list_node* next;
      cursor_list_node* previous;
      friend class cursor_list; 
  };

  class cursor_list {
    private:

      // Data:
      int m_index;
      int m_size;
      cursor_list_node* front;
      cursor_list_node* back;
      cursor_list_node* cursor;

      // Functions:
      void delete_list();

    public:
      cursor_list() : m_index(-1), m_size(0), front(nullptr), back(nullptr),
        cursor(nullptr) {}
      cursor_list(cursor_list& copied_list);
      bool is_empty();
      int size();
      int index();
      int front_data();
      int back_data();
      int cursor_data();
      bool operator==(cursor_list& rhs); // rhs = right hand side
      cursor_list& operator= (cursor_list& rhs);  // rhs = right hand side
      friend std::ostream& operator<<(std::ostream& out, const cursor_list& rhs); // rhs = right hand side
      void clear();
      void move_cursor_front();
      void move_cursor_back();
      void move_cursor_prev();
      void move_cursor_next();
      void prepend(int new_data);
      void append(int new_data);
      void insert_before_cursor(int new_data);
      void insert_after_cursor(int new_data);
      void delete_front();
      void delete_back();
      void delete_cursor();
      ~cursor_list();
  };
}

#endif /* ANIL_CURSOR_LIST_H */

报错信息

gdb中的段错误提示如下:

Program received signal SIGSEGV, Segmentation fault.
0x00000000004018c8 in anil::cursor_list::front_data (this=0x7ffe055100c0)
    at anil_cursor_list.cpp:82
82      return this->front->data;

运行环境为Ubuntu 16.04.6 LTS。

调试过程

使用gdb的next和step命令逐行运行程序,在段错误发生前打印了this和this->front的值,结果如下:

Breakpoint 1, run_tests (os=..., bst_test=1, verbose=false) at main.cpp:121
121         anil::cursor_list my_copied_cursor_list = my_cursor_list;
(gdb) next
138         if (my_copied_cursor_list.index() != -1) {}
(gdb) 
139         if (my_copied_cursor_list.size() != 1) {}
(gdb) 
140         if (my_copied_cursor_list.front_data() != 1) {}
(gdb) step
anil::cursor_list::front_data (this=0x7ffc683c4500) at anil_cursor_list.cpp:79
79    if (this != nullptr && this->is_empty() == false) {
(gdb) next
80      return this->front->data;
(gdb) print this
$7 = (anil::cursor_list * const) 0x7ffc683c4500
(gdb) print this->front
$8 = (anil::cursor_list_node *) 0xe92cb976bc5a2900
(gdb) next

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401956 in anil::cursor_list::front_data (this=0x7ffc683c4500)
    at anil_cursor_list.cpp:80
80      return this->front->data;

问题解决

该Heisenbug的根本原因是拷贝构造函数未初始化类成员变量,修改拷贝构造函数,在执行拷贝逻辑前先初始化所有成员变量后,段错误不再复现。修改后的拷贝构造函数代码如下:

this->m_index = -1;
this->m_size = 0;
this->front = nullptr;
this->back = nullptr;
this->cursor = nullptr;
if (copied_cursor_list.is_empty() == false) {
  for (cursor_list_node* it = copied_cursor_list.front; it != nullptr;
       it = it->next) {
    this->append(it->data);
  }
}

内容的提问来源于stack exchange,提问作者Anil Celik Maral

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 17:36:11