为什么使用asprintf()而不是sprintf()?

时间:2012-10-05 13:05:23

标签: c printf gnu asprintf

我很难理解你为什么需要asprintf。 它在手册中说

  

函数asprintf()vasprintf()sprintf(3)vsprintf(3)的类似函数   free(3),除了他们分配足够大的字符串以容纳   输出包括终止空字节,并返回一个指针   通过第一个参数来解决它。该指针应该传递给   asprintf(&buffer, "/bin/echo %s is cool", getenv("USER")); 在不再需要时释放已分配的存储空间。

所以这是我试图理解的例子:

{{1}}

如果缓冲区分配的字符串足够大而不是说char * =(string)

,那有什么区别

2 个答案:

答案 0 :(得分:102)

如果使用sprintf()vsprintf(),则需要先分配缓冲区,并且需要确保缓冲区足够大以包含sprintf写入的内容。否则sprintf()将很乐意覆盖缓冲区末尾之外的任何内存。

char* x = malloc(5 * sizeof(char));
// writes "123456" +null but overruns the buffer
sprintf(x,"%s%s%s", "12", "34", "56");

...将'6'和终止null写入超出分配给x的空格的末尾,破坏其他变量或导致分段错误。

如果你很幸运,它会在分配的块之间记录内存,并且不会造成伤害 - 这一次。这会导致间歇性的错误 - 最难诊断的错误。使用像 ElectricFence 这样的工具会导致超出失败,这是很好的。

提供过长输入的非恶意用户可能会导致程序以意外方式运行。恶意用户可以利用它作为将自己的可执行代码导入系统的方法。

对此的一个防范是使用snprintf(),它会将字符串截断为您提供的最大长度。

char *x = malloc(5 * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56"); // writes "1234" + null

如果空间可用,返回值size写入的长度 - 不包括终止空

在这种情况下,如果size大于或等于5,那么您就知道发生了截断 - 如果您不想截断,则可以分配新字符串并再次尝试snprintf()

char *x = malloc(BUF_LEN * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56");
if (size >= BUF_LEN) {
    realloc(&x,(size + 1) * sizeof(char));
    snprintf(x, 5, "%s%s%s", "12", "34", "56");
}

(这是一个非常天真的算法,但它说明了这一点)

asprintf()为您完成这一步 - 计算字符串的长度,分配该内存量,并将字符串写入其中。

char *x;
int size = asprintf(&x, "%s%s%s", "12", "34", "56");

在所有情况下,一旦你完成x,你需要释放它,或者你泄漏了内存:

free(x);

asprintf()是隐式malloc(),因此您必须检查它是否有效,就像使用malloc()或任何其他系统调用一样。

if (size == -1 ) {
   /* deal with error in some way */
}

请注意asprintf()是libc的GNU和BSD扩展的一部分 - 您无法确定它是否会在每个C环境中都可用。 sprintf()snprintf()是POSIX和C99标准的一部分。

答案 1 :(得分:20)

好处是安全。

许多程序通过在填充用户提供的数据时让程序员提供的缓冲区溢出来允许系统漏洞发生。

asprintf为您分配缓冲区可以保证不会发生。

但是必须检查asprintf的返回值,以确保内存分配实际成功。见http://blogs.23.nu/ilja/2006/10/antville-12995/