关于C#:为什么使用asprintf()而不是sprintf()?

Why use asprintf() instead of sprintf()?

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

The functions asprintf() and vasprintf() are analogs of sprintf(3) and
vsprintf(3), except that they allocate a string large enough to hold
the output including the terminating null byte, and return a pointer
to it via the first argument. This pointer should be passed to
free(3) to release the allocated storage when it is no longer needed.

因此,这是我尝试理解的示例:

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

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


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

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

...在分配给x的空间的末尾写入'6'和终止null,这会破坏某些其他变量或导致分段错误。

如果幸运的话,它会在分配的块之间践踏内存,这不会造成伤害-这次。这会导致间歇性错误,这是最难诊断的错误。最好使用诸如ElectricFence之类的工具,该工具会导致超速运行失败。

输入时间过长的非恶意用户可能会导致程序异常运行。恶意用户可以利用此方法将自己的可执行代码获取到系统中。

一种防止这种情况的方法是使用snprintf(),它会将字符串截断为您提供的最大长度。

1
2
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()

1
2
3
4
5
6
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()一步为您完成此操作-计算字符串的长度,分配该内存量,然后将字符串写入其中。

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

在所有情况下,完成x后,都需要释放它,否则会泄漏内存:

1
free(x);

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

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

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


好处是安全。

当程序填充了用户提供的数据时,程序员提供的缓冲区就会溢出,从而导致许多程序被系统利用。

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

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