【问题标题】:Concatenating a stack string with a heap string gives odd results将堆栈字符串与堆字符串连接会产生奇怪的结果
【发布时间】:2011-10-15 20:33:42
【问题描述】:

我相信下面有一个合理的解释,但我还是有点困惑。

问题在于创建_TCHAR[CONSTANT]_TCHAR*、连接它们并返回结果的函数。

出于某种原因,从_tmain() 调用whatTheHeck() 返回乱码。

_TCHAR* whatTheHeck(_TCHAR* name) {
    _TCHAR Buffer[BUFSIZE];
    DWORD dwRet;
    dwRet = GetCurrentDirectory(BUFSIZE, Buffer);
    _TCHAR* what = new _TCHAR[BUFSIZE];
    what = _tcscat(Buffer, TEXT("\\"));
    what = _tcscat(what, name);
    return what;
}

int _tmain(int argc, _TCHAR* argv[]) {

    _TCHAR* failure = whatTheHeck(TEXT("gibberish);")); // not again ..
    _tprintf(TEXT("|--> %s\n"), failure);

    _TCHAR* success = createFileName(TEXT("readme.txt")); // much better
    _tprintf(TEXT("|--> %s\n"), success);

    return 0;
}

相比之下,当使用堆时,事情会按预期工作。

_TCHAR* createFileName(_TCHAR* name) {
    _TCHAR* Buffer = new _TCHAR[BUFSIZE];
    DWORD dwRet;
    dwRet = GetCurrentDirectory(BUFSIZE, Buffer);
    Buffer = _tcscat(Buffer, TEXT("\\"));
    Buffer = _tcscat(Buffer, name);
    return Buffer;
}

为什么会有差异?

是不是因为_tcscat() 连接了内存地址而不是它们的内容并返回清除了堆栈?

【问题讨论】:

    标签: c++ windows string memory-management tchar


    【解决方案1】:

    这是因为_tcscat 将第一个目标参数连接到,然后返回它。因此,它返回一个指向数组 Buffer 的指针,并将其存储在这一行中的 what 中:

    what = _tcscat(Buffer, TEXT("\\"));
    

    然后这个指针被返回,一旦你尝试使用它就会有未定义的行为,因为本地的Buffer 不再存在。

    此外,上面这行代码还会导致分配给what 的内存泄露:

    _TCHAR* what = new _TCHAR[BUFSIZE];
    what = _tcscat(Buffer, TEXT("\\")); // this loses the memory allocated
                                        // in the previous line
    

    【讨论】:

      【解决方案2】:

      您的代码存在很多问题。让我们把它拆开,好吗:

      _TCHAR* whatTheHeck(_TCHAR* name)   // We're inside a local scope
      {
          _TCHAR Buffer[BUFSIZE];         // "Buffer" has automatic storage
      
          _TCHAR* what = new _TCHAR[BUFSIZE];  // "what" points to newly allocated dyn. memory
      
          what = _tcscat(Buffer, TEXT("\\"));  // Oh no, we overwrite "what" - leak!
                                               // Now what == Buffer.
      
          what = _tcscat(what, name);  // Equivalent to "_tcscat(Buffer, name)"
      
          return Buffer;               // WTPF? We're returning a local automatic!
       }
      

      如您所见,你们都在无缘无故地new 导致内存泄漏,并且您还返回了一个本地对象在其生命周期之后的地址!

      强烈推荐

      1. 阅读documentation for strcat 并了解“来源”和“目的地”,
      2. 不使用strcat,而是使用更安全的版本,例如strncat
      3. 不使用strncat,而是使用std::string

      【讨论】:

        【解决方案3】:

        what 指向的动态分配内存未初始化。它包含胡言乱语。 _tcscat 期望字符串正确地以空值结尾。

        _TCHAR* what = new _TCHAR[BUFSIZE]();
        

        这用'\0' 字符填充what

        _TCHAR* what = new _TCHAR[BUFSIZE];
        what[0] = '\0';
        

        这会正确终止空字符串。

        GetCurrentDirectory 不希望缓冲区以空值终止。它向其中写入一些内容并正确地以空值终止它。然后,您可以将其传递给连接函数。


        附带说明,您的函数似乎容易受到缓冲区溢出的影响。显然您允许GetCurrentDirectory 填满您分配的所有内容,然后您想追加到它而不关心是否还有剩余空间。

        【讨论】:

        • what 指向的分配内存泄漏,不是乱码的原因。
        • 我假设_tcscat 的工作方式类似于strcat,在这种情况下,结果的分配应该没问题,尽管是多余的(目标指针无论如何都不会被修改)。
        • @Martinho:我错过了 OP 第一次通过Buffer。无法想象有人在不知道 C 字符串函数如何工作的情况下管理 C 字符串...
        【解决方案4】:
        _TCHAR* what = new _TCHAR[BUFSIZE];
        what = _tcscat(Buffer, TEXT("\\"));
        

        你不是用函数的局部变量Buffer 覆盖what。一旦堆栈展开,Buffer 就会超出范围,因此您会得到意外的值。这也是内存泄漏。

        在堆分配场景中,您可能更愿意将指针声明为 const 以避免此类危险:

        _TCHAR* const what = new _TCHAR[BUFSIZE];
                ^^^^^ (avoids overwriting)
        

        更好的方法是使用std::string 并解决这些小问题。

        【讨论】:

          猜你喜欢
          • 2018-03-08
          • 2010-10-14
          • 1970-01-01
          • 2012-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多