如果使用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 标准的一部分。