【问题标题】:Variable Arguments in C creating error in ValgrindC 中的变量参数在 Valgrind 中产生错误
【发布时间】:2025-12-26 06:30:11
【问题描述】:

我试图运行一个使用函数concat_str 的程序。它可以接受多个参数作为字符串,参数的结尾由"quit" 表示。我的函数代码如下:

char *concat_str(char *str1, ...)
{
    va_list pstr;
    char *minion = NULL, *temp = NULL;
    minion = (char*) malloc (sizeof(str1));
    strcpy (minion,str1);
    va_start (pstr, str1);
    if ( strcmp ("quit",str1) == 0)
    {
        va_end (pstr);
        return minion;
    }
    while (1)
    {
        temp = va_arg (pstr, char *);
        if ( strcmp ("quit", temp) == 0)
        {
            break;
        }
        minion = (char*) realloc (minion, sizeof(temp));
        strncat (minion,temp,sizeof(temp));
    }
    va_end (pstr);
    return minion;
}

同样的调用语句是:

char *result;
result = concat_str("hello", "hai", "how", "are", "you", "quit");

我确实得到了正确的输出。但是当我使用memcheck 工具在valgrind 上运行它时,我遇到了很多错误。错误如下:

==2635== Invalid write of size 1
==2635==    at 0x4A065D3: strncat (mc_replace_strmem.c:218)
==2635==    by 0x400A7E: concat_str (var_fun.c:23)
==2635==    by 0x400757: main (var_main.c:15)
==2635==  Address 0x4C33038 is 0 bytes after a block of size 8 alloc'd
==2635==    at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==2635==    by 0x400A5F: concat_str (var_fun.c:22)
==2635==    by 0x400757: main (var_main.c:15)
==2635==
==2635== Invalid read of size 1
==2635==    at 0x4A06594: strncat (mc_replace_strmem.c:218)
==2635==    by 0x400A7E: concat_str (var_fun.c:23)
==2635==    by 0x400757: main (var_main.c:15)
==2635==  Address 0x4C33038 is 0 bytes after a block of size 8 alloc'd
==2635==    at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==2635==    by 0x400A5F: concat_str (var_fun.c:22)
==2635==    by 0x400757: main (var_main.c:15)
==2635==
==2635== Invalid write of size 1
==2635==    at 0x4A065BF: strncat (mc_replace_strmem.c:218)
==2635==    by 0x400A7E: concat_str (var_fun.c:23)
==2635==    by 0x400757: main (var_main.c:15)
==2635==  Address 0x4C33038 is 0 bytes after a block of size 8 alloc'd
==2635==    at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==2635==    by 0x400A5F: concat_str (var_fun.c:22)
==2635==    by 0x400757: main (var_main.c:15)

