如果你使用
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, 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标准的一部分。
asprintf()
和vasprintf()
是GNU扩展函数。已添加GNU标签。 - alk