【问题标题】:How to declare appropriate size for the buffer如何为缓冲区声明适当的大小
【发布时间】:2013-05-23 22:47:35
【问题描述】:

我在我正在处理的 Visual C++ 项目中使用TCHAR,其定义如下所示:

#ifdef _UNICODE
    typedef wchar_t TCHAR;
#else
    typedef char TCHAR;
#endif

我需要将一些数据放入缓冲区buff

char buff[size] = {0};  // how to declare the buffer size - what should be its value ?
sprintf(buff, "%s (ID: %i)", res->name(), res->id());

地点:

name() 返回TCHAR*

id() 返回int

如何计算size的值——实际需要的确切缓冲区容量(如果没有定义unicode,则较小,如果定义unicode,则较大)?另外我想保护自己免受缓冲区溢出的可能性,我应该使用什么样的保护?

更重要的是,我在这里将缓冲区声明为char。如果我将缓冲区声明为int,大小值会有什么不同(即,如果与声明为 char 相比,它会小 4 倍)?

更新

我部分基于 Mats Petersson 的回答得出的结论是:

    size_t len;
    const char *FORMAT;
#ifndef _UNICODE
    len = strlen((char*)res->name()); 
    FORMAT = "%s (ID: %i)";
#else
    len = wcslen(res->name());
    FORMAT = "%S (ID: %i)";
#endif    

    int size = 7 * sizeof(TCHAR) +                             /* place for characters inside format string */
               len * sizeof(TCHAR) +                           /* place for "name" characters */
               strlen(_itoa(id, ioatmp, 10)) * sizeof(TCHAR) + /* place for "id" digits */
               1 * sizeof(TCHAR);                              /* zero byte(s) string terminator */

    char *buff = new char[size];  /* buffer has to be declared dynamically on the heap,
                                   * because its exact size is not known at compilation time */
    sprintf(buff, FORMAT, name, id);
    delete[] buff;

这是正确的想法还是我错过了什么?

【问题讨论】:

  • buff[size * sizeof(TCHAR) / sizeof(char)]?
  • @Luchian Grigore:不,尺寸是我真正要问的 - 我想了解应该如何计算尺寸的值,基于我知道将传递哪些数据的事实它(数据:"%s (ID: %i)", res->name(), res->id())。

标签: c++ c visual-c++ memory-management buffer-overflow


【解决方案1】:

从后面开始,buff 应该始终是 char,因为这是 sprintf 存储的内容。

其次,如果您的res->name() 正在返回一个宽字符(unicode)字符串,您的格式字符串应该使用"%S",对于常规ASCII,您应该使用"%s"

现在,计算缓冲区所需的长度,并避免溢出。做这样的事情并不难

      const TCHAR *nm = res->name();
      size_t len; 
#ifndef UNICODE
      len = strlen(nm); 
#else
      ... see below. 
#endif

然后猜测数字的长度(一个整数不能超过 12 位),以及在格式字符串中作为常量生成的确切字符数。

这适用于标准 ASCII 变体。

但是,使用宽字符变体会变得更有趣,因为它可以在输出字符串中占用多个字节(例如,编写总是需要多字节编码的汉字)。一种解决方案是:

 len = snprintf(0, NULL, "%S", nm);

这应该给你正确的数字[我认为]。这是一个相当麻烦的方法,但它会起作用。我不确定是否有一种简单的方法可以以另一种方式将宽字符串转换为“存储此字符串所需的字节数”。

编辑:我会认真考虑支持非 UNICOD 变体是否很重要,然后将整个内容转换为使用swprintf(...)。您仍然需要长度,但它应该只是wcslen(res->name()) 的结果,而不是需要一些复杂的转换计算。

【讨论】:

  • 连同我在底部的编辑,我试图澄清%S%s的情况。
  • 请注意,使用 sprintf 是不可移植的(但是,TCHAR 也不是,呵呵)。此外,您应该使用 _tcsclen(...) 以类型为单位正确获取 TCHAR 的长度,size_t byteLen = _tcsclen(res->name()) * sizeof(TCHAR);
  • @Mats Petersson:+1 感谢您的宝贵时间。你能检查我在哪里计算的问题的更新吗?看起来对吗?格式字符串中字符的位置是什么,我应该将其计算为7 * sizeof(TCHAR) 吗?
  • @JaroslawWaliszko:我不是 100%,但我认为这会起作用,尽管您的缓冲区有时会太大,因为您在 Unicode 情况下主要将 ascii 打印到缓冲区中。
【解决方案2】:
  1. 您可以使用:snprintf/swnprintf,它将返回您需要的字符数/wchars。
  2. 这里char buff[size] = {0}; 您正在缓冲区之外写入。更新:我会收回它 - 如果 size 是常量,它只是一个带有初始化的声明。
  3. 如果最后一个参数是int,则此"%s (ID: %i)" 应更改为:"%s (ID: %d)"

【讨论】:

  • 广告。 2(这里char buff[size] = {0};你在缓冲区之外写。)-我怎么能在缓冲区之外写,因为我还没有定义它的大小-这就是我真正要问的关于。
  • 请注意,只有 MS 版本的 snprintf 有这种行为,一般 snprintf 返回写入的字符数。 linux.die.net/man/3/snprintf
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-19
  • 2013-06-14
相关资源
最近更新 更多