正如其他人所指出的那样,对于您的问题的基本答案是“是的,您容易受到缓冲区溢出的攻击”。
在C99中,您可以使用VLA:
void ircsocket_print(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = vsnprintf(0, 0, message, args);
va_end(args);
char buffer[len+1];
va_start(args, fmt);
int len = vsnprintf(buffer, len+1, message, args);
va_end(args);
send(ircsocket_connection, buffer, len, 0);
}
请注意调用
vsnprintf()
函数时,第二个参数传入 0 可以获取需要的长度,然后再调用该函数进行数据格式化。同时,每次调用
vsnprintf()
后请注意仔细重置
args
,这是 C 标准所要求的:
§7.15 <stdarg.h>
如果需要访问可变参数,则被调用函数应声明一个对象(本子句中通常称为 ap
)并指定其类型为 va_list
。对象 ap
可作为参数传递给另外一个函数;如果该函数使用参数 ap
调用了宏 va_arg
,则在调用函数中,参数 ap
的值是不确定的,并且在任何进一步引用 ap
之前,应向 va_end
宏传递该值。
此方法的缺点之一是它采用悲观的方式,在预先调用了两次
vsnprintf()
的情况下执行。如果第一次调用就足够,您可以更乐观一些(大多数情况下,512 就足够了),只有在第一次调用后显示不足时才分配更多的空间。
使用 VLA 的另一个缺点是,如果本地变量
buffer
的空间不足,则您的代码可能永远无法恢复。您需要判断这是否是一个严重的问题。如果是,建议使用显式内存分配函数(例如
malloc()
)。
char buffer = malloc(len+1);
if (buffer == 0)
return; // Report error?
...second vsnprintf() and send()...
free(buffer);
由于您的函数只返回常量
1
,因此没有明显的理由将其作为返回任何内容的函数 - 如果它是返回
void
的函数,则调用代码不需要对返回值进行任何检查。 另一方面,也许您应该返回
send()
调用的结果(如果
malloc()
失败可能会返回错误指示)。我还将格式(消息)参数更改为
const char *
; 既不这个函数也不调用它的函数修改格式字符串。
vsnprintf
返回将要打印的字符数,而不截断输出。 - Blagovest Buyuklievva_end(va);
和va_start(va, message);
,然后才能调用第二个vsnprintf()
。这在C99标准的§7.15<stdarg.h>
中有说明:_[...]被调用的函数应声明一个类型为va_list的对象(通常在本分落中称为ap)。对象ap可以作为参数传递给另一个函数;如果该函数使用参数ap调用va_arg宏,则调用函数中ap的值是不确定的,并且应在任何进一步引用ap之前传递给va_end宏。_ - Jonathan Lefflernew
运算符(或者是new
运算符?)不是C的一部分。delete
也不是。 - Jonathan Leffler