__inline int my_sprintf (char *dest,char *format,...)
{
va_list va;
va_start(va,format);
return vsprintf(dest,format,va);
}
我的问题是我无法将缓冲区大小参数添加到my_sprintf,因为它已在超过50k的地方使用,因此我无法将vsprintf替换为vsprintf_s或vsnprintf。
使上述功能更安全的替代方法?
您在这里问的是(一个)著名问题的专业化:“ 如果我只有一个指针,如何获得数组的大小?“
无法找出dest
指向的对象的大小。您最好的选择可能是硬着头皮,更改那50k的位置以通过大小。
您可能没有告诉我们更多的代码。例如,在您提到的那些“ 50k”地方,尺寸是已知的吗?如果是这样,您可以摆脱一个肮脏的可变参数宏,该宏在幕后使用sizeof
,然后调用一个带有length参数的函数。
[抱歉,这里没有银弹,就像@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);
}
}
全部other * .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
函数本身的行为是微不足道的,因此我将把练习留给读者。如您所述,呼叫是最困难的部分。我假设这些调用的结构如下:
[如果在传递my_sprintf()
而不是真实的char *
的情况下,您可以使用宏来提供帮助。