尝试打印 2D 矢量时可能导致分割故障的原因

What could be causing the Segmentation Fault when attempting to print a 2D vector

提问人:underloaded_operator 提问时间:5/28/2023 最后编辑:underloaded_operator 更新时间:5/28/2023 访问量:147

问:

这是一个家庭作业问题。我正在处理我的作业,在尝试打印表格时遇到了一些问题。Hash Table

约束:

  • C++11
  • 否或std::mapstd::unordered_map

最小可重复示例

#include <iostream>
#include <vector>
#include <atomic>
#include <ctime>
#include <iomanip>
#include <string>

class DataEntry {
public:
  std::string get_date() { return date; }
  std::string get_country() { return country; }
  int get_c_cases() { return c_cases; }
  int get_c_deaths() { return c_deaths; }
  inline void set_date(std::string set_date) { this->date = set_date;};
  inline void set_country(std::string set_country) { this->country = set_country;};
  inline void set_c_deaths(int set_c_deaths) { this->c_deaths = set_c_deaths;};
  inline void set_c_cases(int set_c_cases) { this->c_cases = set_c_cases;};

private:
  std::string date;
  std::string country;
  int c_cases;
  int c_deaths;
};

class CovidDB {
private:
  std::vector<std::vector<DataEntry*>> HashTable;
  int size = 17;

public:
  void display_table();
  bool add(DataEntry* entry);
  int hash(std::string country);
};

int CovidDB::hash(std::string country) {
  int sum = 0;
  int count = 0;
  
  for (char c : country) {
    sum = sum + ((count + 1) * c);
    count++;
  }
  return sum % size; //returns the hash
}

void CovidDB::display_table() {
  for (const auto& vec : HashTable) {
    for (const auto& entry : vec) {
      if (entry != nullptr) {
        std::cout << "[Date: " << entry->get_date() << "], "
                  << "[Country: " << entry->get_country() << "], "
                  << "[Cases: " << entry->get_c_cases() << "], "
                  << "[Deaths: " << entry->get_c_deaths() << "]" << std::endl;
      }
    }
  }
}

bool CovidDB::add(DataEntry* entry) {
  time_t now = time(0);
  tm* ltm = localtime(&now);
  // DATE FORMAT: mm/dd/yy
  std::string current_date_str = std::to_string(1 + ltm->tm_mon) + "/" + std::to_string(ltm->tm_mday) + "/" + std::to_string(ltm->tm_year % 100);
  std::istringstream iss(current_date_str);
  std::tm current_date = {};
  iss >> std::get_time(&current_date, "%m/%d/%y");
  
  std::tm entry_date = {};
  std::istringstream iss2(entry -> get_date());
  iss2 >> std::get_time(&entry_date, "%m/%d/%y");
  
  if (mktime(&current_date) > mktime(&entry_date)) {
    std::cout << "[Record rejected]" << std::endl;   
    return false;
  }
  
  int index = hash(entry -> get_country());
  
  if (HashTable[index].empty()) {
    HashTable[index].push_back((entry));
  } else {
    bool added = false;
    for (DataEntry* existing_entry : HashTable[index]) {

      std::atomic<bool> valid(false);
      valid.store(hash(existing_entry->get_country()) == hash(entry->get_country()) &&
                       existing_entry->get_country() == entry->get_country());
      if (valid) {
        existing_entry->set_date(entry -> get_date());
        existing_entry->set_c_cases(existing_entry->get_c_cases() + entry->get_c_cases());
        existing_entry->set_c_deaths(existing_entry->get_c_deaths() + entry->get_c_deaths());
        added = true;
        delete entry;
        break;
      }
    }
    if (!added) {
      HashTable[index].push_back(entry);
    }
  }
  return true;
  return true;
}

int main() {
  CovidDB db;

  // Create two DataEntry objects
  DataEntry* entry1 = new DataEntry();
  entry1->set_date("01/01/23");
  entry1->set_country("Slovenia");
  entry1->set_c_cases(1);
  entry1->set_c_deaths(1);

  DataEntry* entry2 = new DataEntry();
  entry2->set_date("02/02/23");
  entry2->set_country("Slovenia");
  entry2->set_c_cases(1);
  entry2->set_c_deaths(1);

  // Add the entries to the CovidDB
  db.add(entry1);
  db.add(entry2);

  // Display the table
  db.display_table();

  // Clean up memory
  delete entry1;
  delete entry2;

  return 0;
}

