【问题标题】:Getting a memory leak from dynamically allocated arrays从动态分配的数组中获取内存泄漏
【发布时间】:2014-03-03 20:16:32
【问题描述】:

我正在尝试编写一个反转用户输入字符串的程序。如果他们输入“book”,它应该返回“koob”。我必须调用函数 reverseit(char *inputString, char *outputString) 并在其中编写我的算法。

一切正常,但最后返回 malloc 错误

:malloc: *** error for object 0x7fff5c8a5bc0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

我写的代码:

void reverseit(char *inputString, char* outputString);

int main()
{
    char input[100];
    cin.getline(input, 10);
    int lenn = strlen(input); 
    char* usrinput = new char[lenn+1];
    usrinput = input;
    int len = strlen(usrinput);
    char* inputString = new char[len+1];
    inputString = usrinput;
    char* outputString = new char[len+1];
    reverseit(inputString, outputString);
    for (int o = 0; o < len; o++)
    {
        cout << outputString[o];
    }
    cout << endl;
    delete [] usrinput;
    delete [] inputString;
    delete [] outputString;
    return 0;

}

void reverseit(char *inputString, char *outputString)
{
    size_t length = strlen(inputString);
    char temp;
    int k = 0;
    for(int i = length - 1; i >= 0 ; i--)
    {
        temp = inputString[i];
        outputString[k] = temp;
        k++;
    }
}

有几个限制确实使这比实际上更困难,因为除了两个 char 指针之外,我不允许将任何其他变量调用到 reverseit 中。

我的具体问题是,如果我要删除所有动态数组,内存泄漏如何仍然发生,是否会发生泄漏,因为我没有为每个数组输入正确的大小?谢谢

【问题讨论】:

  • 你正在删除你不应该删除的东西,并且没有删除你应该删除的东西。尝试按照您删除的char* 指向的内容。
  • 因为您遇到了 malloc 错误,我假设您在 C 库的某些部分做错了什么。我还没有完全阅读你的代码,但是 strlen 依赖于被 \0 终止的字符串,所以我将输入数组定义为 char input[100] = { };否则,数组的内容是未定义的,并且可能暂时不包含 \0。这只是一个猜测(因此不是答案)。
  • @Marckvdv 以这种方式初始化数组是一个很好的技巧,但您似乎真的没有阅读太多代码,他在下一行代码中使用getline 总是添加一个结尾的分隔符。
  • @Excelsius 啊我不知道,除非必须,否则我不会真正在 C++ 中使用 C 风格的东西。
  • @Marckvdv 是的,这看起来很像家庭作业,否则我会说那些讨厌的 char 数组和你好 string :)

标签: c++ arrays memory dynamic-arrays


【解决方案1】:
usrinput = input;
inputString = usrinput;

要复制字符串,您需要复制每个char,而不仅仅是重新分配指针。使用= 会丢弃动态分配的内存——这就是内存泄漏——并导致所有指针指向内存中的同一位置。这意味着usrinputinputString 最终都指向input,因此最后的delete[] 调用失败,因为input 没有分配给new

strcpy(userinput, input);
strcpy(inputString, usrinput);

