使用snprintf避免缓冲区溢出

时间:2009-11-21 12:51:02

标签: c printf

我正在使用这样的snprintf来避免缓冲区溢出:

char err_msg[32] = {0};
snprintf(err_msg, sizeof(err_msg) - 1, "[ ST_ENGINE_FAILED ]");

我添加-1以保留空终止符的空间,以防字符串长度超过32个字节。

我的想法是否正确?

平台:

  • GCC 4.4.1
  • C99

6 个答案:

答案 0 :(得分:27)

正如其他人所说,在这种情况下你不需要-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结合使用,您可以创建非常安全的格式化字符串操作。

答案 1 :(得分:12)

您不需要-1,因为参考说明:

  

函数snprintf()和   vsnprintf()写的不多   大小字节(包括尾随   '\ 0')。

请注意“包括尾随'\ 0'”部分

答案 2 :(得分:9)

不需要-1。 C99 snprintf 始终零终止。 Size参数指定输出缓冲区的大小,包括零终止符。因此,代码变为

char err_msg[32];
int ret = snprintf(err_msg, sizeof err_msg, "[ ST_ENGINE_FAILED ]");

ret包含打印的实际字符数(不包括零终结符)。

但是,不要与微软的_snprintf(前C99)混淆,后者 null-terminate,并且就此而言,行为完全不同(例如,返回{{如果缓冲区不够大,则代替将要打印的长度)。如果使用-1,则应使用与问题相同的代码。

答案 3 :(得分:3)

根据snprintf(3)

  

函数snprintf()vsnprintf()的写入次数不超过size个字节(包括尾随的'\0')。

答案 4 :(得分:1)

对于给出的示例,您应该这样做:

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 ]";

答案 5 :(得分:-1)

sizeof将返回数据类型在内存中使用的字节数,而不是字符串的长度。例如。 sizeof(int)在32位系统上返回'4'字节(嗯,取决于我猜的实现)。由于您在数组中使用常量,因此可以愉快地将其传递给printf。