提问人:New 提问时间:2/25/2023 最后编辑:New 更新时间:2/25/2023 访问量:189
在C语言中使用getline从stdin读取时如何避免内存泄漏?
How to avoid memory leaks when using getline to read from stdin in C?
问:
在 C 中使用 getline 从 stdin 读取时,我遇到了内存泄漏,尽管确保所有 malloc 都已释放,但在构建简单的 shell 时问题仍然存在。作为 C 语言的新手,我正在寻找有关在使用 getline 进行文件输入时如何正确处理内存的建议。
这是我用来读取该行的代码
char *readline(int *eof)
{
char *input = NULL;
size_t bufsize = 0;
*eof = getline(&input, &bufsize, stdin);
return (input);
}
这是实际的 main 函数。
while (status)
{
mode = isatty(STDIN_FILENO);
if (mode != 0)
{
write(STDOUT_FILENO, "$ ", 3);
}
line = readline(&eof);
if (eof == -1)
{
exit(EXIT_FAILURE);
}
args = tokenize(line);
status = hsh_execute(args, env, argv[0]);
i = 0;
while(args[i] != NULL)
{
free(args[i]);
i++;
}
free(args);
free(line);
}
这是我运行命令 echo “/bin/ls” | ./shell 时 valgrind 返回的错误
==33899== Invalid free() / delete / delete[] / realloc()
==33899== at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==33899== by 0x10980D: main (shell.c:40)
==33899== Address 0x4a96040 is 0 bytes inside a block of size 120 free'd
==33899== at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==33899== by 0x1097D5: main (shell.c:36)
==33899== Block was alloc'd at
==33899== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==33899== by 0x48EB1A2: getdelim (iogetdelim.c:62)
==33899== by 0x1096CA: readline (readline.c:12)
==33899== by 0x10976B: main (shell.c:26)
==33899==
==33899==
==33899== HEAP SUMMARY:
==33899== in use at exit: 120 bytes in 1 blocks
==33899== total heap usage: 4 allocs, 4 frees, 4,848 bytes allocated
==33899==
==33899== LEAK SUMMARY:
==33899== definitely lost: 0 bytes in 0 blocks
==33899== indirectly lost: 0 bytes in 0 blocks
==33899== possibly lost: 0 bytes in 0 blocks
==33899== still reachable: 120 bytes in 1 blocks
==33899== suppressed: 0 bytes in 0 blocks
==33899== Rerun with --leak-check=full to see details of leaked memory
==33899==
==33899== For lists of detected and suppressed errors, rerun with: -s
==33899== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
关于 realloc 问题,这就是我在 tokenize 函数中处理它的方式
#define BUFSIZE 64
#define DELIM " \t\r\n\a"
char **tokenize(char *line)
{
/*declaration of various fucntions*/
char **toks = malloc(sizeof(char *) * BUFSIZE);
int position = 0;
char *token, **token_backup;
int bufsize = BUFSIZE;
/*allocation error*/
if (toks == NULL)
{
free(toks);
perror("hsh");
}
token = strtok(line, DELIM);
/*Store token in toks*/
while (token != NULL)
{
toks[position] = token;
position++;
/*not enough memory*/
if (position >= BUFSIZE)
{
bufsize += BUFSIZE;
token_backup = toks;
toks = realloc(toks, position * sizeof(char *));
if (toks == NULL)
{
free(token_backup);
printf("allocation error\n");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL, DELIM);
}
toks[position] = NULL;
return (toks);
}
答:
1赞
Necklondon
2/25/2023
#1
我猜那条线
char *readline(int *eof)
应该是
char *readline(ssize_t *eof)
并应相应地声明。eof
评论
1赞
Necklondon
2/25/2023
@user3386109 是的,你是对的!
0赞
chqrlie
2/25/2023
#2
该函数不会在它返回的已分配数组中分配单个字符串。这解释了如何释放两次:何时和结束时。此外,如果该行包含多个单词或以空格开头,则使用指向已分配块内部的指针进行调用,该指针具有未定义的行为。tokenize
line
free(args[i])
i
0
free(line)
main
free
该函数也存在问题:tokenize
- 当您重新分配数组时,您传递了错误的大小:而不是您应该传递 .
position * sizeof(char *)
bufsize * sizeof(char *)
这是修改后的版本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* getline wrapper with a different API */
ssize_t readline(char **linep) {
size_t bufsize = 0;
*linep = NULL;
return = getline(linep, &bufsize, stdin);
}
#define BUFSIZE 64
#define DELIM " \t\r\n\a"
char **tokenize(char *line) {
/* declaration of various variables */
int bufsize = BUFSIZE;
int position = 0;
char **toks = malloc(sizeof(char *) * bufsize);
char *token, **token_backup;
if (toks == NULL) {
/* allocation error */
perror("cannot allocate array");
exit(EXIT_FAILURE);
}
/* break line into tokens and store them in toks */
token = strtok(line, DELIM);
while (token != NULL) {
toks[position] = token;
position++;
if (position >= BUFSIZE) {
/* need more space */
bufsize += BUFSIZE;
token_backup = toks;
toks = realloc(toks, sizeof(char *) * bufsize);
if (toks == NULL) {
free(token_backup);
perror("cannot reallocate array");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL, DELIM);
}
toks[position] = NULL;
return toks;
}
char **env = NULL;
int hsh_execute(char **args, char **env, char *command);
int main(void) {
int status = 1;
while (status) {
char *line;
char **args;
if (isatty(STDIN_FILENO)) {
write(STDOUT_FILENO, "$ ", 3);
}
if (readline(&line) == -1) {
// end of file reached
exit(EXIT_FAILURE);
}
args = tokenize(line);
status = hsh_execute(args, env, argv[0]);
free(args);
free(line);
}
return 0;
}
2赞
dbush
2/25/2023
#3
您没有内存泄漏。您正在释放已释放的内存。
i = 0;
while(args[i] != NULL)
{
free(args[i]);
i++;
}
free(args);
free(line);
每个都是指向 内部某处的指针。它们不是单独分配的。args[i]
line
所以摆脱循环,只有自由和.args
line
评论
0赞
New
2/25/2023
正如你所说,我摆脱了循环,但我仍然丢失了 120 个字节。Valgrind 一直说 120 字节仍然可以访问。这正常吗
0赞
dbush
2/25/2023
这是你没有向我们展示的其他一些分配。运行 valgrind,它会告诉你来源。--leak-check=full --show-reachable=yes
0赞
New
2/25/2023
我第一次运行它并得到相同的消息,除了 4 个 frees 更改为 3.此外,lauch 函数只是调用没有分配的 execve--leak-check=full --show-leak-kinds=all
1赞
pm100
2/25/2023
#4
我的猜测是正确的,您正在使用 strtok。strtok 将返回指向输入缓冲区的指针。所以和 一样。所以你实际上最终删除了两次argv[0]
line
line
评论
getline
free
line