C++类头初始化vector触发随机段错误,移到构造函数恢复是什么原因?
根本原因
你触发未定义行为的核心问题是SDL音频回调的userdata参数传递和类型转换完全不匹配:
- 你在初始化
SDL_AudioSpec时,给want_.userdata赋值的是&sound_userdata_,也就是类成员sound_userdata_(从后续代码逻辑看是一个int类型变量)的地址 - 但在静态转发函数
forward_audio_callback里,你直接把这个userdata强制转换成了Interface*类型,并用这个非法的指针调用了非静态成员函数audio_callback
这个错误的指针转换意味着你所有对Interface成员变量的访问(比如audio_buffer_、sample_length_)都是在访问完全不属于当前Interface实例的内存地址,属于典型的野指针越界写。
各类“临时修复”生效的原因
所有你测试出来的“修复方案”都没有解决核心的指针错误,只是因为内存布局变化刚好避开了段错误:
- 把
screen_memory_初始化移到构造函数内生效:类内默认初始化和构造函数内赋值会改变编译器对类成员的内存布局排序、对齐策略,你越界写的地址刚好不再覆盖screen_memory_内部存储指针的区域,所以不会出现vector访问异常。 - 把
sample_length_改成局部变量生效:原来的代码中调用audio_callback时需要往错误的Interface*偏移对应位置写成员变量sample_length_,改成局部变量后不需要对类成员做写操作,刚好避开了越界写的动作。 - 把
sample_length_改成public生效:C++标准允许编译器对private和public的成员变量调整内存排布顺序,调整后越界写的偏移地址刚好没有踩到关键的内存结构,属于巧合。
正确修复方案
你需要统一userdata的类型:如果你需要在回调里访问Interface的成员,就把want_.userdata设为this(当前Interface实例的指针),然后在audio_callback里从当前实例取出sound_userdata_即可:
// 构造函数内修改userdata赋值 want_.userdata = this; // 回调函数修改 void Interface::audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) { audio_buffer_ = reinterpret_cast<Sint16 *>(raw_buffer); int sample_length = bytes / 2; int & sample_nr = sound_userdata_; for (int i = 0; i < sample_length; i++, sample_nr++) { double time = (double) sample_nr / (double) SAMPLE_RATE; audio_buffer_[i] = static_cast<Sint16>( AMPLITUDE * (2 * (2 * floor(220.0f * time) - floor(2 * 220.0f * time)) + 1)); } }
额外问题:你的SDL初始化代码也存在逻辑错误,
SDL_Init(SDL_INIT_AUDIO != 0)的写法是把布尔值传给了SDL_Init的初始化标志位参数,正确写法是一次性传入要初始化的模块:if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) != 0)
内容的提问来源于stack exchange,提问作者TheMackou
相关产品推荐
相关产品推荐

