我在这里有一个关于粒子光子的小例子程序,它有一个我无法弄清楚的内存错误。
它的作用:加载一个带有小字符串块的缓冲区,将该大缓冲区转换回字符串。然后它创建了一堆对象,这些对象只是小块缓冲区的包装器。它重复这样做,并且我没有在setup()之后分配任何新内存,但内存缓慢下降直到它崩溃。
#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;
}
}
}
非常小,构造函数只是将字符串存储在字符缓冲区中。这个对象只是一个包装器。
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属性。
答案 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的每个新字符带来大量内存,可能会破坏内存。
这最终不是解决方案,但可能很高兴知道。