打印功能似乎在 80% 的时间(估计)工作正常,例如,当我将数据从文件推送到 2D 矢量并打印时,它工作正常,但是当我尝试对相同的 2 个条目进行分组时.csvhash

例如:

Country: Slovenia
Date: 01/01/23
Cases: 1
Deaths: 1
Country: Slovenia
Date: 02/02/23
Cases: 1
Deaths: 1

它分段错误...我知道这意味着一个人正在尝试访问不属于他的内存(通常试图删除一个SEGFAULTnullptr)

在在这里问一个问题之前,我尝试了很多东西:

橡皮鸭调试

我注意到外部循环通过在循环之间放置来运行时间,这意味着当涉及到 .hash-1std::court << "here";entry

我还更改了 和 to,因为外部向量是向量的向量,而内部向量是指针的向量。autostd::vecotr<DataEntry*> vecDataEntry*DataEntry

调试

我尝试使用 调试可执行文件,因为我无法配置我的 Vs Code 调试器,但它抛出错误gbd

Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-211.el8.x86_64 libgcc-8.5.0-16.el8_7.x86_64 libstdc++-8.5.0-16.el8_7.x86_64

我用谷歌搜索了一下,我找到的唯一解决方案是root安装这些库,我不能,因为这是学校的服务器,运行会让我被踢掉。sudo

我尝试了 XCode 调试器,但由于某种原因,我的代码运行方式不同......难道是因为一个是 Linux 而另一个不是?ssh

警卫

我添加了“守卫”以防止打印空的哈希表,并添加了一个守卫,以便我不会取消引用 不幸的是,没有效果。nullptr

C++ C++11 向量

评论

0赞 PaulMcKenzie 5/28/2023
delete entry;-- 如果没有分配 ,这是错误的。entrynew
0赞 underloaded_operator 5/28/2023
entry分配了 。我在 SO 上知道说是 C++ 编程的 Java 风格,但这就是我在学校被教导分配内存的方式newDataEntry *entry = new DataEntry
0赞 PaulMcKenzie 5/28/2023
但这就是我在学校被教导分配记忆的方式-- -- 这把一切都搞砸了。此外,您应该发布一个最小的可重现示例DataEntry entry; YourHashTable.add(&entry);
0赞 Retired Ninja 5/28/2023
delete entry;我也担心,这取决于它是如何称呼的。如果该条目在函数的早期被拒绝,为什么不删除它?您必须使用指针吗?如果没有,它会更安全。
0赞 underloaded_operator 5/28/2023
是的,我试过这样做,但我想出的最好的仍然是至少 100 行代码,所以我认为只添加方法会是最好的

答:

2赞 PaulMcKenzie 5/28/2023 #1

给定程序,一个问题是 ,在日期代码后的第一行:mainadd()

int index = hash(entry->get_country());
if (HashTable[index].empty()) {
    HashTable[index].push_back((entry));
}

这是一个空向量,但您正在 处访问它,这是越界的。HashTableindex

如果使用 而不是 ,则错误应该变得明显。at()[]

terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 6) >= this->size() (which is 0)

编辑:由于您提到构造函数将内部向量的大小调整为 17 个条目,另一个潜在的错误是函数是传递的条目,但您的程序在调用后也是相同的条目。add()deletemaindeleteadd()

bool CovidDB::add(DataEntry* entry) {
//...
if (valid) {
        existing_entry->set_date(entry -> get_date());
        existing_entry->set_c_cases(existing_entry->get_c_cases() + entry->get_c_cases());
        existing_entry->set_c_deaths(existing_entry->get_c_deaths() + entry->get_c_deaths());
        added = true;
        delete entry;  // <-- This will issue a delete call on entry
        break;

...

int main()
{
   ...
   // Clean up memory
   delete entry1;
   delete entry2;

解决方法是删除函数中的。deleteadd()

评论

0赞 underloaded_operator 5/28/2023
是的,对不起,这有点让我不好。我忘了添加我的构造函数...构造函数只是为了让它不会超出我所认为的界线。欣赏你的答案size = 17HashTable.resize(size);
0赞 PaulMcKenzie 5/28/2023
我更新了答案。请注意,无论向量的大小如何,您都应该检查是否在向量的范围内。index