我写了一段代码,积累了不必要的内存空间,并在需要时将其归还,将其组织为堆栈(客户的要求很奇怪)。
void* stack = nullptr;
template <typename P>
void push(P* p) {
// reinterpret memory space of *p as a storage for the address of the top element of the stack;
*reinterpret_cast<void**>(p) = stack;
// now *p is the top element, so update stack to point to *p;
stack = p;
}
template <typename P>
P* pop() {
if (stack == nullptr) throw;
// get the address of the top element
P* p = static_cast<P*>(stack);
// now stack should point to the next element after *p, which address is stored in *p's memory space
stack = *reinterpret_cast<void**>(stack);
return p;
}
stack
是指向顶部元素的指针。当新的内存空间为 pushed
时,其前 8 个字节用于存储前一个元素的地址,stack
被更新。一个简单的栈链。
当我试图实现这种行为时,编译器经常这样说(我知道某些内存操作是非法的):
对强制转换的赋值是非法的,不支持左值强制转换
我想知道我的实施现在是否合法?
#include <iostream>
#include <bitset>
constexpr size_t bytes_8 = 8 * 8;
constexpr size_t bytes_20 = 8 * 20;
constexpr size_t bytes_50 = 8 * 50;
// we assume that std::bitset holds its data contigiously in the beginning
struct A {
std::bitset<bytes_8> bits;
};
struct B {
std::bitset<bytes_20> bits;
};
struct C {
std::bitset<bytes_50> bits;
};
int main() {
// Imagine they were allocated on heap:
A a; B b; C c;
std::cout << "Addresses" << '\n'
<< "a: " << &a << "\t"
<< "b: " << &b << "\t"
<< "c: " << &c
<< std::endl << std::endl << std::endl;
push<A>(&a);
std::cout << "---------- push<A>(&a) ----------\n"
<< "'a' is the top element (and the only one), so 'stack' points to it,\n"
<< "meanwhile 'a' stores the address of the previous element (none)\n\n"
<< "stack: " << stack << "\ta.bits: " << std::hex << a.bits.to_ullong()
<< std::endl << std::endl;
push<B>(&b);
std::cout << "---------- push<B>(&b) ----------\n"
<< "now 'b' is the top element, so 'stack' updates to point to it,\n"
<< "'b' stores the address of the previous element (a)\n\n"
<< "stack: " << stack << "\tb.bits: " << std::hex << b.bits.to_ullong()
<< std::endl << std::endl;
push<C>(&c);
std::cout << "---------- push<C>(&c) ----------\n"
<< "finally 'c' is the top element and 'stack' points to it,\n"
<< "'c' stores the address of the previous element (b)\n\n"
<< "stack: " << stack << "\tc.bits: " << std::hex << c.bits.to_ullong()
<< std::endl << std::endl << std::endl;
auto p1 = pop<C>();
std::cout << "---------- pop<C>() ----------\n"
<< "'b' is the top element and the next one after it is 'a'\n\n"
<< "ret: " << p1 << "\nstack: " << stack << "\tb.bits: " << std::hex << b.bits.to_ullong()
<< std::endl << std::endl;
auto p2 = pop<B>();
std::cout << "---------- pop<B>() ----------\n"
<< "'a' is the top element and the last one in the stack\n\n"
<< "ret: " << p2 << "\nstack: " << stack << "\ta.bits: " << std::hex << a.bits.to_ullong()
<< std::endl << std::endl;
auto p3 = pop<A>();
std::cout << "---------- pop<A>() ----------\n"
<< "the stack is empty\n\n"
<< "ret: " << p3 << "\nstack: " << "0x" << stack
<< std::endl << std::endl;
return 0;
}
Addresses
a: 0x7fffffffdc20 b: 0x7fffffffdc40 c: 0x7fffffffdc60
---------- push<A>(&a) ----------
'a' is the top element (and the only one), so 'stack' points to it,
meanwhile 'a' stores the address of the previous element (none)
stack: 0x7fffffffdc20 a.bits: 0
---------- push<B>(&b) ----------
now 'b' is the top element, so 'stack' updates to point to it,
'b' stores the address of the previous element (a)
stack: 0x7fffffffdc40 b.bits: 7fffffffdc20
---------- push<C>(&c) ----------
finally 'c' is the top element and 'stack' points to it,
'c' stores the address of the previous element (b)
stack: 0x7fffffffdc60 c.bits: 7fffffffdc40
---------- pop<C>() ----------
'b' is the top element and the next one after it is 'a'
ret: 0x7fffffffdc60
stack: 0x7fffffffdc40 b.bits: 7fffffffdc20
---------- pop<B>() ----------
'a' is the top element and the last one in the stack
ret: 0x7fffffffdc40
stack: 0x7fffffffdc20 a.bits: 0
---------- pop<A>() ----------
the stack is empty
ret: 0x7fffffffdc20
stack: 0x0
即使您设法删除未定义的行为,您的实现也不可能工作。
将元素推入堆栈会覆盖其中的一部分。这是一个有损操作。
sizeof void*
对象表示开头的 *p
字节已经消失,永远无法恢复。即使您不再使用该对象,它的析构函数仍然需要运行,并且通常需要该数据。