我找不到的 C++ 内存泄漏

C++ memory leak that I can't find

我这里有一个关于粒子光子的小示例程序,它有一个我无法弄清楚的内存错误。

它的作用:加载一个包含小字符串块的缓冲区,然后将该大缓冲区转换回字符串。然后它创建了一堆对象,这些对象只是小块缓冲区的包装器。它重复执行此操作,并且在 setup() 之后我没有分配任何新内存,但内存缓慢下降直至崩溃。

main.cpp

包括变量声明

#include "application.h" //needed when compiling spark locally

#include <string>
#include <unordered_map>
#include "dummyclass.h"

using namespace std;
SYSTEM_MODE(MANUAL);

char* buffer;
unordered_map<int, DummyClass*> store;
string alphabet;
unsigned char alphabet_range;
unsigned char state;
int num_chars;

static const unsigned char STATE_INIT = 0;
static const unsigned char STATE_LOAD_BUFFER = 1;
static const unsigned char STATE_PREP_FOR_DESERIALIZE = 2;
static const unsigned char STATE_FAKE_DESERIALIZE = 3;
static const unsigned char STATE_FINISH_RESTART = 4;

删除对象辅助函数

bool delete_objects()
{
    Serial.println("deleting objects in 'store'");
    for(auto iter = store.begin(); iter != store.end(); iter++)
    {
        delete iter->second;
        iter->second = nullptr;
    } 
    store.clear();

    if(store.empty())
        return true;
    else
        return false;
}

设置函数,分配内存,初始化赋值

void setup()
{
    Serial.begin(9600);
    Serial1.begin(38400);
    delay(2000);

    buffer = new char[9000];
    alphabet = string("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~!@#$^&*()_-?/><[]{}|");
    alphabet_range = alphabet.length() - 1;
    state = STATE_INIT;
    num_chars = 0;
}

循环函数,得到运行一遍又一遍

void loop()
{
    switch(state){
        case STATE_INIT: {

            strcpy(buffer, "");
            state = STATE_LOAD_BUFFER;
            delay(1000);
            break;

        }
        case STATE_LOAD_BUFFER: {

            if(num_chars < 6000){
                string chunk;
                for(char i = 0; i < 200; i++){
                    int index = rand() % alphabet_range;
                    chunk.append(alphabet.substr(index, 1));
                    num_chars++;
                }
                strcat(buffer, chunk.c_str());
            }
            else{
                num_chars = 0;
                state = STATE_PREP_FOR_DESERIALIZE;
            }
            delay(500);
            break;

        }
        case STATE_PREP_FOR_DESERIALIZE: {

            Serial.println("\nAttempting to delete current object set...");
            delay(500);
            if(delete_objects())
                Serial.println("_delete_objects succeeded");
            else {
                Serial.println("_delete_objects failed");
                break;
            }
            state = STATE_FAKE_DESERIALIZE;
            delay(1000);
            break;

        }
        case STATE_FAKE_DESERIALIZE: {

            string buff_string(buffer);
            if(buff_string.length() == 0){
                Serial.println("Main:: EMPTY STRING CONVERTED FROM BUFFER");
            }

            int index = 0;
            int key = 1;
            while(index < buff_string.length())
            {
                int amount = (rand() % 50) + 5;
                DummyClass* dcp = new DummyClass(buff_string.substr(index, amount));
                store[key] = dcp;
                index += amount;
                key++;
            }
            state = STATE_FINISH_RESTART;
            delay(1000);
            break;

        }
        case STATE_FINISH_RESTART: {

            state = STATE_INIT;
            break;

        }
    }

}

dummyclass.h

非常简单,构造函数只是将字符串存储在字符缓冲区中。这个对象只是一个包装器。

using namespace std;

class DummyClass {
    private:
        char* _container;

    public:
        DummyClass(){
        }

        DummyClass(string input){
            _container = new char[input.length()];
            strcpy(_container, input.c_str());
        }

        ~DummyClass(){
            delete _container;
            _container = nullptr;
        }

        char* ShowMeWhatYouGot(){
            return _container;
        }
};

编辑:

这是我遇到的一个真正的问题,我不确定为什么它被否决了。帮帮我,我怎样才能更清楚?我不愿意缩小代码,因为它模仿了一个更大的程序的许多方面,它只是简单地建模。我想保留代码的结构,以防这个错误突然出现 属性。

因此,经过一些测试后,我想对评论正确答案的 Russ Schultz 大声疾呼。如果您想正式 post 解决方案,我很乐意将其标记为正确。

内存错误是由于分配 char 缓冲区 _container 而不考虑空终止字符引起的,这意味着我正在加载一个太大的字符串。 (不完全确定为什么这会导致错误但不会引发错误?)

然而,在另一个网站上,我也收到了这条建议:

            string chunk;
            for(char i = 0; i < 200; i++){
                int index = rand() % alphabet_range;
                chunk.append(alphabet.substr(index, 1));
                // strcat(buffer, alphabet.substring(index, index + 1));
                num_chars++;
            }

This loop looks suspect to me. You are depending on the string append method to grow chunk as needed, but you know you are going to run that loop 200 times. Why not use the string reserve method to just allocate that much space? I bet that this chews up a lot of memory with each new char you append calling realloc, potentially fragmenting memory.

这最终不是解决方案,但了解它可能会有所帮助。

始终考虑字符串终止符:

    DummyClass(string input){
        _container = new char[input.length()];
        strcpy(_container, input.c_str());
    }

分配的字节太少,无法容纳随后复制到其中的输入字符串和终止符。最后附加的 [=11=] 覆盖了一些东西,这很可能是将分配的内存片段成功重新集成回堆所需的元数据。我真的很惊讶它没有崩溃...

它可能不会在每次分配时都发生(只有当你溢出到一个新的 8 字节对齐的块中时),但一次就足够了:)