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

Del*_*ani 1 c string memory-management variadic-functions

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

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;
}
Run Code Online (Sandbox Code Playgroud)

问题是,如果我这样做:

puts(copycat("herp", "derp", "hurr", "durr"));
Run Code Online (Sandbox Code Playgroud)

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

我还不太清楚为什么呢.有任何想法吗?

Oli*_*rth 5

变量参数列表函数并不神奇地知道有多少个参数,所以你最有可能走到堆栈直到碰巧碰到一个NULL.

您需要一个参数numStrings,或者在字符串列表后面提供一个显式的null-terminator参数.


pax*_*blo 5

您需要在列表中使用哨兵标记:

puts(copycat("herp", "derp", "hurr", "durr", NULL));
Run Code Online (Sandbox Code Playgroud)

否则,va_arg实际上并不知道何时停止.因为你正在调用未定义的行为,所以你得到垃圾是一个纯粹的意外.例如,当我按原样运行代码时,我遇到了分段错误.

变量参数函数,例如printf需要传递多少项的传递:printf本身使用前面的格式字符串来解决这个问题.

这两种通用方法是计数(或格式字符串),当您不能将其中一个可能的值用作标记​​(末尾的标记)时,它非常有用.

如果你可以使用一个标记(在指针的情况下像NULL一样,或者在非负有符号整数的情况下为-1),那通常会更好,所以你不必计算元素(并且可能得到元素数和元素列出步骤).


请记住,这puts(copycat("herp", "derp", "hurr", "durr"));是一个内存泄漏,因为您正在分配内存,然后丢失指向它的指针.使用:

char *s = copycat("herp", "derp", "hurr", "durr");
puts(s);
free (s);
Run Code Online (Sandbox Code Playgroud)

是解决这个问题的一种方法,您可能希望在分配失败时输入错误检查代码.