是什么导致realloc()无效的下一个大小错误?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了是什么导致realloc()无效的下一个大小错误?相关的知识,希望对你有一定的参考价值。

我必须编写一个程序,首先要从用户那里读取文本,然后将其分成段落,句子和单词。我设法使它适用于小文本,但对于大文本,我得到了realloc()无效的下一个尺寸错误。我设法跟踪导致错误的块,但我不知道为什么导致该错误。这是代码(https://gist.github.com/Kritsos/ed576224a36db0f68323bec6d541c286):

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

void remove_special_char(char *A)

    int k, length, i;

    length = strlen(A);
    do
    
        k = strcspn(A, ".,!?;");
        if(k != length)
            A[k] = ' ';
    
    while(k != length);

    if(A[0] == ' ')
        for(i = 0;i < length;i++)
            A[i] = A[i + 1];

    for(i = 0;i < length;i++)
        if(!((A[i] >= 'a' && A[i] <= 'z') || (A[i] >= 'A' && A[i] <= 'Z')) && (A[i + 1] == ' ' || A[i + 1] == '\0'))
            for(k = i;k < length;k++)
                A[k] = A[k + 1];




int main()

    char **paragraphs = malloc(sizeof(char *)), **sentences = malloc(sizeof(char *)), **words = malloc(sizeof(char *)), *option;
    int num_par = 0, num_sent = 0, num_words = 0, i, quit = 0, count, length, j;

    if(paragraphs == NULL || sentences == NULL || words == NULL)
        return 1;

    do
    
        option = malloc(10000 * sizeof(char));
        if(option == NULL)
            return 1;
        fgets(option, 10000, stdin);
        option[strcspn(option, "\n")] = '\0';
        length = strlen(option);
        option = realloc(option, (length + 1) * sizeof(char));
        if(option == NULL)
            return 1;

        if(strstr(option, "ap:"))
        
            //paragraphs
            num_par++;
            paragraphs = realloc(paragraphs, num_par * sizeof(char *));
            if(paragraphs == NULL)
                return 1;
            paragraphs[num_par - 1] = malloc((length - 2) * sizeof(char)); //length - 2 --> -3 ap: + 1 '\0'
            if(paragraphs[num_par - 1] == NULL)
                return 1;
            strcpy(paragraphs[num_par - 1], option + 3);
            remove_special_char(paragraphs[num_par - 1]);

            //sentences
            count = 0;//number of sentences
            for(i = 0;i < length;i++)
                if(option[i] == '.' || option[i] == '!' || option[i] == '?' || option[i] == ';')
                    count++;

            i = 3;
            while(count > 0)
            
                num_sent++;
                sentences = realloc(sentences, num_sent * sizeof(char *));
                if(sentences == NULL)
                    return 1;
                sentences[num_sent - 1] = malloc(200 * sizeof(char));
                if(sentences[num_sent - 1] == NULL)
                    return 1;

                j = 0;
                while(option[i] != '.' && option[i] != '!' && option[i] != '?' && option[i] != ';')
                    sentences[num_sent - 1][j++] = option[i++];
                i++;
                sentences[num_sent - 1][j] = '\0';
                sentences[num_sent - 1] = realloc(sentences[num_sent - 1], (strlen(sentences[num_sent - 1]) + 1) * sizeof(char));
                if(sentences[num_sent - 1] == NULL)
                    return 1;
                remove_special_char(sentences[num_sent - 1]);
                count--;
            

  //THIS BLOCK RIGHT HERE CAUSES THE ERROR           
            //words
            count = 1; //number of words in paragraphs[num_par - 1] && words = spaces + 1
            length = strlen(paragraphs[num_par - 1]);
            for(i = 0;i < length;i++)
                if(paragraphs[num_par - 1][i] == ' ')
                    count++;

            i = 0;
            while(count > 0)
            
                num_words++;
                words = realloc(words, num_words * sizeof(char *));
                if(words == NULL)
                    return 1;

                words[num_words - 1] = malloc(50 * sizeof(char));
                if(words[num_words - 1] == NULL)
                    return 1;

                j = 0;
                while(paragraphs[num_par - 1][i] != ' ')
                    words[num_words - 1][j++] = paragraphs[num_par - 1][i++];
                i++;
                words[num_words - 1][j] = '\0';
                words[num_words - 1] = realloc(words[num_words - 1], (strlen(words[num_words - 1]) + 1) * sizeof(char));
                if(words[num_words - 1] == NULL)
                    return 1;
                count--;
            
  //END OF FAULTY BLOCK
        

        if(strstr(option, "qt"))
            quit = 1;
        free(option);

    
    while(!(quit));

    //test
    for(i = 0;i < num_par;i++)
        printf("%s\n", paragraphs[i]);
    printf("--------------- %d\n",num_par);
    for(i = 0;i < num_sent;i++)
        printf("%s\n", sentences[i]);
    printf("--------------- %d\n",num_sent);
    for(i = 0;i < num_words;i++)
        printf("%s\n", words[i]);
    printf("--------------- %d\n",num_words);

    //memory leaks
    for(i = 0;i < num_par;i++)
        free(paragraphs[i]);
    free(paragraphs);
    for(i = 0;i < num_sent;i++)
        free(sentences[i]);
    free(sentences);
    for(i = 0;i < num_words;i++)
        free(words[i]);
    free(words);

    return 0;


[也很抱歉,阅读和遵循它有点困难。这是valgrind报告:

==6252== Invalid read of size 1
==6252==    at 0x109033: main (main.c:112)
==6252==  Address 0x522fd66 is 0 bytes after a block of size 6 alloc'd
==6252==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6252==    by 0x108BEB: main (main.c:57)
==6252== 
==6252== Invalid read of size 1
==6252==    at 0x10900F: main (main.c:113)
==6252==  Address 0x522fd66 is 0 bytes after a block of size 6 alloc'd
==6252==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6252==    by 0x108BEB: main (main.c:57)
==6252== 
==6252== Invalid write of size 1
==6252==    at 0x109012: main (main.c:113)
==6252==  Address 0x522ffe2 is 0 bytes after a block of size 50 alloc'd
==6252==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6252==    by 0x108F91: main (main.c:107)
==6252== 
==6252== 
==6252== More than 10000000 total errors detected.  I'm not reporting any more.
==6252== Final error counts will be inaccurate.  Go fix your program!
==6252== Rerun with --error-limit=no to disable this cutoff.  Note
==6252== that errors may occur in your program without prior warning from
==6252== Valgrind, because errors are no longer being displayed.
==6252== 
==6252== 
==6252== Process terminating with default action of signal 11 (SIGSEGV)
==6252==  Access not within mapped region at address 0x562D000
==6252==    at 0x109012: main (main.c:113)
==6252==  If you believe this happened as a result of a stack
==6252==  overflow in your program's main thread (unlikely but
==6252==  possible), you can try to increase the size of the
==6252==  main thread stack using the --main-stacksize= flag.
==6252==  The main thread stack size used in this run was 8388608.
==6252== 
==6252== HEAP SUMMARY:
==6252==     in use at exit: 94 bytes in 7 blocks
==6252==   total heap usage: 13 allocs, 6 frees, 11,342 bytes allocated
==6252== 
==6252== LEAK SUMMARY:
==6252==    definitely lost: 0 bytes in 0 blocks
==6252==    indirectly lost: 0 bytes in 0 blocks
==6252==      possibly lost: 0 bytes in 0 blocks
==6252==    still reachable: 94 bytes in 7 blocks
==6252==         suppressed: 0 bytes in 0 blocks
==6252== Rerun with --leak-check=full to see details of leaked memory
==6252== 
==6252== For counts of detected and suppressed errors, rerun with: -v
==6252== ERROR SUMMARY: 10000000 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
答案

这些行看起来可疑:

            while (paragraphs[num_par - 1][i] != ' ')
                words[num_words - 1][j++] = paragraphs[num_par - 1][i++];

它不检查行尾条件。

应该不是这个:

while ( (paragraphs[num_par - 1][i] != '\0') &&
        (paragraphs[num_par - 1][i] != ' '))

   words[num_words - 1][j++] = paragraphs[num_par - 1][i++];

一些一般性建议。您的代码随着malloc和realloc的数量而大量流失。处理内存管理的代码多于实际的程序逻辑。这就给格式化和样式留下了很多不足之处。我要花很长的时间来写出如何提高其可读性和可调试性的方法,但是在找出所有错误之后,考虑将其提交给CodeReview

一些一般性建议

对于所有iffor块都使用花括号块,即使对于“单行”也是如此。代替这个:

for (i = 0; i < length; i++)
    if (!((A[i] >= 'a' && A[i] <= 'z') || (A[i] >= 'A' && A[i] <= 'Z')) && (A[i + 1] == ' ' || A[i + 1] == '\0'))
        for (k = i; k < length; k++)
            A[k] = A[k + 1];

此:

for (i = 0; i < length; i++)

    if (!((A[i] >= 'a' && A[i] <= 'z') || (A[i] >= 'A' && A[i] <= 'Z')) && (A[i + 1] == ' ' || A[i + 1] == '\0'))
    
        for (k = i; k < length; k++)
        
            A[k] = A[k + 1];
        
    

您的remove_special_char函数似乎刚刚存在,可以从字符串中删除标点符号。它似乎是通过用空格替换所有标点符号字符来实现的。然后删除所有空格,然后再进行一些其他双重嵌套的for循环。我认为整个功能可以替换如下:

int isPunctuation(char c)

    return ((c == '.') || (c == ',') || (c == '!') || (c == '?') || (c == ';'));


void remove_special_char(char* str)

    char* ptrRead = str;
    char* ptrWrite = str;

    while (*ptrRead)
    
        if (!isPunctuation(*ptrRead))
        
            *ptrWrite = *ptrRead;
            ptrWrite++;
        
        ptrRead++;
    
    *ptrWrite = '\0';

您始终引用数组索引number-1,这确实使您很难阅读。

 sentences[num_sent - 1][j++] = option[i++];

如果您在此之后才增加了num_sent,那么您的代码将如下所示:

sentences[num_sent][j++] = option[i++];

// increment num_sent at the end of the loop

此外,在使用索引值时请勿对其进行递增。调试和读取要困难得多。更好:

sentences[num_sent][j] = option[i];
i++;
j++;

不要在类似情况的函数中内联函数:

realloc(words[num_words - 1], (strlen(words[num_words - 1]) + 1) * sizeof(char));

更好:

wordsize = strlen(words[num_words]) + 1;
words[num_words] realloc(words[num_words], wordsize * sizeof(char));

sizeof(char)我认为总是1(至少在C ++中),但是我离题了。但是,如果您希望这样做,请编写一个辅助函数:

char* reallocateCharsForString(char* existing, size_t newlength)

    return (char*)realloc(existing, (newlength+1) * sizeof(char));

现在您将+1sizeof(char)的错误抽象为一个函数。现在您的分配语句看起来好多了:

wordlength = strlen(words[num_words]);
words[num_words] = reallocateCharsForString(words[num_words], wordlength);

您也可以为malloc做类似的抽象。

希望这会有所帮助。

另一答案

sizeof(char)根据定义为1;最好写:

 words[num_words - 1] = malloc(50 * sizeof(*words[0]));

或仅给出字节缓冲区

 words[num_words - 1] = malloc(50);

使用malloc()realloc()(尤其是后者)有很多危险:

  • 两者:大小为0的重新分配都可能返回无法取消引用的非NULL指针。解决此问题的唯一方法是在调用*alloc()之前添加0校验。
  • 两者:如果乘法nmemb * sizeof(*ptr)将溢出,则结果分配将小于预期的数目,这将很可能给您带来麻烦。一个解决方案是reallocarray(),在某些实现中(至少GLIBC和BSD)存在。此外,如果将reallocarray()(仅解决calloc())用于字符串,则对您来说甚至更安全。 malloc()会更好,但是很难找到使用它的实现。
  • recallocarray():分配recallocarray()的结果而不检查其是否失败将导致内存泄漏。尽管可能不是您想要的,但部分解决方案是使用realloc()。它存在于BSD中。更好的解决方案是在分配作业之前添加支票。
  • 两者:如果在赋值后不检查指针是否为非NULL,则尝试取消引用它的程序将中断。解决的办法是写自己的支票。

以上是关于是什么导致realloc()无效的下一个大小错误?的主要内容,如果未能解决你的问题,请参考以下文章

realloc 使用详解(分析realloc invalid pointer指针无效等错误)

无效的下一个大小和双重免费错误

free():无效的下一个大小(快)字符串太长了? [重复]

加密导致无效密钥大小错误[重复]

Swift 中的 Realloc UnsafeMutablePointer 导致错误

无效的下一个控制变量引用 ERROR