通过指针给std::string赋值时memcpy.asm错误的解决方法
自定义Stack模板处理std::string时触发访问违例的原因与解决思路
问题描述
我实现了一个固定大小的Stack模板结构体,处理基本类型和自定义结构体时均正常,但使用std::string时抛出access violation reading location异常,错误发生在memcpy.asm中,MSVS2019显示错误指向赋值语句的下一行有效代码。计算得sizeof(std::string)为28字节,64字节的Stack理论上足够容纳它加额外的大小标识字节,求问题原因和解决思路。
原代码
// Stack结构体代码 #include<iostream> #include<Windows.h> template<uint32_t S> struct Stack { size_t size; byte base[S]; byte* top; // Stack() { top = (byte*)base; size = 0; } ~Stack(){} // template<class T> void alloc(T*& rt) { if (sizeof(T) > MAXBYTE) return; rt = (T*)top; top += sizeof(T); size += sizeof(T) + 1; *top = sizeof(T); top++; } void alloc(void*& rt, size_t arg) { if (arg > MAXBYTE) return; rt = top; top += arg; size += arg + 1; *top = arg; top++; } template<class T> void* push(T arg) { if (arg > MAXBYTE) return 0; T* rt; alloc<T>(rt); *rt = arg; return rt; } template<class T> T&& pop() { if (top == base) return 0; top--; size -= *top; top -= *top; return (T&&)*(T*)top; } void pop() { if (top == base) return; top--; size -= *top; top -= *top; } }; // 测试代码 int main() { Stack<64> t; std::string* a; t.alloc(a); *a = "TEST"; // std::cout << "press enter to exit..."; std::cin.get(); return 0; }
问题原因
- 内存对齐不满足:
std::string作为非平凡类型,在64位系统下通常要求8字节对齐。Stack直接将top指针强制转换为T*,但top的地址可能不满足std::string的对齐要求,赋值操作时触发内存访问错误。 - 未构造对象直接赋值:
alloc函数仅分配了内存空间,未调用std::string的构造函数。std::string内部维护着指向堆内存的指针等状态,未初始化的对象直接赋值会导致未定义行为,比如访问野指针。 - 边界检查不严谨:
alloc仅检查单个元素大小是否超过MAXBYTE,未验证Stack剩余空间是否足够容纳元素加标识字节(虽然本次理论空间足够,但逻辑存在漏洞)。 - pop函数逻辑错误:返回
T&&的实现中,返回的是已释放内存位置的引用,属于悬垂引用,后续访问会触发未定义行为。
解决思路
- 保证内存对齐:使用
std::align(需包含<memory>头)调整top指针,确保分配的地址满足T的对齐要求。 - 正确构造与析构对象:
- 分配内存后,用定位new(
new (rt) T())构造对象; - 弹出对象时,必须调用对象的析构函数,避免资源泄漏和未定义行为。
- 分配内存后,用定位new(
- 完善边界检查:计算Stack剩余空间,判断是否足够容纳当前元素加标识字节,不足则拒绝分配。
- 修正pop函数逻辑:避免返回悬垂引用,改为通过输出参数返回弹出的对象,确保操作的安全性。
修正后的示例代码
#include<iostream> #include<Windows.h> #include<string> #include<memory> // 用于std::align template<uint32_t S> struct Stack { size_t size; byte base[S]; byte* top; Stack() : top(reinterpret_cast<byte*>(base)), size(0) {} ~Stack() { // 析构所有剩余对象 while (top != base) { pop(); } } template<class T> bool alloc(T*& rt) { const size_t elem_size = sizeof(T); const size_t required = elem_size + 1; const size_t remaining = S - (top - base); // 检查元素大小和剩余空间 if (elem_size > MAXBYTE || remaining < required) { return false; } // 调整指针以满足对齐要求 std::align(alignof(T), elem_size, top, remaining); rt = reinterpret_cast<T*>(top); // 定位new构造对象 new (rt) T(); top += elem_size; size += required; *top = static_cast<byte>(elem_size); top++; return true; } template<class T> bool push(const T& arg) { T* rt; if (!alloc(rt)) { return false; } *rt = arg; return true; } template<class T> bool pop(T& out) { if (top == base) { return false; } top--; size_t elem_size = *top; // 校验类型大小匹配 if (elem_size != sizeof(T)) { top++; return false; } top -= elem_size; T* elem = reinterpret_cast<T*>(top); out = std::move(*elem); elem->~T(); // 析构对象 size -= elem_size + 1; return true; } void pop() { if (top == base) { return; } top--; size_t elem_size = *top; top -= elem_size; // 通用pop无法确定类型,建议优先使用带输出参数的pop版本 size -= elem_size + 1; } }; // 测试代码 int main() { Stack<64> t; std::string* a; if (t.alloc(a)) { *a = "TEST"; std::cout << "Stored string: " << *a << std::endl; } std::string out_str; if (t.pop(out_str)) { std::cout << "Popped string: " << out_str << std::endl; } std::cout << "press enter to exit..."; std::cin.get(); return 0; }
内容的提问来源于stack exchange,提问作者nikodem0808
相关产品推荐
相关产品推荐

