繁体   English   中英

C中简单可变参数字符串连接的内存分配

[英]Memory allocation for simple variadic string concatenation in C

我有以下测试函数可复制并连接可变数量的字符串参数,并自动分配:

char *copycat(char *first, ...) {
    va_list vl;
    va_start(vl, first);
    char *result = (char *) malloc(strlen(first) + 1);
    char *next;
    strcpy(result, first);
    while (next = va_arg(vl, char *)) {
        result = (char *) realloc(result, strlen(result) + strlen(next) + 1);
        strcat(result, next);
    }
    return result;
}

问题是,如果我这样做:

puts(copycat("herp", "derp", "hurr", "durr"));

它应该打印出一个16字节的字符串"herpderphurrdurr" 而是打印出一个42字节的字符串,它是正确的16字节加上26个字节的垃圾字符。

我不太确定为什么。 有任何想法吗?

variable-argument-list函数无法神奇地知道有多少个参数,因此,您很可能会遍历堆栈,直到碰巧碰到NULL为止。

您要么需要一个numStrings参数,要么在字符串列表之后提供一个明确的null终止符参数。

您的清单上需要一个标记标记:

puts(copycat("herp", "derp", "hurr", "durr", NULL));

否则, va_arg实际上不知道何时停止。 由于您正在调用未定义的行为,因此您将成为垃圾的事实纯属偶然。 例如,当我按原样运行您的代码时,我遇到了段错误。

可变参数函数(例如printf需要某种指示来指示要传入多少个项目: printf本身使用前面的格式字符串来弄清楚这一点。

两种通用方法是计数(或格式字符串),当您不能将可能的值之一用作标记(末尾有一个标记)时,此方法很有用。

如果您可以使用前哨(例如,对于指针,则为NULL;对于非负有符号整数,则为-1),通常情况下更好,因此您不必对元素进行计数(并且可以获取元素计数和元素列表不对)。


请记住, puts(copycat("herp", "derp", "hurr", "durr")); 这是内存泄漏,因为要分配内存然后丢失指向它的指针。 使用方法:

char *s = copycat("herp", "derp", "hurr", "durr");
puts(s);
free (s);

是解决此问题的一种方法,如果分配失败,您可能需要放入错误检查代码。

我从您的代码中了解到,您假设一旦“弹出”每个参数,va_next将返回NULL。 这是错误的,因为va_next绝对无法确定参数的数量:您的while循环将一直运行,直到随机命中NULL。

解决方案:提供参数数量,或添加带有附加“ NULL”参数的函数调用。

PS:如果您想知道为什么printf不需要这样的附加参数,那是因为期望的参数数量是从格式字符串(“%flag”的数量)推导出来的

作为其他答案的补充,将NULL用作可变参数的参数时,应将NULL转换为期望的类型: (char *)NULL 如果将NULL定义为0,则将存储一个int,当int的大小为sime且指针由所有位0表示NULL时,该int会意外地工作。但这不能保证,因此您可能会遇到奇怪的情况移植代码或什至仅更改编译器时很难调试的行为。

正如其他人提到的, va_arg不知道何时停止。 调用函数时,您可以提供NULL (或其他标记)。 只是一些注意事项:

  • 您必须在从mallocrealloc获得的指针上调用free
  • 没有理由在C中realloc mallocrealloc的结果。
  • 调用realloc ,最好将返回值存储到一个临时变量中。 如果realloc无法重新分配足够的内存,它将返回NULL但原始指针不会释放。 如果您使用realloc你的方式,它是无法重新分配内存,那么你已经失去了原有的指针和你的后续调用strcat很可能会失败。 您可以这样使用它:

     char *tmp = realloc(result, strlen(result) + strlen(next) + 1); if (tmp == NULL) { // handle error here and free the memory free(result); } else { // reallocation was successful, re-assign the original pointer result = tmp; } 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM