【问题标题】:Segmentation fault but unable to reason how, memory allocation looks fine to me分段错误但无法推理,内存分配对我来说看起来不错
【发布时间】:2016-02-21 21:35:07
【问题描述】:

我有一个节点,我定义它的全局指针变量如下:

typedef struct node
{
    char* word;
    struct node* next;
} node;

node* HashTable =  NULL;
node* HeadOfHashTable = NULL;

现在,我分配的内存如下:

void allocateMemory(int numOfElements, bool isRealloc, const char* word)
{
    if(!isRealloc)
    {
        printf("Allocating %d blocks\n", numOfElements);
        HashTable = malloc(sizeof(node*) * numOfElements);
    } else {
        printf("Reallocating %d blocks for %s", numOfElements, word);
        HashTable = realloc(HashTable, sizeof(node*) * numOfElements);
    }

    if(HashTable == NULL)
    {
        printf("### Out Of Memory ###\n");
        exit(0);
    }
    HeadOfHashTable = HashTable;
}

现在,我在下面的方法中传递一个 HASH 值和单词以放入哈希表中。我已经评论了我遇到段错误的地方。

void putInHashTable(char* ch, unsigned int hashValue)
{
    HashTable += hashValue;

    printf("Processing at address: %p and has value was %d\n", HashTable, hashValue);

    if(HashTable == NULL || HashTable == '\0' || HashTable == 0)
    {
        printf("Hash table is NULL");
    }

    if(HashTable->word == NULL)
    {
        HashTable->word = malloc(sizeof(char) * (LENGTH + 1));
        strcpy(HashTable->word, ch);
        printf("New word: %s\n", HashTable->word);
    } else {
        printf("### Collision detected ###\n"); // ***** BELOW LINE GIVES SEG FAULT ******
        printf("    Earlier value is %s, new value is %s and its pointer is %p\n", HashTable->word, ch, HashTable->next);
        putInLinkedList(ch);
    }

    HashTable = HeadOfHashTable;
}

以下是控制台日志:

Allocating 65336 blocks
Processing at address: 0xb7568c28 and has value was 388
New word: a
Processing at address: 0xb756b9a0 and has value was 1843
New word: aaa
Processing at address: 0xb7570c08 and has value was 4480
New word: aaas
Processing at address: 0xb75ae608 and has value was 36032
### Collision detected ###
Segmentation fault (core dumped)

我的疑惑:

  • 我正在分配 65336 个内存块,而我遇到段错误的点的哈希值为 36032,因此我确信指针变量 HashTable 具有有效的内存地址。那为什么会出现段错误?
  • 如果它不是一个有效的地址,那么为什么它没有在这个 IF 条件if(HashTable == NULL || HashTable == '\0' || HashTable == 0) 中被捕获。我什至使用了 calloc 然后我也遇到了 seg 错误并且上面的 IF 条件没有被捕获。
  • 我在这条线printf(" Earlier value is %s, new value is %s and its pointer is %p\n", HashTable->word, ch, HashTable->next); 遇到了段错误。这意味着在取消引用指针时出现了一些问题,那么为什么在此之前我没有遇到段错误,这意味着我应该只在这里遇到段错误 - if(HashTable->word == NULL)

【问题讨论】:

  • if(HashTable == NULL || HashTable == '\0' || HashTable == 0) 如果您不确定如何检查 NULL,请询问 stackoverflow。我们随时为您提供帮助!
  • sizeof(node*) --> sizeof(node)
  • 您的调试器会告诉您崩溃发生的位置。
  • HashTable = malloc(sizeof(node*) * numOfElements); ==> HashTable = malloc(sizeof *HashTable * numOfElements);
  • @hagrawal 实际上,您的调试器会告诉您的不仅仅是崩溃发生的地方。了解如何使用它,它将为您节省大量时间。

标签: c pointers segmentation-fault


【解决方案1】:

数星星。

 FOO * foo_ptr = malloc (sizeof (FOO) * n_foos);
 //  ^                                ^             
 //  |                                |
 //  one star to the left of `=`      one star to the right of `=`

经验法则:作业两边的星数必须相同。为什么?

      sizeof(FOO)    sizeof(FOO)    sizeof(FOO)   
     _____________  _____________  ______________
    /             \/             \/              \
     _____________  _____________  ______________
    [_____FOO_____][_____FOO_____][______FOO_____]
    ^
    |
    FOO* foo_ptr; // a FOO* points to a FOO
                  // pointer arithmetic works by incrementing the address\
                  // by sizeof(FOO)
                  // and so on

其他优秀代码示例:

 FOO ** foo_ptr = malloc (sizeof (FOO*) * n_foos); // same number of stars
 FOO *** foo_ptr = malloc (sizeof (FOO**) * n_foos); // still same

错误代码:

 FOO ** foo_ptr = malloc (sizeof (FOO) * n_foos); // numbers don't match
 FOO * foo_ptr = malloc (sizeof (FOO*) * n_foos); // numbers don't match

你的线路

HashTable = malloc(sizeof(node*) * numOfElements);

(将HashTable 的类型替换为node* 后)直接落入错误代码箱,因此请尝试修复它。

如果你想要一个节点数组:

HashTable = malloc(sizeof(node) * numOfElements);

如果你想要一个节点数组,你也可以拥有它。这不是真正推荐的,因为节省的空间很小,性能下降可能会很大,并且代码不太优雅。但你可以拥有它:

node** HashTable = malloc(sizeof(node*) * numOfElements); // count! the! stars!

恭喜!您现在有一个 numOfElements 未初始化 指针数组。现在您需要将它们初始化为某个值,通常为 NULL:

for (i = 0; i < numOfElements; ++i) HashTable[i] = NULL;

而且每次你​​想给表赋值时都需要分配一个新的node

if (HashTable[hashValue] == NULL) 
{
  HashTable[hashValue] = malloc(sizeof(node));
  if (HashTable[hashValue] == NULL) 
  {
    panic ("Out of memory!");
  }
  HashTable[hashValue]->word = ...
  HashTable[hashValue]->next = ...
} 
else 
{
  // collision etc
}

在我们讨论的时候,请注意这些与主要问题相切的时刻:如何正确检查 NULL,如何检查 malloc 的返回值,以及如何使用数组索引而不是变异一个来回的全局指针变量。 (如果你想使用指针算法,在putInHashTable 中有一个 local 指针变量)。

(当然如果你不使用n_foos,或者使用calloc,你需要对星数做出心理调整)。

【讨论】:

  • 谢谢。我从@BLUEPIXY 评论中发现了这个问题。实际上我想要的只是为指针变量分配空间,这样我就不会占用内存中的大量空间,而只是指针变量空间。所以,理想情况下,我应该在HashTable += hashValue; 这一行之后做一个malloc(sizeof(node)),但是我的疑问是我怎么知道我是否已经为这个指针地址做了一个malloc?这实际上是一个 HashTable,所以如果我已经对空间进行了 malloc,那么我就不想再做一次 malloc,因为我会在类似的哈希值处丢失我的旧值。
  • 也许你想用你的经验法则来说明可能有例外 - FOO * foo_ptr = malloc (sizeof (FOO));。即使 RHS 和 LHS 的星数不匹配,但仍然是有效的分配。
  • 查看更新。是的,也有例外,它们在最后一行进行了总结。
  • 谢谢老兄,我会审核你的更新,给我一些时间。
  • 感谢您的回答和时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 2015-10-06
  • 1970-01-01
相关资源
最近更新 更多