63

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

函数asprintf()andvasprintf()是 and 的类似物sprintf(3)vsprintf(3)除了它们分配一个足够大的字符串来保存包括终止空字节在内的输出,并通过第一个参数返回一个指向它的指针。free(3)当不再需要时,应该传递该指针 以释放分配的存储空间。

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

asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));

如果缓冲区分配足够大的字符串与说 char* = (string) 有什么区别

4

2 回答 2

134

如果使用sprintf()or 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包括终止的 null

在这种情况下,如果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, size + 1 , "%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 标准的一部分。

于 2012-10-05T13:19:09.613 回答
21

好处是安全。

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

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

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

于 2012-10-05T13:09:12.113 回答