Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
`realloc():尝试处理未知大小的输入时,下一个大小无效`_C_Realloc - Fatal编程技术网

`realloc():尝试处理未知大小的输入时,下一个大小无效`

`realloc():尝试处理未知大小的输入时,下一个大小无效`,c,realloc,C,Realloc,我有以下代码: 如果传递了NULL,则函数get\u unlimited\u input将分配一个新字符串,否则它将只向现有字符串追加字符。 最后,它会截断多余的字节。 (DEFAULT\u BUFFER\u SIZE被设置为5以测试多次重新分配的情况) #包括 #包括 #包括 #定义默认缓冲区大小5 字符*获取\无限制\输入(字符*缓冲区){ 大小\u t当前大小; if(buffer==NULL){ buffer=malloc(默认缓冲区大小*sizeof(char)); 当前缓冲区大小=默

我有以下代码: 如果传递了
NULL
,则函数
get\u unlimited\u input
将分配一个新字符串,否则它将只向现有字符串追加字符。 最后,它会截断多余的字节。 (
DEFAULT\u BUFFER\u SIZE
被设置为
5
以测试多次重新分配的情况)

#包括
#包括
#包括
#定义默认缓冲区大小5
字符*获取\无限制\输入(字符*缓冲区){
大小\u t当前大小;
if(buffer==NULL){
buffer=malloc(默认缓冲区大小*sizeof(char));
当前缓冲区大小=默认缓冲区大小;
}否则{
当前缓冲区大小=strlen(缓冲区)+默认缓冲区大小;
}
char*cursor=缓冲区+当前大小-默认缓冲区大小;
对于(;;){
int current=getchar();
*游标=(字符)当前;
游标++;
如果(当前=='\n'| |当前==EOF)
打破
如果(光标>=缓冲区+当前大小){
当前缓冲区大小+=默认缓冲区大小;
缓冲区=realloc(缓冲区,当前_大小);
游标=缓冲区+当前大小-默认缓冲区大小;
}
}
*游标='\0';
缓冲区=realloc(缓冲区,光标-缓冲区);
返回缓冲区;
}
int main(){
printf(“>”);
char*buffer=get_unlimited_input(NULL);
printf(“>”);
获取不受限制的输入(缓冲区);
}
在大多数情况下,它工作正常,但如果我先通过117个字符,然后通过12个字符,它就会崩溃:

>.....................................................................................................................
>............
realloc(): invalid next size
Aborted (core dumped)

问题出在哪里?

除其他问题外,在返回缓冲区之前,请先修剪缓冲区中的所有多余空间。但是,如果将缓冲区传递给函数,则假定它仍然有额外的空间。因此,您不能将从函数返回的缓冲区传递回函数。但你就是这么做的

    } else {
        current_size = strlen(buffer) + DEFAULT_BUFFER_SIZE;
    }
...
    buffer = realloc(buffer, cursor - buffer);
另外,正如KamilCuk所指出的,在返回的字符串中没有为终止符留出空间,因此在其上调用
strlen
是不安全的

您应该记录对函数输入的要求以及函数输出保证满足的要求。这样就更容易发现这样的错误


当您看到“如果一个缓冲区被传递给这个函数,它必须有额外的空间”和“从这个函数返回的缓冲区没有任何额外的空间”,很明显,您无法将返回的缓冲区传递回函数,因为输出保证不满足输入要求。

基本问题是,当您使用非空指针调用
get_unlimited_input
时(先前调用的预存缓冲区),它假定缓冲区大小为
strlen(缓冲区)+默认缓冲区大小
,这是错误的。前一个调用实际上已经重新分配了缓冲区,以匹配不包括终止NUL的字符串长度(这意味着终止NUL本身可能丢失)

您可以通过在存储NUL之后和重新定位之前通过增加游标来修正这些问题(这样realloc将足够大),然后在传递非空指针时将当前_大小的计算更改为
strlen(buffer)+1


另一个问题是,当您从getchar获取EOF时,您正在将该EOF强制转换为一个char并将其存储在缓冲区中。EOF不是一个有效的字符——让
getchar
返回
int
而不是
char
的全部意义在于,它可以将EOF作为不同于任何字符的信号。因此,在EOF中,您正在缓冲区中存储一些随机垃圾字符(或非字符),这些字符可能只是作为垃圾显示,或者可能导致输出崩溃或错误(取决于系统)。

如果您从
malloc
realloc
free
中获得运行时错误,则意味着您已损坏了堆。堆损坏的常见原因包括释放内存块后使用内存块(这包括调用
free
两次)和缓冲区溢出(和下溢)

