最近调查一个protocol buffer库的问题:应用程序使用了libprotobuf-lite库,在调试Debug版本时如果链接了Release版本的libprotobuf-lite库,会在调用RepeatedPtrFieldBase::Destroy()时报告堆出错;但是如果链接的是Debug版本的libprotobuf-lite库就能正常运行。
调查了很长时间,原因可能是这样的:
1. 在编译Release版本的libprotobuf-lite库时,链接的是Release版本的VC库:
void RepeatedPtrFieldBase::Reserve(int new_size) { 调用
void** RepeatedPtrFieldBase::InternalExtend(int extend_amount) {
...
rep_ = reinterpret_cast<Rep*>(
new char[kRepHeaderSize + sizeof(old_rep->elements[0]) * new_size]);
是在protobuf库文件repeated_field.cc中定义的普通类成员函数,new引用的(malloc)是Release版本的VC库;
2. 在编译Debug版本的应用程序时用到了protobuf库的模板函数:
template <typename TypeHandler>
void RepeatedPtrFieldBase::Destroy() {
...
delete [] reinterpret_cast<char*>(rep_);
模板函数要有具体的模板参数才能生成代码,因此会延迟到编译应用程序时才生成具体代码,delete引用的(free)是Debug版本的VC库。
于是内存分配和释放采用了不同的库,导致了前面描述的问题。
总结:一般第三方程序库在链接前就完成了所有目标文件(.obj)的生成,链接生成库时内存分配和释放代码会引用相同版本的VC库,因此不管应用程序是Debug版本还是Release版本使用都没有问题。而象protobuf这样带有模板函数的程序库,相关的二进制代码在链接库时没有全部生成,部分延期到了编译应用程序代码时,因此无法保证库导出函数和模板函数会引用相同版本的VC库,一旦这两处出现内存分配和释放的处理交叉就会导致潜在的内存错误。