0

我的代码工作正常,但我收到 valgrind 错误。我想知道如何更正我的代码以正确使用这些 malloc 和 free 语句与 char * * dest。请不要告诉我不要 malloc 和 free,除非我在不正确的位置这样做。非常感谢在 answer03.c 中为 strcat_ex 提供一个或两个更正代码,或者解释我对 malloc、free 和 malloc 之后的初始化的误解。我为这篇长篇文章提前道歉,但我想提供一切必要的东西。

更多信息:我主要关注方法 strcat_ex (这与 strncat 不同——阅读函数描述以查看与 int *n 的区别)。问题的出现是因为我需要重新分配 dest (char **) 中字符串 (char *) 的参数内存,并且如果它没有分配足够的空间,并且在我 malloc 之后它没有被初始化。这对我来说没有意义如何在 malloc 之后初始化“堆”内存。我不相信初始化必须在 malloc 之后发生。

注意:pa03.c 和 answer03.h 根本不应该改变。

这是相关的 valgrind 错误(memcheck.log):

==28717== 1 errors in context 7 of 10:
==28717== Conditional jump or move depends on uninitialised value(s)
==28717==    at 0x402D09C: strcat (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==28717==    by 0x8048F98: strcat_ex (answer03.c:29)
==28717==    by 0x8048631: main (pa03.c:16)
==28717==  Uninitialised value was created by a heap allocation
==28717==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==28717==    by 0x8048F46: strcat_ex (answer03.c:21)
==28717==    by 0x8048631: main (pa03.c:16)
==28717== 
==28717== ERROR SUMMARY: 10 errors from 10 contexts (suppressed: 0 from 0)

引用的行:

第 16 行(来自pa03.c)不应更改。作为调用方法参数defnsrc返回变量 result 的示例,在pa03.c中声明如下:

result=strcat_ex(&dest, &n, src);

第 21 行(来自answer03.c):

char * buffer = malloc(1 + 2 * (sizeOfDest + strlen(src)));

第 29 行(来自answer03.c):

buffer = strcat(buffer,src);

这是相关的源代码。这是 valgrind 错误所在并且需要 stackoverflow 知识(answer03.c):

编辑:已添加注释并已注释掉行以删除我在代码中与我的问题没有直接关系的错误。对于这些令人发指的错误,我深表歉意,但在其中留下了线条以帮助未来的读者理解。

#include "answer03.h"
#include <string.h>

char * strcat_ex(char * * dest, int * n, const char * src)
{
            //Edit: Removed Line Below - Irrelevant variable resplaced with *n
    //int sizeOfDest;

    if(*dest == NULL)
    {
        *n = 0;
    }
    else
    {
        //Edit: Removed Line Below - variable replaced with *n 
        //sizeOfDest = strlen(*dest);
    }
    //Edit: Removed Line Below
    //if(*dest != NULL && sizeOfDest >= 1 + sizeOfDest + strlen(src))
    //Edit: Corrected Line
    if(*dest !=NULL && *n >= 1 + strlen(*dest) + strlen(src)) 
    {
        strcat(*dest, src);
    }
    else
    {
        //Edit: *n replaced sizeOfDest and changes needed to be made to reflect this. Commented out lines were incorrect and irrelevant. Lines directly below them are the corrected versions, until you reach the next blank line

        //*n = 1 + 2 * (sizeOfDest + strlen(src));
        if(*dest != NULL)
           *n = 1 + 2 * (strlen(*dest) + strlen(src));
        else
           *n = 1 + 2 * strlen(src); 

        //char * buffer = malloc(1 + 2 * (sizeOfDest + strlen(src)));
        char * buffer = malloc(sizeof(char) * *n);

        if(*dest != NULL)
        {
            strcpy(buffer, *dest);
            free(*dest);
        }
        *dest = malloc(sizeof(buffer));
        buffer = strcat(buffer,src);
        *dest = buffer;
    }
    return *dest;
}




低于此点的所有内容都应保持不变并且已知是正确的:

我的编译语句(Makefile):

gcc -Wall -Wshadow -g pa03.c answer03.c -o pa03

我的 valgrind 声明(Makefile):

valgrind --tool=memcheck --leak-check=full --verbose --track-origins=yes --log-file=memcheck.log ./pa03

这是 strcat_ex ( answer03.h ) 的函数定义:

#ifndef PA03_H
#define PA03_H 

#include <stdlib.h>

/**
 * Append the C-string 'src' to the end of the C-string '*dest'.
 *
 * strcat_ex(...) will append the C-string 'src' to the end of the string
 * at '*dest'. The parameter 'n' is the address of a int that specifies how
 * many characters can safely be stored in '*dest'. 
 *
 * If '*dest' is NULL, or if '*dest' is not large enough to contain the result
 * (that is, the sum of the lengths of *dest, src, and the null byte), then
 * strcat_ex will:
 * (1) malloc a new buffer of size 1 + 2 * (strlen(*dest) + strlen(src))
 * (2) set '*n' to the size of the new buffer
 * (3) copy '*dest' into the beginning of the new buffer
 * (4) free the memory '*dest', and then set '*dest' to point to the new buffer
 * (5) concatenate 'src' onto the end of '*dest'.
 *
 * Always returns *dest.
 *
 * Why do we need to pass dest as char * *, and n as int *? 
 * Please see the FAQ for an answer.
 *
 * Hint: These <string.h> functions will help: strcat, strcpy, strlen.
 * Hint: Leak no memory.
 */
char * strcat_ex(char * * dest, int * n, const char * src);
//...

以下是调用 source 作为测试的相关代码(pa03.c):

#include <stdio.h>
#include <string.h>
#include "answer03.h"

int main(int argc, char **argv)
{
  char * src;
  char * dest;
  char * result;
  int n;

  src="World!";
  dest=NULL;
  result=strcat_ex(&dest, &n, src);
  printf("src=\"World!\";\ndest=NULL;\nstrcat_ex(&dest, &n, src);\n --> gives %s with n=%d\n",result,n);
  result=strcat_ex(&dest, &n, "");
  printf("Then strcat_ex(&dest, &n, \"\") yields --> gives %s with n=%d\n",result,n);
  strcpy(dest,"abc");
  result=strcat_ex(&dest, &n, "def");
  printf("Then strcpy(dest,\"abc\"); strcat_ex(&dest, &n, \"def\") yields --> gives %s with n=%d\n",result,n);  
  free(dest);
  //...

这是相关的输出(来自pa03.c的打印语句):注意这是正确的输出(我当前的代码能够产生)。

src="World!";
dest=NULL;
strcat_ex(&dest, &n, src);
 --> gives World! with n=13
Then strcat_ex(&dest, &n, "") yields --> gives World! with n=13
Then strcpy(dest,"abc"); strcat_ex(&dest, &n, "def") yields --> gives abcdef with n=13
//...

最后的话:

我附上了编译此代码所需的文件以及使用 gcc 和 valgrind 在 linux 中的 valgrind 错误日志。valgrind 中还有更多内容,但我发布了我认为最相关的内容。提前致谢。

Zip 包括所有文件:
http ://www.filedropper.com/files_11

4

2 回答 2

3

您当前的功能已完全损坏。它包含看不到结果的逻辑、至少一个内存泄漏以及与未初始化的目标缓冲区的未经检查的连接。在错误的事情中,有很多:

  • 假设sizeofDest不仅规定了当前目标字符串的存储,还规定了任何连接操作的容量。这是完全错误的,这就是为什么n提供给这个函数的原因。
  • 彻底的内存泄漏:*dest = malloc(sizeof(buffer));不仅分配了完全不正确的内存大小(指针的大小;不是它指向的内容),而且仅在两行之后就泄漏了所述分配。
  • 死代码布尔逻辑:给定任何非负值N,表达式N >= N + 1 + M,其中 M 是非负值,永远不可能为真。
  • 您永远不会使用与按地址的目标指针一起提供的关键信息:当前目标缓冲区大小由n. 该值对该算法至关重要,因为它决定了目标缓冲区的实际大小,并且结合当前字符串长度 in *dest*,最终将决定是否需要调整大小。

这是正确执行此功能的一种方法:

char *strcat_ex(char ** dest, int * n, const char * src)
{
    size_t dst_len = 0, src_len = strlen(src);

    // determine current string length held in *dest
    if (*dest && **dest)
        dst_len = strlen(*dest);

    size_t req_len = dst_len + src_len + 1;

    // is space already available for the concatination?
    if (*dest && *n >= req_len)
    {
        // we already know where the target address of the
        // concatination is, and we already know the length of
        // what is being copied. just copy chars.
        if (src_len)
            memcpy(*dest+dst_len, src, src_len+1);
    }
    else
    {
        // resize is required
        void *tmp = realloc(*dest, req_len);
        if (tmp != NULL)
        {
            // resize worked, original content of *dest retained, so
            // we can once again simply copy bytes.
            *dest = tmp;
            memcpy(*dest+dst_len, src, src_len+1);
            *n = (int)req_len;
        }
        else
        {
            perror("Failed to resize target buffer");
            exit(EXIT_FAILURE);
        }
    }

    return *dest;
}

我对这个设计的作者提出了很大的问题,因为他们选择int了保存目标缓冲区大小的变量。这个量级显然永远不会是负数,并且使用所有标准库大小操作使用的相同类型以外的任何东西都是没有意义的,size_t. 我会提请设计这个的人注意这一点。

简单测试

int main()
{
    char *dst = NULL;
    int n = 0;

    strcat_ex(&dst, &n, "some string");
    printf("%s : %d\n", dst, n);
    strcat_ex(&dst, &n, " more data");
    printf("%s : %d\n", dst, n);

    *dst = 0; // zero-term the string

    strcat_ex(&dst, &n, "after empty");
    printf("%s : %d\n", dst, n);
    strcat_ex(&dst, &n, " and more");
    printf("%s : %d\n", dst, n);
    strcat_ex(&dst, &n, " and still more");
    printf("%s : %d\n", dst, n);
}

输出

some string : 12
some string more data : 22
after empty : 22
after empty and more : 22
after empty and more and still more : 36

你的测试

运行您的测试程序会产生以下结果:

src="World!";
dest=NULL;
strcat_ex(&dest, &n, src);
 --> gives World! with n=7
Then strcat_ex(&dest, &n, "") yields --> gives World! with n=7
Then strcpy(dest,"abc"); strcat_ex(&dest, &n, "def") yields --> gives abcdef with n=7
于 2015-02-16T20:48:02.437 回答
1

如果*destNULL,您将strcat在刚刚被'd 并且未初始化buffer时调用。要么设置为 0,要么使用.buffermallocbuffer[0]calloc

此外,您分配*dest然后设置*destbuffer两行,这会泄漏一些内存。不需要第一次分配到*dest.

于 2015-02-16T20:20:23.410 回答