损坏发生在运行时错误之前。这可能发生在很久以前,因此如果您只是在错误发生时开始调试程序,可能很难重建发生的情况。还有其他工具可以帮助您更准确地定位问题。在使用GCC或Clang的类Unix系统上,这是非常有用的

# Settings for Ubuntu 20.04; you may need to adapt for your system
$ export ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-10/bin/llvm-symbolizer
$ gcc -O -Wall -Wextra a.c -fsanitize=address,undefined && python3 -c "print('.'*117+'\n'+'.'*12)" | ./a.out
=================================================================
==446177==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c000000236 at pc 0x7f246fc0ea6d bp 0x7ffd4e309380 sp 0x7ffd4e308b28
READ of size 119 at 0x60c000000236 thread T0
    #0 0x7f246fc0ea6c  (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x67a6c)
    #1 0x55c04dfb32e7 in get_unlimited_input (.../65891246/a.out+0x12e7)
    #2 0x55c04dfb34b1 in main (.../65891246/a.out+0x14b1)
    #3 0x7f246f06f0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #4 0x55c04dfb320d in _start (.../65891246/a.out+0x120d)

0x60c000000236 is located 0 bytes to the right of 118-byte region [0x60c0000001c0,0x60c000000236)
allocated by thread T0 here:
    #0 0x7f246fcb4ffe in __interceptor_realloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe)
    #1 0x55c04dfb345c in get_unlimited_input (.../65891246/a.out+0x145c)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x67a6c) 
Shadow bytes around the buggy address:
  0x0c187fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff8000: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c187fff8010: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa
  0x0c187fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
  0x0c187fff8030: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
=>0x0c187fff8040: 00 00 00 00 00 00[06]fa fa fa fa fa fa fa fa fa
  0x0c187fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==446177==ABORTING
你。最重要的部分是堆栈跟踪,以及缓冲区溢出发生在“118字节区域”上的指示,这表明它发生在第一次调用
get_unlimited_input
的末尾或第二次调用的开头。堆栈跟踪提供发生溢出的确切代码地址,可用于在调试器中设置断点;您将看到它接近函数的末尾。正如其他人已经指出的那样

    *cursor = '\0';
    buffer = realloc(buffer, cursor - buffer);
错误:您没有为
'\0'
终止符留出空间。你需要

    *(cursor++) = '\0';
    buffer = realloc(buffer, cursor - buffer);


我还没有检查其他bug()。

您的代码中存在多个问题,导致堆损坏,如诊断所示:

  • 您对当前分配大小的假设不正确:
    current\u size=strlen(buffer)+DEFAULT\u buffer\u size太乐观了。由于在返回缓冲区之前将其重新分配到
    cursor-buffer
    字节,因此字符串末尾没有松弛

  • 将字节存储到数组后,测试
    '\n'
    EOF
    。这可能是换行符的预期行为,但对于不是字符的
    EOF
    ,这是不正确的

  • 使用
    buffer=realloc(buffer,cursor-buffer)重新分配
    buffer
    也不正确:
    cursor
    指向空终止符,因此应使用
    cursor+1-buffer
    的大小将空终止符保留在allo
        *(cursor++) = '\0';
        buffer = realloc(buffer, cursor - buffer);
    
        *cursor = '\0';
        buffer = realloc(buffer, cursor - buffer + 1);
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #define DEFAULT_BUFFER_SIZE  16  /* use address alignment as incremental size */
    
    char *get_unlimited_input(char *buffer) {
        size_t current_size, pos;
        char *p;
    
        if (buffer == NULL) {
            pos = 0;
            current_size = DEFAULT_BUFFER_SIZE;
            buffer = malloc(DEFAULT_BUFFER_SIZE);
            if (buffer == NULL)
                return NULL;
        } else {
            pos = strlen(buffer);
            current_size = pos + 1;
        }
        for (;;) {
            int c = getchar();
            if (c == EOF || c == '\0')
                break;
            if (pos + 1 == current_size) {
                // reallocate the buffer
                current_size += DEFAULT_BUFFER_SIZE;
                p = realloc(buffer, current_size);
                if (p == NULL)
                    break;
                buffer = p;
            }
            buffer[pos++] = (char)c;
            if (c == '\n')
                break;
        }
        buffer[pos] = '\0';
        p = realloc(buffer, pos + 1);
        return (p != NULL) ? p : buffer;
    }
    
    int main() {
        printf("> ");
        char *buffer = get_unlimited_input(NULL);
        printf("got: %s\n", buffer);
        printf("> ");
        get_unlimited_input(buffer);
        printf("got: %s\n", buffer);
        return 0;
    }