c语言 - 家庭作业:我在某处有内存泄漏,但我找不到它。关于如何更有效地使用valgrind的任何提示?



好的,首先,公平警告,这是一个类的项目。除了修复我的内存泄漏之外,我没有寻求任何帮助。我想我已经在这个 C 代码的空间中遵循了一些可怕的编码实践。无论如何,当我运行 Valgrind 来搜索内存泄漏发生的位置时,我根本不清楚我错过了什么内存泄漏。我知道至少有两个字符串我没有释放,而是我 malloc()ed,纯粹基于 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: %dn", 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]: %sn",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  = "exitn";
    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 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

更新:根据要求。我的制作文件:

CC=gcc
CFLAGS=-g -Wall
TARGET=tosh
$(TARGET): $(TARGET).c
    $(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c

瓦尔格林德版本: 瓦尔格林德-3.7.0

海湾合作委员会版本: gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2

echo $LD_PRELOAD:

(这什么也没印)

未能成功复制。但是有几个注意事项。

  1. 使用可用的错误和警告。

    gcc -Wall -Wextra -pedantic …
    
  2. 检查返回值和操作是否成功。这对于减少错误至关重要,尤其是那些只有一英里后的用户案例才会显示的错误。


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.c tosh的目录中的以下命令:

$ valgrind tosh

我应该运行的是:

$ valgrind ./tosh

当我对命令进行更新时,代码运行良好,没有内存泄漏。感谢您的帮助!对不起,我花了这么长时间才弄清楚这一点。

最新更新