动态增长的字符串阵列内存问题

发布于 2025-01-23 16:52:59 字数 3447 浏览 1 评论 0原文

我正在制作填字游戏程序,其中必须使用单词字典。我正在尝试将jspell字典文件加载到动态字符串阵列中,但我一直在得到

错误malloc():不匹配下一步

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "dictionary.h"


void dict_init(Dictionary * dict, char * dict_dir, size_t w_len)
{

    printf("dictionary.c (dict_init): initializing dictionary.\n");

    /*Adjust this value to control the initial array size*/
    size_t init_size = 1000;

    /*initialize dictionary file directory*/
    dict->dir = malloc(strlen(dict_dir) * sizeof(char) + 1);
    strcpy(dict->dir, dict_dir);

    /*create memory for words array*/
    dict->words = malloc(init_size * sizeof(char *));

    /*initialize array size*/
    dict->size = init_size;

    /*initilize word length*/
    dict->w_len = w_len;

    /*initialize word counter*/
    dict->counter = 0;

    /*load words into dictionary*/
    dict_load(dict);

    printf("dictionary.c (dict_init): dictionary initialized.\n");
}


void dict_add(Dictionary * dict, char * word)
{
    char ** dictionary = dict->words;

    /*check if word array is full*/
    if(dict->counter == dict->size)
    {
        /*increrase size of dictionary*/
        dict->size *= 1.5;
        dict->words = realloc(dict->words, dict->size * sizeof(char *));
    }

    /*add word to dictionary*/
    dictionary[dict->counter] = malloc(strlen(word) * sizeof(char) + 1);
    strcpy(dictionary[dict->counter], word);
    dict->counter++;

    free(word);
}


void dict_free(Dictionary * dict) 
{
    free(dict->words);
}


void dict_load(Dictionary * dict)
{
    FILE * fp;

    char * line = NULL;
    char * word = NULL;

    size_t len = 0;
    ssize_t read;

    fp = fopen(dict->dir, "r");

    /*check if file exists*/
    if (fp == NULL)
    {
        perror("ERROR: File not found.");
        exit(EXIT_FAILURE);
    }

    /*discard first line*/
    if(strstr(dict->dir, ".dic"))
        getline(&line, &len, fp);

    /*read file lines*/
    while ((read = getline(&line, &len, fp)) != -1) 
    {
        if(((strstr(line, "[CAT=punct") == NULL) && (word = parse_line(line, dict->w_len)) != NULL)) {
            dict_add(dict, word);
        }
    }

    fclose(fp);
    free(line);

    printf("dictionary.c (dict_load): dictionary loaded %ld words.\n", dict->counter);
}


char * parse_line(char * line, size_t w_len)
{
    int i;
    char s_tmp[101] = "";
    char * dlm_slash, * dlm_space, * dlm_tab , *substring;

    /*get delimiter pointer*/
    dlm_slash = strchr(line, '/');
    dlm_space = strchr(line, ' ');
    dlm_tab = strchr(line, '\t');

    /*check if delimiter exists in line*/
    if(dlm_slash != NULL)
        i = (int)(dlm_slash - line);

    else if(dlm_space != NULL)
        i = (int)(dlm_space - line);

    else if(dlm_tab != NULL)
        i = (int)(dlm_tab - line);

    else
    {
        /*replace '\n' with '\0'*/
        line[strcspn(line, "\n")] = '\0';
    
        i = strlen(line);
    }

    strncpy(s_tmp, line, i);

    substring = malloc(sizeof(char) * strlen(s_tmp) + 1);
    strncpy(substring, s_tmp, strlen(s_tmp));

    /*lowercase word*/
    lower_case(substring);

    if((is_valid(substring) == 0) && (strlen(substring) <= w_len))
        return substring;

    free(substring);
    return NULL;
}

I'm working on a crosswords program in which a word dictionary is necessary. I'm trying load a jspell dictionary file into an dynamic string array but i keep getting the

error malloc(): mismatching next->prev_size (unsorted)

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "dictionary.h"


void dict_init(Dictionary * dict, char * dict_dir, size_t w_len)
{

    printf("dictionary.c (dict_init): initializing dictionary.\n");

    /*Adjust this value to control the initial array size*/
    size_t init_size = 1000;

    /*initialize dictionary file directory*/
    dict->dir = malloc(strlen(dict_dir) * sizeof(char) + 1);
    strcpy(dict->dir, dict_dir);

    /*create memory for words array*/
    dict->words = malloc(init_size * sizeof(char *));

    /*initialize array size*/
    dict->size = init_size;

    /*initilize word length*/
    dict->w_len = w_len;

    /*initialize word counter*/
    dict->counter = 0;

    /*load words into dictionary*/
    dict_load(dict);

    printf("dictionary.c (dict_init): dictionary initialized.\n");
}


