内存泄漏和无效的读/写

时间:2016-10-09 14:36:39

标签: c memory memory-management memory-leaks valgrind

在我因为没有看到过多的Valgrind问题而被撕裂之前:我做到了。我花了很长时间观察,而且我发现的所有人都没有/** * Implements a dictionary's functionality. */ #include <ctype.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "dictionary.h" // number of characters we are using (a-z and ') #define LETTERS 27 // max guaranteed number of nonnegative char values that exist #define CHARVALUES 128 // create node structure for trie typedef struct node { struct node *children[LETTERS]; bool is_word; } node; // create root node for trie node *root; // stores the size of our dictionary unsigned int dict_size = 0; /** * Returns true if word is in dictionary else false. */ bool check(const char *word) { // keeps track of where we are; starts with root for each new word node *current_node = root; while (*word != '\0') { // indices: 'a' -> 0, ..., 'z' -> 25, '\' -> 26 int index = (tolower(*word) - 'a') % CHARVALUES; if (index >= LETTERS - 1) { // by assumption, the char must be '\'' if not '\n' or a letter index = LETTERS - 1; } // if the node we need to go to is NULL, the word is not here if (current_node->children[index] == NULL) { return false; } // go to the next logical node, and look at the next letter of the word current_node = current_node->children[index]; word++; } } return current_node->is_word; } /** * Loads dictionary into memory. Returns true if successful else false. */ bool load(const char *dictionary) { FILE *inptr = fopen(dictionary, "r"); if (inptr == NULL) { return false; } // allocate memory for the root node root = malloc(sizeof(node)); // store first letter (by assumption, it must be a lowercase letter) char letter = fgetc(inptr); // stores indices corresponding to letters int index = 0; /** * we can assume that there is at least one word; we will execute the loop * and assign letter a new value at the end. at the end of each loop, due * to the inside loop, letter will be a newline; we know the EOF in the * dictionary follows a newline, so the loop will terminate appropriately */ do { // keeps track of where we are; starts with root for each new word node *current_node = root; // this loop will only execute if our character is a letter or '\'' while (letter != '\n') { // indices: 'a' -> 0, ..., 'z' -> 25, '\' -> 26 index = (letter - 'a') % CHARVALUES; if (index >= LETTERS - 1) { // by assumption, the char must be '\'' if not '\n' or a letter index = LETTERS - 1; } // allocate memory for a node if we have not done so already if (current_node->children[index] == NULL) { current_node->children[index] = malloc(sizeof(node)); // if we cannot allocate the memory, unload and return false if (current_node->children[index] == NULL) { unload(); return false; } } // go to the appropriate node for the next letter in our word current_node = current_node->children[index]; // get the next letter letter = fgetc(inptr); } // after each linefeed, our current node represents a dictionary word current_node->is_word = true; dict_size++; // get the next letter letter = fgetc(inptr); } while (letter != EOF); fclose(inptr); // if we haven't returned false yet, then loading the trie must have worked return true; } /** * Returns number of words in dictionary if loaded else 0 if not yet loaded. */ unsigned int size(void) { return dict_size; } void clear(node *head) { for (int i = 0; i < LETTERS; i++) { if (head->children[i] != NULL) { clear(head->children[i]); } } free(head); } /** * Unloads dictionary from memory. Returns true if successful else false. */ bool unload(void) { clear(root); return true; } 正确的字节数。如果我不正确并且这是重复的,我将很乐意接受您的参考。

在这个程序中,我使用树来创建一个拼写检查器。我的代码中有很多东西需要修复,但内存泄漏是我真正需要帮助解决的问题。 (很多这段代码都是临时的,因此它会编译,以便我可以修复内存泄漏。)

问题在于我很确定我为节点分配了正确的空间量,我认为Valgrind确认了这一点,因为我只有2个未释放的块(365,371个分配中)。

无论如何,我将发布整个代码(以防任何人需要完整的上下文),但我认为相关的部分是加载函数和清除函数,我分别分配和释放内存。

==18981== HEAP SUMMARY:
==18981==     in use at exit: 448 bytes in 2 blocks
==18981==   total heap usage: 365,371 allocs, 365,369 frees, 81,843,792 bytes allocated
==18981== 
==18981== 448 (224 direct, 224 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==18981==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18981==    by 0x4011B0: load (dictionary.c:111)
==18981==    by 0x4008CD: main (speller.c:40)
==18981== 
==18981== LEAK SUMMARY:
==18981==    definitely lost: 224 bytes in 1 blocks
==18981==    indirectly lost: 224 bytes in 1 blocks
==18981==      possibly lost: 0 bytes in 0 blocks
==18981==    still reachable: 0 bytes in 0 blocks
==18981==         suppressed: 0 bytes in 0 blocks

相关的valgrind输出如下:

            if (current_node->children[index] == NULL)
            {
                current_node->children[index] = malloc(sizeof(node));

                // if we cannot allocate the memory, unload and return false
                if (current_node->children[index] == NULL)
                {
                    unload();
                    return false;
                }

            }

所以,我对这个输出的解释是,在下面的代码块中:

malloc

Function GroupString(s As String, groupSize As Long, _ Optional delim As String = ",", _ Optional prefix As String = "", _ Optional postfix As String = "") As String Dim n As Long, m As Long, i As Long Dim chunks As Variant n = Len(s) m = Int(n / groupSize) If n Mod groupSize = 0 Then ReDim chunks(0 To m - 1) Else ReDim chunks(0 To m) 'includes final chunk of size < groupSize End If For i = 0 To m - 1 chunks(i) = Mid(s, 1 + i * groupSize, groupSize) Next i If n Mod groupSize > 0 Then chunks(m) = Mid(s, 1 + m * groupSize) 'final chunk End If GroupString = prefix & Join(chunks, delim) & postfix End Function 语句(实际上是line dictionary.c:111)执行两次,以便永远不会释放分配的内存。 (这是正确的吗?)现在,这让我认为真正的问题在于我的明确功能,即它写得不好而且不能清除我的每个节点。

但是,我已经盯着代码几个小时,我几乎看不出它有什么问题。 (我确信很多;我对此并不太擅长。)

对此的任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

首先:

代码错过了将成员数组children初始化为每个节点NULL的所有malloc()malloc()不对分配的内存执行任何初始化。它只包含“垃圾”。

所以这里

       if (current_node->children[index] == NULL)
       {

成为一个“随机”的决定,如果不是已经引发了未定义的行为。

(顺便说一句,“无效读/写”你的问题的标题提及,你没有告诉我们,很可能也提到上面的这一行代码......) < / p>

要解决此问题,您可以使用以下内容:

#include <stdlib.h> (/* for malloc() */
#include <errno.h> (/* for errno */


/* Allocate and initialises a new node. */
/* Returns 0 on success and -1 on failure. */

int node_create(node ** pn)
{
  int result = 0; /* Be optimistic. */

  if (NULL == pn)
  {
    errno = EINVAL;
    result = -1;
  }
  else
  {
    node * n = malloc(sizeof *n);
    if (NULL == n)
    {
      result = -1;
    }
    else
    {
      for (size_t i = 0; i < LETTERS; ++i)
      {
        n -> children[i] = NULL;
      }

      n -> is_word = 0;
      (*pn) = n;
    }
  }

  return result;
}

并像这样使用它:

  ...

  if (-1 == node_create(&root))
  {
    perror("node_create() failed");
    return false;
  }