使用snprintf避免缓冲区溢出

我正在使用这样的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; } 

将此function与va_copy结合使用,您可以创建非常安全的格式化字符串操作。

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

函数snprintf()和vsnprintf()写入的字节数不超过大小(包括尾部’\ 0’)。

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

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

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

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

但是,不要与Microsoft的_snprintf (pre-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 ]"; 

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