__inline int my_sprintf (char *dest,char *format,...)
{
va_list va;
va_start(va,format);
return vsprintf(dest,format,va);
}
我的问题是我无法将缓冲区大小参数添加到my_sprintf,因为它在超过50k的位置使用,我不能用vsprintf_s或vsnprintf替换vsprintf。
任何使上述功能更安全的替代方案?
答案 0 :(得分:5)
你在这里要问的是(in)着名问题的特殊化:“如果我拥有的是一个指针,如何获得数组的大小?”
无法确定dest
指向的对象的大小。你最好的选择可能是咬紧牙关并改变那些50k的位置以超越这个尺寸。
您的代码可能还有更多内容,而您没有告诉我们。例如,在你提到的那些“50k”的地方,大小是否已知?如果是这样,你可以使用在后台使用sizeof
的脏的可变参数宏,然后调用一个带有长度参数的函数。
答案 1 :(得分:3)
对不起,这里没有银弹,正如@cnicutar已经提到的那样。
您可以首先限制缓冲区大小并在溢出时断言。类似的东西:
#define SPRINTF_TRACE_BUFSIZE 4096
int my_sprintf( char* dest, const char* fmt, ... )
{
/* in threaded code use malloc(3) instead */
static char trace_buf[SPRINTF_TRACE_BUFSIZE];
va_list va;
va_start( va, fmt );
int rc = vsnprintf( trace_buf, SPRINTF_TRACE_BUFSIZE, fmt, va );
assert( rc != -1 && rc < SPRINTF_TRACE_BUFSIZE );
memcpy( dest, trace_buf, rc + 1 ); /* +1 for \0 terminator */
return rc;
}
然后开始降低跟踪缓冲区大小,直到断言开始触发。此时,您可以找到并修复有问题的电话。
这当然会减慢整个系统的速度,但我们不会在这里讨论性能。
只是为了强调它 - 这是一个快速和肮脏的黑客与大型旧的现有代码库, 不使用它进行新的开发 。
答案 2 :(得分:0)
这种大规模的运动重构努力需要自动化。改变my_sprintf
函数本身的行为是微不足道的,所以我将把这个练习留给读者。如你所知,电话是困难的部分。我假设这些调用的结构类似于:
ret = my_sprintf(dest, "%d:%s", arg1, arg2);
许多IDE /文本编辑器在其搜索/替换选项中支持正则表达式。我的一个,SlickEdit,特别好,因为它允许Perl正则表达式。这是一个将上述调用转换为以下内容的示例:
ret = my_sprintf(dest, MAX_SIZE, "%d:%s", arg1, arg2);
请注意,已指示SlickEdit以递归方式将此模式应用于项目树中的所有C源文件。
请注意,对话框条目应相当于s/(\".+\")/MAX_SIZE, $1/
的正则表达式。您的开发环境是否具有类似功能?很多人都这样做,所以看一看。
如果没有,还有其他选项,如独立脚本。举个例子,假设你有一些类似* nix的shell和Perl可用。谷歌“perl one liner replace”找到一些不错的例子,比如this和this。通过明智地使用find
命令(examples)来结合这些,您的问题就解决了。
*免责声明:我不能保证我的示例正则表达式的优雅。它适用于我的案例,但正则表达式大师无疑可以改善它。
答案 3 :(得分:0)
malloc()
,realloc()
,calloc()
,free()
等。
void *my_malloc(size_t size) {
size_t newsize = sizeof(size_t) + size;
size_t *p = malloc(size);
if (p) {
*p = size;
p++;
}
return p;
}
size_t my_size(const void *p) {
if (p) {
const size_t *sp = p;
return *(--sp);
}
return 0;
}
void my_free(void *p) {
if (p) {
size_t *sp = p;
free(--sp);
}
}
// All _other_ *.c files call some *.h file with
#define malloc my_malloc
#define free my_free
void *my_malloc(size_t size);
void my_free(void *p);
size_t my_size(const void *p);
现在用分配指针调用my_sprintf()
...
int my_sprintf (char *dest,char *format,...) {
va_list va;
va_start(va,format);
size_t n = my_size(dest);
return vsnprintf(dest,n,format,va);
}
此外,还可以预先设置幻数,以帮助确定传递的指针是否真的是my_allcoated()
。
包装分配功能也是一种确定各种分配问题的方法:双倍免费,最大使用量,所有指针免费,......
答案 4 :(得分:0)
在my_sprintf()
传递的内容不是真char *
的情况下,您可以使用宏来提供帮助。
#define my_sprintf(dest,format,...) \
my_sprintf_func( (dest),sizeof(dest), ( format ), __VA_ARGS__ )
__inline int my_sprintf_func(char *dest,size_t size,char *format,...)
{
va_list va;
va_start(va,format);
if ( size == sizeof(dest) )
return vsprintf(dest,format,va);
return vsnprintf(dest,size,format,va);
}
是的它会评估dest
两次,但如果不知道您的代码库,我就无法说出这是否有问题。但是,在使用数组进行调用的情况下,它至少会有所帮助。
我还认真地建议看看像Purify这样的东西是否值得购买。