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个字节的垃圾字符.
我还不太清楚为什么呢.有任何想法吗?
变量参数列表函数并不神奇地知道有多少个参数,所以你最有可能走到堆栈直到碰巧碰到一个NULL.
您需要一个参数numStrings,或者在字符串列表后面提供一个显式的null-terminator参数.
您需要在列表中使用哨兵标记:
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)
是解决这个问题的一种方法,您可能希望在分配失败时输入错误检查代码.