在子程序中保留内存,如何在main()中释放内存?Lifetimeproblem吗?



为了使我的代码更具可读性,我测试了在一个额外的函数中导出动态内存保留(HEAP)。返回一个指向预留内存起始位置的指针。

但是…如何在使用后释放保留的内存?

int i;
#define buffersize    5
// -------------------------------------------------------------------------------
char * dynReservation_of_Memory(long sizeOfBuf_in_Bytes) {
// -------------------------------------------------------------------------------
char *Buf = new(std::nothrow) char[sizeOfBuf_in_Bytes];
if (!Buf) { Serial.println("Error dyn. memory allocation >> Programmstopp");  while(1){}; }
//
for (byte i = 0; i<sizeOfBuf_in_Bytes; i++) {Buf[i] = i+20;} // test: filling the buffer with 20...24
//
return(&Buf[0]); // adress of first byte in buffer
} // ende Fkt
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Test-Sketch");
//
char * Buffer = dynReservation_of_Memory(buffersize);           
for (byte i = 0; i<buffersize; i++) 
{ Serial.println(Buffer[i], DEC); }         // does the buffer contains the testdata 20...24?
Serial.println("");
//
for (byte i = 0; i<buffersize; i++)         // fill the buffer with new testdata 10...14
{ Buffer[i] = i+10; }
//
for (byte i = 0; i<buffersize; i++)         // does the buffer contains the new testdata 10...14?
{ Serial.println(Buffer[i], DEC); }
Serial.println("");
// -----------------------------------------------------
delete[] Buffer;                            // free reserved memory
if (!Buffer) { Serial.println("Memory no more reserved..."); }                // check, whether memory is free
else       { Serial.println("why the <delete[] Buffer> doesn´t worked?");}
// -----------------------------------------------------
} // end of setup()
void loop() {
// -------------------------
Serial.print(i);  i++; delay(5000);
} // end of loop

我的testprogram的响应是: *

Test-Sketch

20

21

22

23日

24日10

11

1213

14为什么要删除[]Buffer>并´t工作?

*

一个简单的MCVE可能会让OP相信delete[]做了应该做的事情。为此,使用了一个示例struct,它记录构造函数和析构函数的调用。

MCVE on coliru:

#include <iostream>
struct Test {
static inline unsigned idGen = 0;
const unsigned id;

Test(): id(++idGen)
{
std::cout << "Test::Test() -> Test { id: " << id << " } createdn";
}
~Test()
{
std::cout << "Test::~Test() -> Test { id: " << id << " } destroyedn";
}
};
Test* makeTests(size_t n)
{
Test* pTests = new(std::nothrow) Test[n];
return pTests;
}
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";n"; __VA_ARGS__ ; std::cout << 'n'
int main()
{
DEBUG(Test *pTests = makeTests(5));
DEBUG(std::cout << (void*)pTests << 'n');
DEBUG(delete[] pTests);
DEBUG(std::cout << (void*)pTests << 'n');
}

输出:

Test *pTests = makeTests(5);
Test::Test() -> Test { id: 1 } created
Test::Test() -> Test { id: 2 } created
Test::Test() -> Test { id: 3 } created
Test::Test() -> Test { id: 4 } created
Test::Test() -> Test { id: 5 } created
std::cout << (void*)pTests << 'n';
0xbd0c38
delete[] pTests;
Test::~Test() -> Test { id: 5 } destroyed
Test::~Test() -> Test { id: 4 } destroyed
Test::~Test() -> Test { id: 3 } destroyed
Test::~Test() -> Test { id: 2 } destroyed
Test::~Test() -> Test { id: 1 } destroyed
std::cout << (void*)pTests << 'n';
0xbd0c38

delete[]之后,指针仍然指向内存中的同一个地址。尽管如此,内容还是被公布了。在delete[]之后取消引用指针将导致未定义行为。


可能会期望delete/delete[]可以重置指针,但事实并非如此。

中提到了这个话题

Bjarne Stroustrup的c++风格和技术FAQ:

为什么不删除0的操作数?

考虑
delete p;
// ...
delete p;

如果…第一部分没有触及p,然后第二部分"删除p";是一个严重的错误,c++实现无法有效地保护自己(如果没有特殊的预防措施)。由于从定义上讲,删除零指针是无害的,因此一个简单的解决方案是使用&;delete p;&;p=0;"在它完成其他要求之后。然而,c++并不能保证。

原因之一是delete的操作数不必是左值。考虑:

delete p+1;
delete f(x);

在这里,delete的实现没有一个可以赋值为零的指针。这些例子可能很少见,但它们确实暗示不可能保证any pointer to a deleted object is 0.'' A simpler way of bypassing that规则"是有两个指向对象的指针:

T* p = new T;
T* q = p;
delete p;
delete q; // ouch!

c++显式地允许delete的实现将左值操作数归零,我曾希望实现会这样做,但这种想法似乎并未受到实现者的欢迎。如果您认为将指针归零很重要,请考虑使用destroy函数:

template<class T> inline void destroy(T*& p) { delete p; p = 0; }

考虑这又是一个通过依赖标准库容器、句柄等来最小化new和delete的显式使用的原因。

请注意,将指针作为引用传递(允许指针被归零)有一个额外的好处,即防止对右值调用destroy():

int* f();
int* p;
// ...
destroy(f()); // error: trying to pass an rvalue by non-const reference
destroy(p+1); // error: trying to pass an rvalue by non-const reference

最新更新