[英]CS50 Speller has a memory leak somewhere... and I can't find it
[英]Homework: I have a memory leak somewhere, but I can't find it. Any tips on how to use valgrind more effectively?
好的,首先,公平警告,這是一個班級的項目。 除了修復內存泄漏之外,我不是在尋找任何幫助。 我想我已經在這個C代碼的空間上遵循了一些可怕的編碼實踐。 無論如何,當我運行Valgrind來搜索內存泄漏發生的位置時,我一點都不清楚我錯過了什么內存泄漏。 我知道至少有兩個字符串,我沒有釋放,但我malloc()編輯,純粹基於valgrind輸出的大小。 因為我從項目中獲取了一些無關的代碼,valgrind行號很可能已經關閉,因此為了方便起見,我將它們標記為注釋。
一點背景,我正在編寫shell的基本功能。 它目前執行以下操作:
1.接受用戶輸入
2.將輸入解析為cmdin結構
3.執行命令,前提是它沒有管道。
4.從我創建的cmdin中取出空間,然后從步驟1重新開始。 這就是問題發生的地方。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_SIZE 1024
#define MAX_CLEN 2097152
#define READ_END 0
#define WRITE_END 1
#define CHILD_STATUS 0
struct cmdin
{
char *cmd;
char **args;
int nargs;
int pipeflag;
};
//check if the last argument of the command passed to it is a pipe.
//if so, return the position of the pipe, otherwise, return 0
//if it has already found a pipe, it will return the position of that pipe
int conpipe(struct cmdin * cmd)
{
if(!cmd->pipeflag)
{
if(!strcmp(cmd->args[cmd->nargs], "|"))
{
return cmd->nargs;
}
else
{
return 0;
/* PROBLEM LINE BELOW */
} // line 46, where valgrind claims one of the problems exists
}
else
{
//printf("pipeflag: %d\n", cmd->pipeflag);
return (cmd->pipeflag);
}
}
//free the command after each runthrough
int freeze(struct cmdin cmd)
{
int i;
for(i=0; i <= (cmd.nargs); i++)
{
//printf("cmd.args[%d]: %s\n",i, cmd.args[i]);
/* PROBLEM LINE BELOW*/
free(cmd.args[i]); //this is line 62, as noted by valgrind
}
free(cmd.args);
free(cmd.cmd);
return 0;
}
//parse input, and add a null to the end
struct cmdin * parse(char *cmd)
{
//allocate space for the command
struct cmdin *ped = malloc(sizeof(struct cmdin));
//declare pipeflag, and nargs as 0
ped->pipeflag = 0;
ped->nargs = 0;
//allocate space for the array of strings, and for the string cmd.
ped->args = malloc(sizeof(char*) * MAX_SIZE);
ped->cmd = malloc(sizeof(char) * MAX_SIZE);
//scan the input, and put the first argument into the cmd string
sscanf(cmd, "%s %[^\n]", ped->cmd, cmd);
//allocate space for the next string, and then copy cmd to it.
ped->args[ped->nargs] = malloc(sizeof(char) * MAX_SIZE);
strcpy(ped->args[ped->nargs], ped->cmd);
ped->pipeflag = conpipe(ped);
/* PROBLEM LINE BELOW*/
ped->nargs++; // line 86, where valgrind claims the second leak is called?
ped->args[ped->nargs] = malloc(sizeof(char) * MAX_SIZE);
//loop that allocates space for a command, and then assigns
//the next arg to it.
while(sscanf(cmd, " %s %[^\n]", ped->args[ped->nargs], cmd) == 2)
{
ped->pipeflag = conpipe(ped);
ped->nargs++;
ped->args[ped->nargs] = malloc(sizeof(char) * MAX_SIZE);
}
strncpy(ped->args[ped->nargs], cmd, strlen(cmd)-1);
ped->nargs++;
//ped->args[ped->nargs] = malloc(sizeof(char) * MAX_SIZE);
ped->args[ped->nargs] = NULL;
return ped;
}
int main()
{
char cwd[MAX_CLEN];
getcwd(cwd, sizeof(cwd));
char input[MAX_CLEN];
char *exit = "exit\n";
char *cd = "cd";
//Command to take input
printf("tosh$ ");
fgets(input, sizeof input, stdin);
while(strcmp(input, exit))
{
//Command to parse input
struct cmdin *cmd;
cmd = parse(input);
//if there is not a pipeflag
if(!cmd->pipeflag)
{
//Change directories
if(!strcmp(cd, cmd->args[0]))
{
chdir(cmd->args[1]);
getcwd(cwd, sizeof(cwd));
}
else if(strcmp(input, exit))
{
//command to run input
int child_pid;
child_pid = fork();
if(child_pid == 0)
{
if(strcmp(cmd->args[1],cmd->args[0]))
{
execvp(cmd->cmd, cmd->args);
}
else
{
free(cmd->args[1]);
cmd->args[1] = NULL;
cmd->nargs--;
execvp(cmd->cmd, cmd->args);
}
}
else
{
wait(&child_pid);
}
}
freeze(*cmd);
free(cmd);
}
//Command to take input
printf("tosh$ ");
fgets(input, sizeof input, stdin);
}
return 0;
}
注意:這個valgrind的輸入,如下所示:
tosh$ ls -al
tosh$ exit
valgrind Heap和Leak Summaries如下:
HEAP SUMMARY:
==4901== in use at exit: 4,096 bytes in 2 blocks
==4901== total heap usage: 6 allocs, 4 frees, 24,600 bytes allocated
==4901==
==4901== 2,048 bytes in 1 blocks are definitely lost in loss record 1 of 2
==4901== at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4901== by 0x400A48: parse (tosh.c:46)
==4901== by 0x400C97: main (tosh.c:86)
==4901==
==4901== 2,048 bytes in 1 blocks are definitely lost in loss record 2 of 2
==4901== at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4901== by 0x400BDE: parse (tosh.c:62)
==4901== by 0x400C97: main (tosh.c:86)
==4901==
==4901== LEAK SUMMARY:
==4901== definitely lost: 4,096 bytes in 2 blocks
==4901== indirectly lost: 0 bytes in 0 blocks
==4901== possibly lost: 0 bytes in 0 blocks
==4901== still reachable: 0 bytes in 0 blocks
==4901== suppressed: 0 bytes in 0 blocks
更新:根據要求。 我的Makefile:
CC=gcc
CFLAGS=-g -Wall
TARGET=tosh
$(TARGET): $(TARGET).c
$(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c
Valgrind版本:valgrind-3.7.0
gcc版本:gcc(Ubuntu / Linaro 4.7.2-2ubuntu1)4.7.2
echo $ LD_PRELOAD:
(這沒什么打印)
沒有設法重現。 但是有幾個筆記。
使用您提供的錯誤和警告。
gcc -Wall -Wextra -pedantic …
檢查返回值和操作是否成功。 這對於減少錯誤是必不可少的,特別是那些只有一英里的用戶案例顯示的錯誤。
if (!(foo = malloc(size))) {
perror("mem");
}
...
/* This line is no good: */
sscanf(cmd, "%s %[^\n]", ped->cmd, cmd);
/* | |
* +----------+----------------+
* |
* +--- Not good. UB. (Input / Output same.)
*/
你可以添加一個例程來獲取數量和大小......
i = sscanf(cmd, "%s%n %[^\n]%n", ped->cmd, &n1, cmd, &n2);
if (i < 2) {
/* Debug print. */
fprintf(stderr,
"%s:%-3d; "
"sscanf => %d items and %d bytes str[%d]<%s>\n",
__FILE__, __LINENO_,
i, n2, n1, ped->cmd
);
}
等等
好的,我只知道問題所在。 大部分評論員都是對的。 我在不同的源代碼上運行valgrind。 我一直在〜/ usr / bin中保留tosh的副本(在我的$ PATH中)。 我忘了用我最近的更改(包括解決我的內存泄漏問題的那些更新)更新那段代碼。
當我運行valgrind時,我使用了包含tosh.cosh的目錄中的以下命令:
$ valgrind tosh
我應該跑的是:
$ valgrind ./tosh
當我對命令進行更新時,代碼運行良好,沒有內存泄漏。 謝謝你的幫助! 對不起,我花了很長時間才想到這一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.