【问题标题】:Pointer seg faulting although I malloc-ed right尽管我正确分配了指针段错误
【发布时间】:2019-03-08 17:38:05
【问题描述】:

我不明白为什么我的程序在这一行出现段错误:if ((**table->table).link == NULL){ 我似乎有 malloc-ed 内存,我尝试用 gdb 查看它。 *table->table 可访问且不为 NULL,但 **table->table 不可访问。 hash_t的定义:

struct table_s  {   
    struct node_s **table;
    size_t bins;    
    size_t size;
};

typedef struct table_s *hash_t;

void set(hash_t table, char *key, int value){
    unsigned int hashnum = hash(key)%table->bins;
    printf("%d \n", hashnum);
    unsigned int i;
    for (i = 0; i<hashnum; i++){
        (table->table)++;
    }
    if (*(table->table) == NULL){
        struct node_s n = {key, value, NULL};
        struct node_s *np = &n;
        *(table->table) = malloc(sizeof(struct node_s));
        *(table->table) = np;
    }else{
        while ( *(table->table) != NULL){
        if ((**table->table).link == NULL){
            struct node_s n = {key, value, NULL};
            struct node_s *np = &n;
            (**table->table).link = malloc(sizeof(struct node_s));
            (**table->table).link = np;
            break;
        }else if (strcmp((**table->table).key, key) == 0){
            break;
        }
            *table->table = (**(table->table)).link;
        }
        if (table->size/table->bins > 1){
            rehash(table);
        }
    }
}

我从这里调用 set:

  for (int i = 0; i < trials; i++) {
     int sample = rand() % max_num;
     sprintf(key, "%d", sample);
     set(table, key, sample);
  }

【问题讨论】:

  • 这是很多取消引用。你为什么不告诉我们hash_t的定义?
  • 你也应该告诉我们你是如何调用set的,问题的根源可能在调用代码中。请阅读此:How to Ask 和此:minimal reproducible example
  • 当您有复杂的解引用表达式,并且您的代码中有错误时,请打开它。创建临时变量,一次做一个引用。如果您在编写该代码时没有发现问题,请在调试器下运行它,通常很容易找出您取消引用无效指针的确切位置。
  • 使用调试器:单步调试代码并检查变量

标签: c pointers hash segmentation-fault malloc


【解决方案1】:

你的哈希表是这样工作的:你有bins bins,每个 bin 都是一个键/值对的链表。 bin 中的所有项目共享相同的哈希码,以 bin 的数量为模。

在创建或初始化哈希表时,您可能已经创建了 bin 表,如下所示:

table->table = malloc(table->bins * sizeof(*table->table);

for (size_t i = 0; i < table->bins; i++) table->table[i] = NULL;

现在为什么会员table有两颗星?

  • “内”星表示表存储指向节点的指针,而不是节点本身。
  • “外部”开始是分配内存的句柄。如果您的哈希表是固定大小的,例如总是有 256 个 bin,您可以将其定义为:

    struct node_s *table[256];
    

    如果你传递这个数组,它会变成(或“衰减”成)指向它的第一个元素 struct node_s ** 的指针,就像你从 malloc 得到的数组一样。

  • 您可以通过链表访问 l´bins 的内容,链表的头部itable-&gt;table[i]

你的代码还有其他问题:

  • 您想通过(table-&gt;table)++ 实现什么目标?这将使分配的内存的句柄不指向第一个元素,而是指向下一个元素。在执行了 hashnum 次之后,*table-&gt;table 现在将位于正确的节点,但您将丢失必须保留的原始句柄,因为您必须稍后在清理哈希表时将其传递给 free。不要丢失分配内存的句柄!请改用另一个本地指针。

  • 您创建一个本地节点n,然后使用指向该节点的指针在您的链表中创建一个链接。但是节点n 将在您离开该功能后消失,并且链接将“陈旧”:它将指向无效内存。您还必须使用malloc 为节点创建内存。

您的 has 表的简单实现可能是:

void set(hash_t table, char *key, int value)
{
    unsigned int hashnum = hash(key) % table->bins;

    // create (uninitialised) new node
    struct node_s *nnew = malloc(sizeof(*nnew));

    // initialise new node, point it to old head
    nnew->key = strdup(key);
    nnew->value = value;
    nnew->link = table->table[hashnum];

    // make the new node the new head
    table->table[hashnum] = nnew;
}

这使得新节点成为链表的头部。这并不理想,因为如果您覆盖项目,则会找到新项目(这很好),但旧项目仍会在表中(这不好)。但是,正如他们所说,这留给读者作为练习。

(strdup 函数不是标准的,但可以广泛使用。它还会创建新的内存,您必须稍后释放它,但它确保字符串“lives”(仍然有效)在您完成哈希表。)

请注意代码中有多少颗星。如果一颗星太少,那就是在hash_t,你已经把指针的性质去掉了。

【讨论】:

    猜你喜欢
    • 2012-04-01
    • 2018-07-31
    • 1970-01-01
    • 2016-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-23
    • 2020-12-28
    相关资源
    最近更新 更多