Python调用C++编译的.so动态库报std::bad_alloc错误排查
Python调用C++动态库读文件异常的根因与修复方案
问题1:std::bad_alloc崩溃原因
extern "C"仅修改函数符号的导出命名规则,让C ABI可以识别该函数,不会改变函数参数的内存布局和调用约定。C兼容接口绝对不能使用std::string这类C++非POD类型作为参数或返回值。- 最初版本的C接口参数为
std::string,Python侧ctypes传入的实际是C风格的const char*裸指针,C++侧会把这个指针错误解析为std::string对象的起始内存地址,读取到完全非法的内存结构,触发内存分配失败,最终抛出std::bad_alloc异常。
问题2:改为const char*后无法进入读循环的原因
- 核心问题是没有在Python侧显式声明C函数的参数类型:
ctypes默认对Python字符串的传参处理没有严格类型校验,不同Python版本、平台下的默认编码行为不一致,很容易传入非法的路径字符串,导致C++侧ifstream打开文件直接失败。 - C++代码没有做文件打开状态校验,文件打开失败时静默跳过了所有读取逻辑,表现为无法进入while循环。
修复方案
C++侧代码修正
确保对外暴露的C接口全部使用C兼容的基础类型,内部再做C++类型转换,同时补充必要的错误检查:
#include <fstream> #include <string> #include <vector> #include <iostream> #include <algorithm> // 补全split函数实现,如果你已有实现可忽略 std::vector<std::string> split(const std::string& s, char delimiter) { std::vector<std::string> tokens; std::string token; for (char c : s) { if (c == delimiter) { if (!token.empty()) tokens.push_back(token); token.clear(); } else { token += c; } } if (!token.empty()) tokens.push_back(token); return tokens; } void ReadTextFile(const char* fileName) { std::ifstream file(fileName); // 校验文件打开状态 if (!file.is_open()) { std::cerr << "Open file failed, path: " << fileName << std::endl; return; } std::string line; while(std::getline(file, line)) { std::vector<std::string> result = split(line, ':'); if (result.size() > 1) { result[1].erase(std::remove(result[1].begin(), result[1].end(), ' '), result[1].end()); std::cout << result[1] << std::endl; } } file.close(); } extern "C" { // 对外C接口仅使用C兼容基础类型 void ReadTextFileCpp(const char* fileName) { ReadTextFile(fileName); } }
编译动态库命令(g++环境):g++ -fPIC -shared -o ReadTextFile.so ReadTextFile.cpp -std=c++11
Python侧代码修正
加载动态库后必须显式声明函数的参数类型,传参时手动将Python字符串编码为UTF-8字节串,避免隐式类型转换的异常:
from ctypes import cdll, c_char_p lib2 = cdll.LoadLibrary('./ReadTextFile.so') # 显式声明函数参数类型 lib2.ReadTextFileCpp.argtypes = [c_char_p] # 传入路径时手动编码为字节串 file_path = '0166_405504.txt' lib2.ReadTextFileCpp(file_path.encode('utf-8'))
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

