【问题标题】:Memory leak in a std::stringstd::string 中的内存泄漏
【发布时间】:2018-01-18 19:56:21
【问题描述】:

如果用户在 shell 命令行中输入“关机”或“重启”,我正在尝试让我的 shell 终止。一切正常。我使用 C (strtok, cstring) 和 C++ 编写代码。当我使用 valgrind 时,它说 1 个块仍然可以到达。我知道这不是泄漏,但我很想知道导致它的原因以及如何解决它。花了 2 天时间试图找到它。请查看我的代码和 valgrind 报告。

代码

#include <iostream>
#include <fstream>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

using namespace std;

const int MAX_COMMAND_LINE_ARGUMENTS = 8;
const int SLEEP_DELAY = 100000;

int GetCommand (string tokens []);
int TokenizeCommandLine (string tokens [], string commandLine);
bool CheckForCommand ();
void ProcessCommand (string tokens [], int tokenCount);
void ShutdownAndRestart (string tokens [], int tokenCount);

static volatile sig_atomic_t cullProcess = 0;

int main()
{
    string tokens [MAX_COMMAND_LINE_ARGUMENTS];
    int tokenCount;

    do
    {
        tokenCount = GetCommand (tokens);
        ProcessCommand (tokens, tokenCount);
    } while (1);

    return 0;
}
int GetCommand (string tokens [])
{
    string commandLine;
    bool commandEntered;
    int tokenCount;

    do
    {
         cout << "shell> ";
        while (1)
        {
            getline (cin, commandLine);
            commandEntered = CheckForCommand ();            if (commandEntered)
            {
                break;
            }
        }


      } while (commandLine.length () == 0);

      tokenCount = TokenizeCommandLine (tokens, commandLine);
      commandLine.clear();
      return tokenCount;
}

int TokenizeCommandLine (string tokens [], string commandLine)
{
    char *token [MAX_COMMAND_LINE_ARGUMENTS];
    char *workCommandLine = new char [commandLine.length () + 1];
    int i;
    int tokenCount;

    for (i = 0; i < MAX_COMMAND_LINE_ARGUMENTS; i ++)
    {
        tokens [i] = "";
    }

    strcpy (workCommandLine, commandLine.c_str ());
    i = 0;
    if ((token [i] = strtok (workCommandLine, " ")) != NULL)
    {
        i ++;
 while ((token [i] = strtok (NULL, " ")) != NULL)
        {
            i ++;
        }
    }
    tokenCount = i;

    for (i = 0; i < tokenCount; i ++)
    {
        tokens [i] = (string) token [i];
    }

    delete [] workCommandLine;
    return 0;
}

bool CheckForCommand ()
{
    if (cullProcess)
    {
        cullProcess = 0;
        cin.clear ();
        cout << "\b\b  \b\b";
        return false;
    }

    return true;
}



void ProcessCommand (string tokens [], int tokenCount)
{
    if (tokens [0] == "shutdown" || tokens [0] == "restart")
    {
        ShutdownAndRestart (tokens, tokenCount);
        return;
    }
}
void ShutdownAndRestart (string tokens [], int tokenCount)
{
    if (tokenCount > 1)
    {
        cout << "shell: " << tokens [0] << " does not require any arguments" << endl;
        return;
    }

    cout << endl;
    cout << "shell: terminating ..." << endl;
    exit(0);
}

VALGRIND

valgrind --leak-check=full --show-leak-kinds=all --show-reachable=yes tryThis

==20257== Memcheck, a memory error detector
==20257== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==20257== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==20257== Command: tryThis
==20257==
shell> shutdown

shell: terminating ...
==20257==
==20257== HEAP SUMMARY:
==20257==     in use at exit: 33 bytes in 1 blocks
==20257==   total heap usage: 6 allocs, 5 frees, 157 bytes allocated
==20257==
==20257== 33 bytes in 1 blocks are still reachable in loss record 1 of 1
==20257==    at 0x4C2A203: operator new(unsigned long) (vg_replace_malloc.c:334)
==20257==    by 0x4EF3CF8: allocate (new_allocator.h:104)
==20257==    by 0x4EF3CF8: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (basic_string.tcc:607)
==20257==    by 0x4EF5580: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (basic_string.tcc:138)
==20257==    by 0x4EF59B7: _S_construct_aux<char const*> (basic_string.h:1725)
==20257==    by 0x4EF59B7: _S_construct<char const*> (basic_string.h:1746)
==20257==    by 0x4EF59B7: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (basic_string.tcc:215)
==20257==    by 0x40134D: TokenizeCommandLine(std::string*, std::string) (in xxx/tryThis)
==20257==    by 0x4011C7: GetCommand(std::string*) (in xxx/tryThis)
==20257==    by 0x4010CA: main (in xxx/tryThis)
==20257==
==20257== LEAK SUMMARY:
==20257==    definitely lost: 0 bytes in 0 blocks
==20257==    indirectly lost: 0 bytes in 0 blocks
==20257==      possibly lost: 0 bytes in 0 blocks
==20257==    still reachable: 33 bytes in 1 blocks
==20257==                       of which reachable via heuristic:
==20257==                         stdstring          : 33 bytes in 1 blocks
==20257==         suppressed: 0 bytes in 0 blocks
==20257==
==20257== For counts of detected and suppressed errors, rerun with: -v
==20257== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

【问题讨论】:

  • 您是否考虑过使用std::vector 而不是数组?为什么std::string 与手动分配的char* 混合使用?
  • 我没试过。你认为这是造成它的原因吗?
  • 手动分配字符数组比使用std::vectorstd::string更容易出错。
  • std::string 带有手动分配的字符,因为我正在使用 strtok,然后我正在标记用户输入的命令,以便我可以修剪它
  • 我想我试图在分配了一些东西的地方删除东西。即使这不是我想知道那里出了什么问题的理想方式。感谢您的快速回复:)

标签: c++ valgrind stdstring


【解决方案1】:

你通过调用exit(0)ShutdownAndRestart()退出程序。

exit() 不会展开堆栈,它会“中止”程序,只进行最少的清理。具体来说,main() 中的 tokens 变量不会被破坏,并且包含的​​字符串的内存不会被释放。

如果您通过从 main 返回退出程序,问题应该会消失。例如ProcessCommand() 可以返回一个标志来指示主循环是否应该停止,而不是在内部调用exit()

【讨论】:

  • 非常感谢。我会尝试解决方案,如果可行,我会将其标记为答案。
猜你喜欢
  • 1970-01-01
  • 2011-11-09
  • 1970-01-01
  • 2011-03-26
  • 2014-08-18
  • 1970-01-01
  • 2012-10-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多