【问题标题】:Realloc, char**, segfaultrealloc、char**、段错误
【发布时间】:2014-06-05 14:31:10
【问题描述】:

有一个功能。它是 add_lexem 并在指定数组的末尾添加一个元素(char *)。如果没有剩余内存,它会分配一些额外的内存 (100 * sizeof(char *))。该函数会导致段错误,这就是问题所在。

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

void add_lexem(char **lexems, int *lexemsc, int *lexem_n, const char *lexem)
{
    if (*lexem_n >= *lexemsc) {
        lexems = realloc(lexems, sizeof(char *) * (*lexemsc + 100));
        *lexemsc += 100;
    }

    char *for_adding = malloc(sizeof(char) * strlen(lexem));
    strcpy(for_adding, lexem);
    lexems[*lexem_n] = for_adding;
    (*lexem_n)++;
}

int main(void)
{
    char **D = malloc(sizeof(char *) * 2);
    int lexemsc = 2;
    int lexem_n = 0;

    add_lexem(D, &lexemsc, &lexem_n, "MEOW");
    printf("%s\n", D[0]);

    add_lexem(D, &lexemsc, &lexem_n, "BARK");
    printf("%s\n", D[1]);

    // in this place lexem_n becomes equal lexemsc
    add_lexem(D, &lexemsc, &lexem_n, "KWARK"); 
    printf("%s\n", D[2]);

    return 0;
}

输出必须是

MEOW
BARK
KWARK

但它是

MEOW
BARK
Segmentation fault (core dumped)

【问题讨论】:

  • 如果您的意图是char 指针的扩展数组,您似乎缺少一级间接。 IE。 D 也必须通过地址传递,形式参数应该是 char *** lexems (我也不喜欢它,因为一旦你通过了 2 级间接,事情可能会很快向南,但它似乎适合你正在尝试做)。
  • 避免三星问题的干净方法是将char **和大小+使用的值放在一个结构中,从而也避免需要三个指针传递的参数。

标签: c segmentation-fault realloc


【解决方案1】:

您正在按值传递 lexeme 参数,而应该按地址传递:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

// removed unused void ccat(char *str, char c)

void add_lexem(char ***lexems, int *lexemsc, int *lexem_n, const char *lexem)
{
    if (*lexem_n >= *lexemsc) {
        *lexems = realloc(*lexems, sizeof(char *) * (*lexemsc + 100));
        *lexemsc += 100;
    }

    char *for_adding = malloc(sizeof(char) * strlen(lexem)+1);
    strcpy(for_adding, lexem);
    (*lexems)[*lexem_n] = for_adding;
    (*lexem_n)++;
}

int main(void)
{
    char **D = malloc(sizeof(char *) * 2);
    int lexemsc = 2;
    int lexem_n = 0;

    add_lexem(&D, &lexemsc, &lexem_n, "MEOW");
    printf("%s\n", D[0]);

    add_lexem(&D, &lexemsc, &lexem_n, "BARK");
    printf("%s\n", D[1]);

    // in this place lexem_n becomes equal lexemsc
    add_lexem(&D, &lexemsc, &lexem_n, "KWARK");
    printf("%s\n", D[2]);

    return 0;
}

输出

MEOW
BARK
KWARK

注意:三重间接(即 3-start-programming)并不是轻而易举的事情,尽管它实际上符合您似乎在这里尝试做的事情。仔细阅读上面的代码,确保你明白它是如何工作的。

编辑:为添加的字符串添加了终止符空间。 (不知道为什么我错过了它,因为其他人似乎都在第一次审查时发现了它,duh)。

注意:请参阅@wildplasser's answer 这个问题。老实说,这是最好的方法,因为它加强了字符串指针数组和 say-same 的大小之间的关系。如果可以重新编写代码以使用该模型,则应该这样做,并在这样做时选择该答案作为“正确”解决方案。

【讨论】:

    【解决方案2】:

    避免三星级编程的替代方法:将您需要的所有内容放在一个结构中:

    struct wordbag {
            size_t size;
            size_t used;
            char **bag;
            };
    
    void add_lexem(struct wordbag *wb, const char *lexem)
    {
        if (wb->used >= wb->size) {
            wb->bag = realloc(wb->bag, (wb->size+100) * sizeof *wb->bag );
            wb->size += 100;
        }
    
        wb->bag[wb->used++] = strdup(lexem);
    }
    

    【讨论】:

    • +1 这完全是应该做的,因为这些参数(指针数组和相同的大小)是紧密耦合的,应该保持在一个逻辑结构中。
    • 没错。不幸的是,OP 在检查您的复选框时有点太快了 ;-)
    【解决方案3】:

    主要问题是您通过值将D 传递给函数:赋值

    lexems = realloc(...);
    

    D 没有影响。在realloc 执行重新分配的情况下,D 成为一个悬空指针,因此取消引用它成为未定义的行为。

    您需要通过指针传递D,方法与传递lexemsc&amp;lexem_n 相同,这样realloc 的效果在main 函数中也可见。

    另外,你的add_lexem没有为被复制的字符串分配足够的内存:strlen不计算空终止符,所以这两行

    char *for_adding = malloc(sizeof(char) * strlen(lexem));
    strcpy(for_adding, lexem);
    

    '\0' 写入分配空间之后的一个字节。

    【讨论】:

    • +1:我不敢相信我错过了后者,因为它似乎是其他人都在捕捉的。
    【解决方案4】:

    问题可能来自:

     char *for_adding = malloc(sizeof(char) * strlen(lexem));
      strcpy(for_adding, lexem);
    

    尝试char *for_adding = malloc(sizeof(char) * (strlen(lexem)+1)); 为'\0 字符留出一些空间。

    编辑:@WhozCraig 似乎是对的!

    再见,

    【讨论】:

      猜你喜欢
      • 2013-03-29
      • 2014-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多