【问题标题】:Cannot get realloc() to work无法让 realloc() 工作
【发布时间】:2015-12-01 06:36:47
【问题描述】:
FILE *file;
file = fopen(argv[1], "r");
char *match = argv[2];
if (file == NULL) {
    printf("File does not exist\n");
    return EXIT_FAILURE;
}
int numWords = 0, memLimit = 20;
char** words = (char**) calloc(memLimit, sizeof(char));
printf("Allocated initial array of 20 character pointers.\n");
char string[20];

while (fscanf(file, "%[a-zA-Z]%*[^a-zA-Z]", string) != EOF) {
    words[numWords] = malloc(strlen(string) + 1 * sizeof(char));
    strcpy(words[numWords], string);
    printf("Words: %s\n", words[numWords]);
    numWords++;     /*keep track of indexes, to realloc*/ 
    if (numWords == memLimit) {
        memLimit = 2 * memLimit;
        words = (char**) realloc(words, memLimit * sizeof(char*));   /*Fails here*/
        printf("Reallocated array of %d character pointers.\n", memLimit);
    }

}

代码应该打开并读取一个包含标点符号、空格等单词的文件并存储在一个字符串中,但是在 20 次尝试后它会抛出一个错误,我似乎无法让 realloc() 在这里工作,我'我期望成为问题。该数组动态分配了 20 个字符指针,当达到限制时,它应该重新分配双倍。我该如何解决这个问题?

【问题讨论】:

  • sizeof 在第一个 words 分配应该是 sizeof(char*)。这只是一个错字吗?
  • 你的第一个calloc有问题,你有sizeof(char)而不是sizeof(char*)
  • @user3128077 这就是为什么你明确地使用sizeof(char *)。相反,写sizeof words[0]。无论words 是什么类型,这都会产生正确的大小。并且不要转换malloc()的返回值。

标签: c memory-management dynamic malloc realloc


【解决方案1】:

两个音符。首先,你永远不应该转换 calloc/malloc/realloc 的返回值。请参阅this 了解更多信息。

其次,正如其他人在 cmets 中指出的那样,第一个 calloc 语句使用 sizeof(char) 而不是 sizeof(char*) 应该的那样。

【讨论】:

  • 也许您应该提到,除了出于防御性编程目的之外,应该使用sizeof array[0] 而不是sizeof(explicit type)
【解决方案2】:

错误

  • 不要强制转换 ma​​lloc/calloc 返回。没必要。
  • 您的第一个sizeof 错误。应该是sizeof(char*)
  • scanf() 格式字符串。 %s 做得很好。

代码

以下代码对我有用(每行打印一个单词):

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

int main(int argc, char *argv[])
{
    FILE *file;
    file = fopen(argv[1], "r");
    char *match = argv[2];
    if (file == NULL) {
        printf("File does not exist\n");
        return EXIT_FAILURE;
    }
    int numWords = 0, memLimit = 20;
    char **words = calloc(memLimit, sizeof(char*));
    printf("Allocated initial array of 20 character pointers.\n");
    char string[20];

    while (fscanf(file, "%s", string) != EOF) {
        words[numWords] =
            malloc(strlen(string) + 1 * sizeof(char));
        strcpy(words[numWords], string);
        printf("Words: %s\n", words[numWords]);
        numWords++; /*keep track of indexes, to realloc */
        if (numWords == memLimit) {
            memLimit = 2 * memLimit;
            words = realloc(words, memLimit * sizeof(char *));  
            printf
                ("Reallocated array of %d character pointers.\n",
                 memLimit);
        }

    }
}

使用./realloc realloc.c调用

希望对你有帮助。

【讨论】:

    【解决方案3】:

    words 是一个指向指针的指针。这个想法是分配一个指针数组。
    下面是错误的,因为它分配给memLimit 字符而不是memLimit 指针。
    这是主要问题

    char** words = (char**) calloc(memLimit, sizeof(char)); // bad
    

    所以使用一个简单的习惯用法:分配memLimit 组的任何words 指向的组。它更易于编写、阅读和维护。

    char** words = calloc(memLimit, sizeof *words);
    

    避开while (scanf() != EOF) 漏洞。回想一下,各种结果可以来自scanf() 家族。它返回成功扫描字段的计数或EOF。这通常是至少 3 个选项中的 1 个。所以不要测试你不想要的一个结果,测试你想要的一个结果。

    // while (fscanf(file, "%[a-zA-Z]%*[^a-zA-Z]", string) != EOF) {
    while (fscanf(file, "%[a-zA-Z]%*[^a-zA-Z]", string) == 1) {
    

    上面的例子可能不是每个都返回0,但下面的例子很容易。

    int d;
    while (fscanf(file, "%d", &d) == 1) {
    

    @Enzo Ferber 正确地建议使用"%s"。进一步建议遵循上述习惯用法,将输入宽度限制为小于缓冲区大小 1。

    char string[20];
    while (fscanf(file, "%19s", string) == 1) {
    

    建议养成查看分配结果的习惯。

        // better to use `size_t` rather than `int `for array sizes.
        size_t newLimit = 2u * memLimit;
        char** newptr = realloc(words, newLimit * sizeof *newptr);
        if (newptr == NULL) {
           puts("Out-of-memory");
           // Code still can use old `words` pointer of size `memLimit * sizeof *words`
           return -1;
        }
        memLimit = newLimit;
        words = newptr;
      }
    

    【讨论】:

    • 感谢您的详细解释。当我在这里引用页面时,转换 char 是我通常看到的,但我会在下一次引用这些习语。我之前也使用过“%s”但是,在我的问题中,我的意思是在分隔时不包括标点符号空格,“\n”和“\t”,这就是我使用字符正则表达式的原因。有没有更好的方法来做到这一点?
    • @user3128077 1) “casting char”不是什么大问题,但请注意calloc(memLimit, sizeof *words); 中的sizeof *words。 2) 在 C 中,a-zA-Z 是扫描集 3) 通常有 256 个不同的承租人,约 32 个标点符号和 6 个以上的空格,更不用说数字、控制字符和其他可能了。再说一遍:指定你想要的,而不是你不想要的。除非第一个字符不是字母,否则使用 "%[a-zA-Z]%*[^a-zA-Z]" 是可以的。最好使用while (fscanf(file, "%*[^a-zA-Z]"), fscanf(file, "%19[a-zA-Z]", string) == 1)
    【解决方案4】:

    您的第一个分配是问题所在。您分配 20 个字符并将它们视为 20 个字符指针。您超出了分配的缓冲区并破坏了您的内存。

    由于堆损坏,第二次分配失败。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-23
      • 2014-12-26
      • 2017-12-22
      • 2012-02-28
      • 2012-01-29
      • 2014-12-16
      • 2013-04-19
      • 2014-09-20
      相关资源
      最近更新 更多