reinterpret_cast何时违反C++标准?自定义栈实现是否合法?
闲置内存栈式管理的C++代码合规性问题
我写了一段代码,用栈结构管理闲置内存,按需分配(客户特殊需求),实现如下:
void* stack = nullptr; template <typename P> void push(P* p) { // 将*p的内存空间重新解释为存储栈顶元素地址的空间 *reinterpret_cast<void**>(p) = stack; // 现在*p是栈顶元素,更新stack指针指向它 stack = p; } template <typename P> P* pop() { if (stack == nullptr) throw; // 获取栈顶元素地址 P* p = static_cast<P*>(stack); // stack应该指向*p之后的下一个元素,该地址存储在*p的内存空间中 stack = *reinterpret_cast<void**>(stack); return p; }
这里stack是指向栈顶元素的指针,新内存入栈时,其前8字节用于存储前一个栈顶元素的地址,同时更新stack指针,形成简单的链式栈结构。
之前实现时编译器多次报错:assignment to cast is illegal, lvalue casts are not supported,我知道部分内存操作不符合C++标准,现在咨询两个问题:
- 当前的代码实现是否符合C++标准?
reinterpret_cast在哪些情况下会违反C++标准?
代码运行示例及输出
结构体定义
#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; // 假设std::bitset的开头是连续存储的数据 struct A { std::bitset<bytes_8> bits; }; struct B { std::bitset<bytes_20> bits; }; struct C { std::bitset<bytes_50> bits; };
主函数代码
int main() { // 假设它们是在堆上分配的: 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
解答
1. 当前代码是否符合C++标准?
不符合,核心问题如下:
- 对象状态非法篡改:直接通过
reinterpret_cast修改P类型对象的内存(将其前8字节当作void*存储)违反了C++对象模型规则。示例中的A/B/C包含std::bitset,其内部布局未被标准规定,直接修改内存会破坏对象状态,触发未定义行为。 - 语法错误:
pop函数中throw;未指定异常对象,不符合C++语法要求,必须后跟具体的异常表达式(如throw std::runtime_error("栈下溢");)。 - 指针转换的未定义行为:
static_cast<P*>(stack)仅当stack确实指向P类型对象时合法,若栈中混入非P类型对象则触发UB;reinterpret_cast<void**>(p)要求p指向的内存满足void*的对齐要求,若P类型对齐级别低于void*,会导致未对齐访问,属于UB。 - 兼容性问题:你遇到的编译器报错,本质是部分编译器对
reinterpret_cast结果作为左值的支持不符合标准兼容性,也是代码不合规的表现。
2. reinterpret_cast哪些情况会违反C++标准?
reinterpret_cast是C++中风险最高的转换操作,以下场景会触发未定义行为或违反标准:
- 跨类型访问内存:将
int*转为float*后解引用,或无关类类型指针互转后访问成员,属于未定义行为。 - 违反对齐要求:转换后的指针指向的内存不满足目标类型的对齐要求(如将
char*转为double*但地址未对齐到8字节),解引用会触发UB。 - 函数指针与非函数指针互转:标准规定这种转换结果未定义,即便转换回原类型也不保证可用性。
- 篡改对象类型表示:将对象指针转为其他类型指针并修改内容,除非两种类型对象表示完全兼容(如标准布局类型首成员相同),否则属于UB。
- 指针转整数超出范围:若指针大小大于目标整数类型大小,转换结果未定义。
- 去除const/volatile后修改:虽语法允许,但通过转换后的指针修改原
const对象会触发UB(const_cast才是标准的限定符转换方式,但修改const对象本身依然是UB)。
内容的提问来源于stack exchange,提问作者Alexander S
相关产品推荐
相关产品推荐

