我正在使用如下方式来使用snprintf
,以避免缓冲区溢出:
char err_msg[32] = {0};
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]");
我在字符串长度超过32个字节时,加入了-1
以保留空间给空终止符。
我的思路正确吗?
平台:
- GCC 4.4.1
- C99
正如其他人所说,你在这种情况下不需要-1。如果数组大小固定,我会使用strncpy
。它是用于复制字符串的 - sprintf
是用于进行困难格式化的。但是,如果数组的大小未知或者你想确定为格式化字符串需要多少存储空间。这就是我真正喜欢的标准指定版本的snprintf
的地方:
char* get_error_message(char const *msg) {
size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno);
char *buffer = malloc(needed+1);
sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
return buffer;
}
结合使用va_copy
,您可以创建非常安全的格式化字符串操作。
sprintf(buffer, …)
,因为你已经保留了恰好正确大小的buffer
。这将避免这个错误。 - Pascal Cuoq请注意“包括结尾处的'\0'”这一部分内容。函数snprintf()和vsnprintf()不会写入超过size个字节的数据(包括结尾处的'\0')。
不需要-1。C99的snprintf
始终会添加结束符。Size参数指定输出缓冲区的大小,包括零终止符。代码因此变成:
char err_msg[32];
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]");
ret
包含实际打印的字符数(不包括字符串中的零终止符)。
不要与 Microsoft 的 _snprintf
(C99 之前)混淆,该函数不会添加空终止符,并且其行为完全不同(例如,如果缓冲区不够大,则返回 -1
而非应打印的长度)。如果使用 _snprintf
,则应使用与您问题中相同的代码。
snprintf(3)
的说明:
snprintf()
和vsnprintf()
函数不会写入超过指定的size
字节数(包括结尾的'\0'
)。
char err_msg[32];
strncpy(err_msg, "[ ST_ENGINE_FAILED ]", sizeof(err_msg));
err_msg[sizeof(err_msg) - 1] = '\0';
或者更好的方法:
char err_msg[32] = "[ ST_ENGINE_FAILED ]";
strncpy()
函数复制的字节数的描述是不正确的。“最多复制n个字节的src。”我已经调整了示例以修复空终止。 - Matt Joinerstrncpy()
永远不会覆盖缓冲区中的最后一个字符。你必须显式地执行 err_msg[sizeof(err_msg)-1] = '\0';
才能获得终止符。 - D.Shawleysizeof会返回数据类型在内存中使用的字节数,而不是字符串的长度。例如,在32位系统上,sizeof(int)返回4个字节(取决于具体实现方式)。由于您在数组中使用了一个常量,因此可以将其传递给printf函数。
sizeof
应用于目标数组,这是完全正确的。 - caf
snprintf
函数? - Left For Archive_snprintf()
,它的行为不像标准的snprintf()
(我认为它不总是以空字符结尾字符串)。 - Bastien Léonard