void dict_add(Dictionary * dict, char * word)
{
    char ** dictionary = dict->words;

    /*check if word array is full*/
    if(dict->counter == dict->size)
    {
        /*increrase size of dictionary*/
        dict->size *= 1.5;
        dict->words = realloc(dict->words, dict->size * sizeof(char *));
    }

    /*add word to dictionary*/
    dictionary[dict->counter] = malloc(strlen(word) * sizeof(char) + 1);
    strcpy(dictionary[dict->counter], word);
    dict->counter++;

    free(word);
}


void dict_free(Dictionary * dict) 
{
    free(dict->words);
}


void dict_load(Dictionary * dict)
{
    FILE * fp;

    char * line = NULL;
    char * word = NULL;

    size_t len = 0;
    ssize_t read;

    fp = fopen(dict->dir, "r");

    /*check if file exists*/
    if (fp == NULL)
    {
        perror("ERROR: File not found.");
        exit(EXIT_FAILURE);
    }

    /*discard first line*/
    if(strstr(dict->dir, ".dic"))
        getline(&line, &len, fp);

    /*read file lines*/
    while ((read = getline(&line, &len, fp)) != -1) 
    {
        if(((strstr(line, "[CAT=punct") == NULL) && (word = parse_line(line, dict->w_len)) != NULL)) {
            dict_add(dict, word);
        }
    }

    fclose(fp);
    free(line);

    printf("dictionary.c (dict_load): dictionary loaded %ld words.\n", dict->counter);
}


