__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。
使上述功能更安全的任何替代方法?
__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。
使上述功能更安全的任何替代方法?
您在这里要问的是(不)著名问题的专门化:“如果我只有一个指针,如何获得数组的大小? ”
没有办法弄清楚 . 所指向的对象的大小是多少dest
。您最好的选择可能是咬紧牙关并更改这 50k 个位置以通过大小。
您的代码可能还有更多您没有告诉我们的内容。例如,在您提到的那些“50k”地方,大小是否已知?如果是这样,您可以摆脱sizeof
在幕后使用的脏可变参数宏,然后调用带有长度参数的函数。
抱歉,正如@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;
}
然后开始降低跟踪缓冲区大小,直到断言开始触发。那时,您可以找到并修复有问题的电话。
这当然会减慢整个系统的速度,但我们这里不是在谈论性能。
只是为了强调它 - 这是一个快速而肮脏的黑客与大型旧现有代码库作斗争,不要将它用于新开发。
OP 评论说“大量缓冲区是动态分配的,......”。, malloc()
, realloc()
,calloc()
等free()
可以用存储大小的包装函数重写。
typedef union {
max_align_t align;
size_t sz;
} my_header;
void* my_malloc(size_t size) {
my_header *p = malloc(sizeof *p + size);
if (p) {
p->sz = size;
p++;
}
return p;
}
size_t my_size(const void *p) {
if (p) {
const my_header *head = p;
return head[-1].sz;
}
return 0;
}
void my_free(void *p) {
if (p) {
my_header *head = p;
free(--head);
}
}
所有其他*.c 文件调用一些 *.h 文件
#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()
,以帮助识别传递的指针是否真的是一。
包装分配函数也是一种确定各种分配问题的方法:双倍释放、最大使用量、所有指针都释放,...
[编辑] 5年后。
代码需要确保对齐 - 代码重新工作。
在 C11 之前使用广泛类型的联合来代替max_align_t
.
typedef union {
double d;
long l;
void *p;
void (*fp)();
// With C99
complex long double cld;
long long ll;
size_t sz;
} my_header;
这种大规模的练习重构工作需要自动化。改变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
命令(示例)结合起来,您的问题就解决了。
*免责声明:我不能保证我的示例正则表达式的优雅。它适用于我的情况,但正则表达式专家无疑可以改进它。
您可以使用宏来帮助在您my_sprintf()
传递的不是 true 的情况下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 这样的东西是否值得购买。
编译时使用-std=c99
or ,这样你就可以-std=c++11
确保__STDC_VERSION__
or__cplusplus
至少是199901L
,所以你现在可以使用vnsprintf
代替。