为了使我的代码更具可读性,我测试了在一个额外的函数中导出动态内存保留(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