我无法找到的C ++内存泄漏

时间:2015-10-20 21:22:48

标签: c++ memory-management memory-leaks microcontroller

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

它的作用:加载一个带有小字符串块的缓冲区,将该大缓冲区转换回字符串。然后它创建了一堆对象,这些对象只是小块缓冲区的包装器。它重复这样做,并且我没有在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;
        }
};

编辑:

这是我遇到的一个真正的问题,我不确定为什么它会被贬低。帮助我,我怎么能更清楚?我不愿意缩小代码,因为它模仿了一个更大的程序的许多方面,它只是简单的建模。我希望保持代码的结构,以防这个bug是一个emergent属性。

2 个答案:

答案 0 :(得分:1)

始终考虑字符串终结符:

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

分配一个太少的字节来保存输入字符串和终止符,然后将其复制到其中。最后附加的\0会覆盖某些内容,这很可能是将已分配的内存片段重新集成到堆中所需的元数据。我真的很惊讶它并没有崩溃......

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

答案 1 :(得分:-1)

所以,经过一些测试后,我想向 Russ Schultz 发表评论,他们给出了正确答案。如果您想正式发布解决方案,我很乐意将其标记为正确。

内存错误是由于在不考虑空终止字符的情况下分配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++;
            }
     

这个循环看起来对我很怀疑。你依赖于字符串追加方法来根据需要增长块,但是你知道你要运行那个循环200次。为什么不使用字符串保留方法来分配那么多空间?我敢打赌,这会为你追加调用realloc的每个新字符带来大量内存,可能会破坏内存。

这最终不是解决方案,但可能很高兴知道。