【讨论】:

    【解决方案2】:

    问题在于以下几行:

    usrinput = input;
    /* ... */
    inputString = usrinput;
    

    这不是复制字符串的正确方法。请改用strcpy()

    strcpy(usrinput, input);
    

    由于您更改了usrinput 的值,因此您在与使用new[] 分配的指针不同的指针(特别是堆栈分配的数组input)上调用delete[]

    【讨论】:

      【解决方案3】:

      您正在释放堆栈上的数组:

      char input[100];      
      ...
      usrinput = input;
      ...
      delete [] usrinput;
      

      我不完全确定您要在这里完成什么,但这肯定不是您正在做的。如果您希望分配的内存将字符串的值保存在堆栈上,您必须执行以下操作:

      my_buffer[100];
      set_my_buffer(my_buffer);
      char *my_allocated_buffer = new char [100];
      strncpy(my_allocated_buffer, my_buffer, 100);
      

      【讨论】:

        【解决方案4】:

        您将input 设置为usrinput 不正确。 inputchar[] 类型,usrinputchar * 类型。执行此操作时,它会将usrinput 重新指向new()[] 运算符分配的不同内存位置。
        当您现在尝试delete[] 时,该变量现在指向您静态分配的char[] 类型,而不是您动态分配的类型。

        int main(){
        
            char input[100];                   // Statically Allocated
            cin.getline(input, 10);
            int lenn = strlen(input); 
            char* usrinput = new char[lenn+1]; // DYNAMICALLY allocated
            usrinput = input;                  // setting statically allocated into variable that
                                               // previously held address of dynamically allocated
                                               // here is where your dynamic pointer gets LOST
        
            int len = strlen(usrinput);
            char* inputString = new char[len+1];  // Another Dynamic Allocation
            inputString = usrinput;               // This WOULD be OK except that now you are losing the
                                                  // where inputString used to point to which was 
                                                  // dynamically allocated
        
            char* outputString = new char[len+1];
            reverseit(inputString, outputString);
            for (int o = 0; o < len; o++)
            {
                cout << outputString[o];
            }
            cout << endl;
            delete [] usrinput;                    // usrinput now doesn't point to a dynamically
                                                   // allocated space in memory, neither does inputString
            delete [] inputString;
            delete [] outputString;
            return 0;
        }
        

        【讨论】:

        • 好的,然后重新表述我的问题,我将如何让用户输入 char* 类型,以便我可以将两种类型 char* = 设置为彼此?字符* usrinput = 新字符[100]; cin.getline(usrinput, 50);在这种情况下,我没有为数组分配特定的大小,所以它仍然会造成泄漏
        • 措辞可以改进:'it thinks' => 编译器将其解析为 blah(或更实质的东西)
        【解决方案5】:

        您分配了新的内存位置,但您将指针重新分配回原始位置。

        例子:

        int * pointer1 = new int[1];
        pointer1[0] = 10;
        int * pointer2 = new int[1];
        pointer2[0] = 20;
        cout << pointer2[0]; // 20
        pointer2 = pointer1; 
        cout << pointer2[0]; // 10
        cout << pointer1[0]; // 10
        delete [] pointer2;
        cout << pointer1[0]; // Oh no! Garbage Value! But we didn't delete pointer1 did we?
        delete [] pointer1; // FATAL ERROR!
        

        这段代码的问题是,当pointer2 = pointer1时,它只是把pointer1的地址给了pointer2。它确实没有复制任何值。这也意味着分配给指针 2 的原始空间将永远丢失。从未释放。这就是导致代码中出现问题的原因:

        char* usrinput = new char[lenn+1];
        usrinput = input;
        

        您想复制所有值。使用strcpy()

        strcpy(usrinput, input)
        

        【讨论】:

          【解决方案6】:

          不能通过将一个指针分配给另一个指针来复制字符数组中包含的字符串。您需要使用像strcpy 这样的函数来搜索指针指向的内存,直到它到达空终止符('\0')并将内容复制到目标指向的内存。

          例子:

          // Initialize an array of characters which contain the string "Foo bar\0";
          char input[] = "Foo bar";
          // Allocate a character array on the heap and assign it to the pointer pDest
          char *pDest = new char[100];
          // Reassign the pointer to the first element of input
          pDest = input;
          delete [] pDest; // Oops: Deleting the first element of input!
          

          相反,您应该这样做:

          char input[] = "Foo bar";
          char *pDest = new char[100];
          // Copy all chars in the memory at input to the memory pointed to by pDest
          strcpy(pDest, input);
          delete [] pDest; // pDest still points to the dynamic memory, everything's fine
          

          请注意,使用 strcpy 可能很危险,它只是将所有可以找到的内容复制到目的地,直到到达终止的 null 为止,它不会(不能!)检查目的地是否真的足够大。在您的示例中,您已经通过使用 strlen 避免了这种情况,这很好。只是任务不像你想的那样工作。

          除了这个错误,你的代码还有其他可以改进的地方:

          • 根本不需要复制,使用getline获取用户输入,用strlen + 1这个用户输入分配一个新数组就完成了,你可以将这两个变量传递给reverseit(记得在reverseit 末尾添加'\0' 分隔符。
          • reverseit中的临时char是不必要的,只需将inputString[i]直接赋值给outputString[k]
          • 您可以将ik 都放在for 声明中,无需以不同方式处理k
          • cout可以处理char*形式的字符串,不需要循环将结果写入控制台,只需写cout &lt;&lt; outputString

          使用 std::string 类可以更轻松地处理 C++ 中的字符串,但我想这是一些家庭作业,您必须坚持基础知识。

          【讨论】:

            猜你喜欢
            • 2016-12-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-08-15
            • 2016-09-12
            • 2015-06-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多