char * parse_line(char * line, size_t w_len)
{
    int i;
    char s_tmp[101] = "";
    char * dlm_slash, * dlm_space, * dlm_tab , *substring;

    /*get delimiter pointer*/
    dlm_slash = strchr(line, '/');
    dlm_space = strchr(line, ' ');
    dlm_tab = strchr(line, '\t');

    /*check if delimiter exists in line*/
    if(dlm_slash != NULL)
        i = (int)(dlm_slash - line);

    else if(dlm_space != NULL)
        i = (int)(dlm_space - line);

    else if(dlm_tab != NULL)
        i = (int)(dlm_tab - line);

    else
    {
        /*replace '\n' with '\0'*/
        line[strcspn(line, "\n")] = '\0';
    
        i = strlen(line);
    }

    strncpy(s_tmp, line, i);

    substring = malloc(sizeof(char) * strlen(s_tmp) + 1);
    strncpy(substring, s_tmp, strlen(s_tmp));

    /*lowercase word*/
    lower_case(substring);

    if((is_valid(substring) == 0) && (strlen(substring) <= w_len))
        return substring;

    free(substring);
    return NULL;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甩你一脸翔 2025-01-30 16:52:59

我认为这是基本问题:

void dict_add(Dictionary * dict, char * word) {
char ** dictionary = dict->words;       /* **** 1 **** */

/*check if word array is full*/
if(dict->counter == dict->size)
{
    /*increrase size of dictionary*/
    dict->size *= 1.5;                  /* **** 2 **** */
    dict->words = realloc(dict->words, dict->size * sizeof(char *));
                                        /* **** 3 **** */
}

/*add word to dictionary*/

这是一个问题:

dictionary[dict->counter] = malloc(strlen(word) * sizeof(char) + 1);
strcpy(dictionary[dict->counter], word);
dict->counter++;

free(word);                            /* **** 4 **** */
}

问题是dictionary在您称为之前保存了 reallocrealloc可能会进行全新的内存分配,在这种情况下,它将自动free()将其内容复制到新的内容后。因此,您在调用realloc之前制作的指针的任何副本都可能最终指向未分配的内存。写信给未分配的记忆是一个很大的禁忌。在这种特殊情况下,您可能会覆盖Malloc的簿记信息有关未分配的块的信息,这就是为什么它检测到问题并抱怨的原因。算上自己幸运的是:许多内存腐败问题都在相当长的一段时间内没有发现,直到工厂爆炸。

我在编写此书时注意到的其他一些问题,源中有编号的注释:

  1. 实际上根本不需要变量dictionary

  2. dict-&gt; size是一个整数。强迫转换为浮点数,然后将其截断回整数不是很有用。优先dict-&gt; size += dict-&gt; size/2;。最好是首先确保dict-&gt; size并不那么大,以至于增加它会导致整数环绕。 (这不是size_t之类的未签名类型的不确定行为,但不会产生正确的结果。)

  3. 您实际上可以使用临时性,因为realloc可能会使用返回null指示内存分配失败。如果发生这种情况,原始分配是不是自动释放的,您没有办法释放它。 (实际上,您这样做了,因为您有一个混乱地称为字典,但在第1点,我建议您摆脱它。)一个更惯用的调用是:

      if(dict-&gt; counter == dict-&gt; size){
        /*字典的增量酶大小*/
        dict-&gt; size += dict-&gt; size / 2; / *请参见第2点,上面 */
        char ** new_words = realloc(dict-&gt;单词,dict-&gt; size* sizeof(* new_words));
        if(new_words == null){
            / *报告分配错误,并释放您分配的所有内存 */
            /*然后可能退出(1),但是如果这是库函数,则只是
             *返回某种故障指示,以便呼叫者可以做
             *他们自己的清理。
             */
        }
        dict-&gt;单词= new_words;
    }
    dict-&gt;单词[dict-&gt; counter] = word; / *请参阅下方4点 */
     
  4. 您正在释放

    代码> word 在这里,因为它是在parse_line()中分配的。但是,如果您知道自己还是要释放它,那么首先将其副本就没有太多意义了。您最好只使用它。 (但是您需要记录以下事实,即此功能是作为参数传递的单词所有权的所有权。)

    像您一样进行副本可能被认为是清洁的,但是 释放了这一参数,而呼叫者可以这样做。这将具有允许呼叫者提供一个没有动态分配的单词,或者将单词用于其他目的。

  5. (在本摘要中没有指示,但仍然很重要)。 必须释放每个分配的内存的块。因此,您的程序应与执行malloc完全执行免费。但是你不那样做;您只需释放一系列单词指针,然后让单词指向该数组泄漏。您应该解决这个问题。 (请注意,您不需要额外的呼叫即可免费拨打Realloc,因为Realloc本身会释放旧块,如果它分配了新块。 免费。)


Here's the basic problem, I think:

void dict_add(Dictionary * dict, char * word) {
char ** dictionary = dict->words;       /* **** 1 **** */

/*check if word array is full*/
if(dict->counter == dict->size)
{
    /*increrase size of dictionary*/
    dict->size *= 1.5;                  /* **** 2 **** */
    dict->words = realloc(dict->words, dict->size * sizeof(char *));
                                        /* **** 3 **** */
}

/*add word to dictionary*/

This one is the problem:

dictionary[dict->counter] = malloc(strlen(word) * sizeof(char) + 1);
strcpy(dictionary[dict->counter], word);
dict->counter++;

free(word);                            /* **** 4 **** */
}

The problem is that dictionary was saved before you called realloc. realloc might make a brand-new memory allocation, in which case it will automatically free() the old one after copying its contents into the new one. So any copy of the pointer which you made before calling realloc might end up pointing to unallocated memory. Writing to unallocated memory is a big no-no; in this particular case, you're probably overwriting malloc's bookkeeping information about the unallocated block, which is why it detects the problem and complains. Count yourself lucky: lots of memory corruption problems go undetected for quite a while until the factory explodes.

Some other issues which I noticed while writing this, with numbered comments in the source:

  1. There's actually no need for the variable dictionary at all.

  2. dict->size is an integer. Forcing conversion to a floating point number and then truncating back to an integer is not very useful. Prefer dict->size += dict->size/2;. Even better would be to first make sure that dict->size isn't so big that increasing it will cause integer wraparound. (This is not undefined behaviour on unsigned types like size_t, but it's not going to produce correct results.)

  3. Here you could actually use a temporary, because realloc might return NULL indicating a memory allocation failure. If that happens, the original allocation is not automatically freed, and you don't have a way to free it. (Actually you do, since you have a variable confusingly called dictionary, but in point 1 I recommended that you get rid of it.) A more idiomatic call would be:

    if(dict->counter == dict->size) {
        /*increrase size of dictionary*/
        dict->size += dict->size / 2;   /* See point 2, above */
        char** new_words = realloc(dict->words, dict->size * sizeof(*new_words));
        if (new_words == NULL) {
            /* Report allocation error and free all the memory you've allocated */
            /* Then probably exit(1) but if this were a library function, just
             * return some kind of failure indication so that the caller can do
             * their own clean-up.
             */
        }
        dict->words = new_words;
    }
    dict->words[dict->counter] = word; /* See point 4, below */
    
  4. You're freeing word here because it was allocated in parse_line(). But if you know you're going to free it anyway, there wasn't much point making a copy of it first. You might as well just use it. (But you need to document the fact that this function takes ownership of the word passed as an argument.)

    It might be considered cleaner to do the copy as you do but then not free the argument, leaving it for the caller to do that. That would have the advantage of allowing the caller to provide a word which hadn't been dynamically allocated, or use the word for some other purpose.

  5. (Not indicated in this snippet, but nonetheless important). Every block of allocated memory must be freed. So your program should execute free exactly as many times as it executed malloc. But you don't do that; you just free the array of word pointers, and let the words pointed to in that array leak. You should fix that. (Note that you don't need an extra call to free for a call to realloc, since realloc itself frees the old block if it allocates a new one. You only need to match the initial malloc with a free.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文