让asprintf使用也是输入的非NULL目标指针是否安全?

dmc*_*kee 6 c alias stdio

tl; dr可以asprintf天真地用于连接而无需调用临时指针?


asprintfGNU引入并在其他几个clib实现中采用的函数是使用类似方案在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);
Run Code Online (Sandbox Code Playgroud)

当包裹在主要和必要的包含,这对我来说运行良好.

但...

是否在整个地方泄漏记忆?Valgrind说它在我的盒子上.这是一个错误吗?

我面前的那个人页说

asprintf()和vasprintf()函数将*ret设置为指向缓冲区的指针,该缓冲区足以容纳格式化的字符串.应该将此指针传递给free(3)以在不再需要时释放已分配的存储.如果无法分配足够的空间,asprintf()和vasprintf()将返回-1并将ret设置为NULL指针.

在这句话中由于缺少"设置*ret为指向一个新的缓冲[...]",我很想假设函数使用reallocgetline一样.

可能有什么问题?

使用签名int asprintf(char **ret, const char *format, ...);具体.

  1. asprintf跑得太快realloc了.

    想象一下,我们实现了这个函数,我们可以realloc(*ret)在它取消引用其中一个混淆原始缓冲区的变量之前运行.该缓冲区已被释放,这在技术上是未定义的行为.这将代表一个错误.

  2. asprintf在读取之前写入缓冲区.在上面的代码就可以成像功能拷贝的内容argv[1]*ret之前的每va_argstr参数.我引用的联机帮助页似乎并不排除这种情况.

  3. asprintffree *ret直接或通过使用realloc.这将避免问题编号(1),但如果如上所述使用会泄漏内存.再一次,该手册页似乎不排除它.

解决

通过将单个调用替换为asprintfwith,可以避免上述所有操作

{
  char *newStr = NULL;
  asprintf(newStr,"%s %s",argv[i],str);
  free(str);
  str = newStr;
}
Run Code Online (Sandbox Code Playgroud)

但这很笨重.

共识实现是否保证第一个代码示例是安全和正确的?

小智 3

是否到处都在泄漏内存?

是的,它确实。

char *str = strdup(argv[i]);
Run Code Online (Sandbox Code Playgroud)

这里,str包含一个指向 ated 内存的指针malloc(),应该是free()d。

asprintf(&str, "%s %s", argv[i], str);
Run Code Online (Sandbox Code Playgroud)

此处,asprintf()进行修改str,使其指向函数本身分配的其他内存。现在你刚刚丢失了指向 ped 字符串的指针strdup(),因此发生了泄漏。

  • @dmckee 为了说明要点: `ret` 是一个 **out** 参数 - `*ret` 被设置,但它的值从未被使用。您可以从文档中确定这一点,因为......它说“*ret”已设置,并且从未提及使用其值。因此,“重新分配”它是不可能的。 (2认同)