==2635==
==2635== Invalid read of size 1
==2635==    at 0x4A066D4: strlen (mc_replace_strmem.c:246)
==2635==    by 0x32DAC46B18: vfprintf (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC4D3A9: printf (in /lib64/libc-2.5.so)
==2635==    by 0x40076E: main (var_main.c:16)
==2635==  Address 0x4C33038 is 0 bytes after a block of size 8 alloc'd
==2635==    at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==2635==    by 0x400A5F: concat_str (var_fun.c:22)
==2635==    by 0x400757: main (var_main.c:15)
==2635==
==2635== Invalid read of size 1
==2635==    at 0x32DAC6CE09: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC464B2: vfprintf (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC4D3A9: printf (in /lib64/libc-2.5.so)
==2635==    by 0x40076E: main (var_main.c:16)
==2635==  Address 0x4C33040 is 8 bytes after a block of size 8 alloc'd
==2635==    at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==2635==    by 0x400A5F: concat_str (var_fun.c:22)
==2635==    by 0x400757: main (var_main.c:15)
==2635==
==2635== Invalid read of size 1
==2635==    at 0x32DAC6CE1C: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC464B2: vfprintf (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC4D3A9: printf (in /lib64/libc-2.5.so)
==2635==    by 0x40076E: main (var_main.c:16)
==2635==  Address 0x4C3303F is 7 bytes after a block of size 8 alloc'd
==2635==    at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==2635==    by 0x400A5F: concat_str (var_fun.c:22)
==2635==    by 0x400757: main (var_main.c:15)
==2635==
==2635== Invalid read of size 1
==2635==    at 0x32DAC6CD66: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC464B2: vfprintf (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC4D3A9: printf (in /lib64/libc-2.5.so)
==2635==    by 0x40076E: main (var_main.c:16)
==2635==  Address 0x4C33038 is 0 bytes after a block of size 8 alloc'd
==2635==    at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==2635==    by 0x400A5F: concat_str (var_fun.c:22)
==2635==    by 0x400757: main (var_main.c:15)
==2635==
==2635== Invalid read of size 1
==2635==    at 0x32DAC6CD7A: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC464B2: vfprintf (in /lib64/libc-2.5.so)
==2635==    by 0x32DAC4D3A9: printf (in /lib64/libc-2.5.so)
==2635==    by 0x40076E: main (var_main.c:16)
==2635==  Address 0x4C33039 is 1 bytes after a block of size 8 alloc'd
==2635==    at 0x4A0590B: realloc (vg_replace_malloc.c:306)
==2635==    by 0x400A5F: concat_str (var_fun.c:22)
==2635==    by 0x400757: main (var_main.c:15)

错误来源(我自己的推论):

第 22 行:minion = (char*) realloc (minion, sizeof(temp));

Realloc 将新块的指针地址返回到minion。但是旧块会产生问题。

我尝试过的事情

  1. 我将 strncat (minion,temp,sizeof(temp)); 更改为 strncat (minion,temp,sizeof(temp) + 10); 。这减少了一些错误。但是如果字符串参数很长,我又会遇到同样的错误。顺便说一句,我不明白这是如何解决问题的。

  2. 我把minion = (char*) realloc (minion, sizeof(temp));改成了

    字符 t = NULL; t = 奴才; minion = (char) realloc (t, sizeof(temp)); 免费(t);

请告诉我,如果我的错误来源是正确的,并建议我应该如何解决这个问题。

【问题讨论】:

  • 注意:不要在代码中粘贴行号。也适用于minion = (char*) malloc (sizeof(str1)); 不要转换 malloc 的结果。在这里你需要strlen 而不是sizeof
  • 不要将realloc 的结果分配给您重新分配的同一个指针。想想如果realloc 失败并返回NULL 会发生什么,那么您将丢失原始指针并发生内存泄漏(除了您根本不检查失败的事实之外)。
  • @JoachimPileborg。请阅读我尝试过的东西,第二点。
  • @JoachimPileborg 我实际上是在我的主函数中调用免费(结果)
  • 调用free 没关系,因为任何分配函数都失败了,你会得到一个NULL 指针。并且您应该以相反的顺序进行重新分配/分配,即将realloc 结果分配给t,然后如果不是NULL,则将t 分配给minion。而你还是should not cast the result of malloc (and family)

标签: c variables arguments valgrind realloc


【解决方案1】:

表达式

sizeof(str1)

将产生指针的大小,而不是字符串长度。你应该分配

minion = malloc(strlen(str1) + 1);

对于您的重新分配,您必须提供整个数组的大小,而不仅仅是新分配的存储空间,因此您应该跟踪您的字符串长度。

最后,一个风格提示:通常使用NULL 作为标记值来终止字符串列表,而不是使用像"quit" 这样的任意文字。

【讨论】:

  • len = strlen (minion); minion = realloc (minion, len + strlen (temp));这是你的建议吗。出现新错误..
  • @xachu4u:不要忘记终止'\0' 的空间,strlen 不会考虑。也不需要每次都用strlen重新计算字符串长度。
【解决方案2】:
minion = (char*) malloc (sizeof(str1));

这里你需要strlen 而不是sizeof。因为char *str1 是char 指针,sizeof 指针根据机器配置应该是4 或8 字节。

会的

minion =  malloc (strlen(str1)+1); //No need to cast result of malloc

【讨论】:

  • 循环体内部呢?
【解决方案3】:

最后这段代码对我有用。我删除了类型转换 (char*) ,这是不必要的。 我在需要的地方将sizeof 更改为strlen

感谢 Jayesh、Joachim 和 Mohem 的快速回复。

char *concat_str(char *str1, ...)
   {
    va_list pstr;
    char *minion = NULL, *temp = NULL;
    int len;
    minion = malloc (strlen(str1) + 1);
    strcpy (minion,str1);
   va_start (pstr, str1);
   if ( strcmp ("quit",str1) == 0)
          {
           va_end (pstr);
           return minion;
          }
   while (1)
          {
           temp = va_arg (pstr, char *);
           if ( strcmp ("quit", temp) == 0)
                  {
                   break;
                  }
           len = strlen (minion);
           minion = realloc (minion, len + strlen (temp) + 2);
           strncat (minion, temp, strlen(temp) + 1);
          }
   va_end (pstr);
   return minion;
  }

~

【讨论】: