3

在下面的代码中,我试图重新创建scanf以读取所有内容,stdin并在换行符处中断,返回字符串中读取的所有字符。

但问题是代码会泄漏一些内存,尤其是在realloc被调用时。

我也想知道为什么gets使用危险功能

test.c: warning: the 'gets' function is dangerous and should not be used.

我的代码:-

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

#define MAX_BUF_LEN 128
#define __cdecl

//0 - Success
//-1 - Error in input
//1 - Error
__cdecl int read_input(char *s,int len){

char c,*t;
int l,i=0;

if(s==NULL || len < 1)
    return -1;

while((c=getchar()) != '\n'){

    //check if sufficient memory
    //required + used > assigned 
    l=strlen(s);
    if(l + 2 > len){

        len += l + MAX_BUF_LEN;     //realloc max to avoid subsequent realloc as its costly!

        t = realloc(s,len);
        if(t!=NULL)
            s = t;
        else
            return 1;               //No space to store content
    }

    s[i++] = c;

}

s[i++] = '\0';      //Null terminate the Buffer
return 0;   
}

int main(int argc,char* argv[]){

int len = 5+1;
char *s = calloc(len,sizeof(char));

printf("Enter your name\n");

if(!read_input(s,len))
    printf("Hi %s\n",s);

free(s);

return 0;
}

瓦尔格林:

nimish:~/Desktop$ gcc -g -Wall test.c -o test
nimish~/Desktop$ clear && valgrind ./test  --leak-check=full

==3670== Memcheck, a memory error detector
==3670== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==3670== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==3670== Command: ./test --leak-check=full
==3670== 
Enter your name
Nimish Nicolus
==3670== Conditional jump or move depends on uninitialised value(s)
==3670==    at 0x4027029: strlen (mc_replace_strmem.c:282)
==3670==    by 0x804850E: read_input (test.c:23)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40831BF: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a5028 is 0 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40A93A8: _IO_file_xsputn@@GLIBC_2.1 (fileops.c:1317)
==3670==    by 0x408346F: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a502c is 4 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40A93BF: _IO_file_xsputn@@GLIBC_2.1 (fileops.c:1317)
==3670==    by 0x408346F: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a502b is 3 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40A9330: _IO_file_xsputn@@GLIBC_2.1 (fileops.c:1349)
==3670==    by 0x408346F: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a5028 is 0 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40A933C: _IO_file_xsputn@@GLIBC_2.1 (fileops.c:1348)
==3670==    by 0x408346F: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a502a is 2 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
Hi Nimis
==3670== Invalid free() / delete / delete[]
==3670==    at 0x4025BF0: free (vg_replace_malloc.c:366)
==3670==    by 0x80485F2: main (test.c:54)
==3670==  Address 0x41a5028 is 0 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== 
==3670== HEAP SUMMARY:
==3670==     in use at exit: 139 bytes in 1 blocks
==3670==   total heap usage: 2 allocs, 2 frees, 145 bytes allocated
==3670== 
==3670== LEAK SUMMARY:
==3670==    definitely lost: 139 bytes in 1 blocks
==3670==    indirectly lost: 0 bytes in 0 blocks
==3670==      possibly lost: 0 bytes in 0 blocks
==3670==    still reachable: 0 bytes in 0 blocks
==3670==         suppressed: 0 bytes in 0 blocks
==3670== Rerun with --leak-check=full to see details of leaked memory
==3670== 
==3670== For counts of detected and suppressed errors, rerun with: -v
==3670== Use --track-origins=yes to see where uninitialised values come from
==3670== ERROR SUMMARY: 23 errors from 7 contexts (suppressed: 11 from 6)

编辑:- 请参考下面附上的新代码

新代码:-

__cdecl int read_input(char **s,int len){

char c,*t;
int l,i=0;

if((*s)==NULL || len < 1)
    return -1;

while((c=getchar()) != '\n'){

    //check if sufficient memory
    //required + used > assigned 
    l=strlen((*s));
    if(l + 2 > len){

        len += l + MAX_BUF_LEN;     //realloc max to avoid subsequent realloc as its costly!

        t = realloc((*s),len);
        if(t!=NULL)
            (*s) = t;
        else
            return 1;               //No space to store content
    }

    *((*s)+i++) = c;

}

*((*s)+i) = '\0';       //Null terminate the Buffer
return 0;   
} 

瓦尔格林:

==4767== Memcheck, a memory error detector
==4767== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==4767== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==4767== Command: ./test --leak-check=full
==4767== 
Enter your name
Nimish Nicolus
==4767== Conditional jump or move depends on uninitialised value(s)
==4767==    at 0x4027029: strlen (mc_replace_strmem.c:282)
==4767==    by 0x8048516: read_input (test.c:23)
==4767==    by 0x80485DE: main (test.c:51)
==4767== 
Hi Nimish Nicolus
==4767== 
==4767== HEAP SUMMARY:
==4767==     in use at exit: 0 bytes in 0 blocks
==4767==   total heap usage: 2 allocs, 2 frees, 145 bytes allocated
==4767== 
==4767== All heap blocks were freed -- no leaks are possible
==4767== 
==4767== For counts of detected and suppressed errors, rerun with: -v
==4767== Use --track-origins=yes to see where uninitialised values come from
==4767== ERROR SUMMARY: 6 errors from 1 contexts (suppressed: 11 from 6)

它仍然给出了一些问题。

4

4 回答 4

4

以下是手册gets问题的说明:

永远不要使用gets()。因为在事先不知道数据的情况下不可能知道gets()会读取多少个字符,而且因为gets()会继续存储超过缓冲区末尾的字符,所以使用起来非常危险。它已被用来破坏计算机安全。请改用 fgets()。

发生泄漏问题是因为指针s是按值传递的,因此任何更改(包括重新分配后的赋值)都是针对其副本而不是原始指针进行的。结果,s释放只释放最初分配的内存,泄漏realloc-ed 内存。要解决此问题,请更改read_input为采用指向指针的指针,如下所示:

int read_input(char **ps,int len)

传递&sread_input, 并在函数体(*ps)中代替使用。s

于 2012-05-21T15:54:30.640 回答
3

您没有将指针返回到分配的缓冲区。所以,free(s)是没有做你期望的。realloc不保证在同一位置提供额外的空间。因此,缓冲区可以移动。一种解决方案是传递一个指向指针的指针或返回指针而不是返回指针int并使用 NULL 来指示错误。

PS - 还有其他问题,这是非常不安全的代码。

于 2012-05-21T15:55:10.973 回答
2

在循环中,您使用:

l=strlen(s);

s在循环中的任何地方都没有以 null 终止。因此,随后当您调用它时strlen(),它不会给您期望的结果,这可能是内存泄漏的原因。因此,请确保在循环中以 null 终止字符串。

gets很危险,因为它允许您阅读的内容超出您分配的空间。

于 2012-05-21T15:55:14.963 回答
2

gets() 是危险的,因为它不知道缓冲区实际有多大,因此它不能防止大输入行上的缓冲区溢出。对于坏人来说,缓冲区溢出是一种非常常见的攻击向量。使用 fgets() 和 stdin 作为第一个参数,它允许您指定缓冲区的大小。

于 2012-05-21T15:57:33.683 回答