是什么导致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。
一些一般性建议
对于所有if
和for
块都使用花括号块,即使对于“单行”也是如此。代替这个:
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));
现在您将+1
和sizeof(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():无效的下一个大小(快)字符串太长了? [重复]