【发布时间】:2013-03-26 18:46:43
【问题描述】:
tl;dr 可以在不调用临时指针的情况下天真地使用asprintf 进行连接吗?
由 GNU 引入并在其他几个 clib 实现中采用的函数 asprintf 是使用类似方案的 c 中任意连接的诱人解决方案
int i=0;
char *str = strdup(argv[i]);
while (argv[++i]) {
asprintf(&str,"%s %s",argv[i],str); // <=== This line
}
asprintf(&str,"%s\n",str);
当包装在 main 和必要的 include 中时,这对我来说运行良好。
但是……
是不是到处都在泄漏内存? Valgrind 说它在我的盒子上。这是一个错误吗?
我面前的手册页说
asprintf() 和 vasprintf() 函数将 *ret 设置为指向 缓冲区足够大以容纳格式化的字符串。这个指针 应该传递给 free(3) 以释放分配的存储空间 不再需要。如果无法分配足够的空间,asprintf() 并且 vasprintf() 将返回 -1 并将 ret 设置为 NULL 指针。
在没有短语 "set *ret to be a pointer to a new buffer [...]" 的情况下,我很想假设该函数使用realloc,就像getline 一样。
可能有什么问题?
使用签名int asprintf(char **ret, const char *format, ...); 表示具体性。
-
asprintf运行realloc太快了。想象一下,我们在一个很远的地方实现了这个函数,以至于我们可以在
realloc(*ret)取消引用一个别名为原始缓冲区的可变参数之前运行它。该缓冲区已被释放,这在技术上是未定义的行为。这将代表一个错误。 asprintf在读取缓冲区之前先写入缓冲区。在上面的代码中,我们可以想象函数将argv[1]的内容复制到*ret之前的每个va_arg上的str参数。我引用的手册页似乎没有排除这种情况。asprintf不直接使用free *ret,也不是通过使用realloc。这将避免问题编号 (1),但如果按上述方式使用,则会泄漏内存。再次,该手册页似乎没有排除它。
解决方法
上述所有情况都可以通过替换对asprintf 的单个调用来避免
{
char *newStr = NULL;
asprintf(newStr,"%s %s",argv[i],str);
free(str);
str = newStr;
}
但这很笨重。
共识实现是否保证第一个代码示例安全正确?
【问题讨论】:
-
顺便问一下,好问题。 +1。
-
asprintf不是标准函数,它是 GNU 扩展。 “是不是到处都在泄露内存?” - 当然如此。医生说要释放内存,但你没有。asprintf不可能realloc它的第一个参数——这是一个输出参数,它的内容不能被假定。 -
@JimBalter Er...是的。这是正确的。认为它们也可以在 Mac OS 现在使用的 BSD 库中找到。后续编辑。
-
如果你将一个非空指针传递给
asprintf,它会被malloc的调用覆盖。它不使用预先分配的内存。 -
“我很想假设该函数像 getline 一样使用 realloc。” -- 做出这样的假设是完全错误的,因为
getline文档详细说明了如何使用lineptr参数的值。只需比较文档,很明显asprintf的工作方式不同......它从不使用*ret的先前值。