4
__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。

使上述功能更安全的任何替代方法?

4

6 回答 6

5

您在这里要问的是(不)著名问题的专门化:“如果我只有一个指针,如何获得数组的大小?

没有办法弄清楚 . 所指向的对象的大小是多少dest。您最好的选择可能是咬紧牙关并更改这 50k 个位置以通过大小。


您的代码可能还有更多您没有告诉我们的内容。例如,在您提到的那些“50k”地方,大小是否已知?如果是这样,您可以摆脱sizeof在幕后使用的脏可变参数宏,然后调用带有长度参数的函数。

于 2015-05-28T14:51:36.243 回答
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;
}

然后开始降低跟踪缓冲区大小,直到断言开始触发。那时,您可以找到并修复有问题的电话。

这当然会减慢整个系统的速度,但我们这里不是在谈论性能。

只是为了强调它 - 这是一个快速而肮脏的黑客与大型旧现有代码库作斗争,不要将它用于新开发

于 2015-05-28T15:04:14.297 回答
2

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; 
于 2015-05-28T17:07:22.757 回答
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”可以找到一些很好的例子,比如thisthis。将这些与明智地使用find命令(示例)结合起来,您的问题就解决了。

*免责声明:我不能保证我的示例正则表达式的优雅。它适用于我的情况,但正则表达式专家无疑可以改进它。

于 2015-05-28T17:04:59.517 回答
0

您可以使用宏来帮助在您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 这样的东西是否值得购买。

于 2015-05-28T20:03:13.777 回答
0

编译时使用-std=c99or ,这样你就可以-std=c++11确保__STDC_VERSION__ or__cplusplus至少是199901L,所以你现在可以使用vnsprintf代替。

于 2021-11-26T12:12:45.453 回答