Python2转Python3时ctypes调用带char*参数的C函数报错原因排查
Python2到Python3 ctypes迁移:访问冲突错误原因分析
问题场景
将Python2中基于ctypes调用C函数的代码迁移至Python3.11时,触发内存访问错误:OSError: exception: access violation writing 0x00000000000094E0
C代码(mydll.c)
// mydll.c #include <stdio.h> void myfunc(char* c, int i, char* c2) { printf("Hello World"); } int main() { return 0; }
Python代码(foo.py)
# foo.py import ctypes import sys PY3 = sys.version_info.major == 3 if PY3: clibrary = ctypes.WinDLL("mydll.dll", winmode=1) else: clibrary = ctypes.WinDLL("mydll.dll") prototype = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p) c1 = (ctypes.c_char * 512)() i = ctypes.c_int(0) c2 = (ctypes.c_char * (600 * 8))() func = prototype(("myfunc", clibrary)) func(c1, i, c2)
错误根源
问题核心是调用约定不匹配,和字符串编码无关:
ctypes.WinDLL默认加载的函数使用stdcall调用约定,但CFUNCTYPE默认采用cdecl调用约定。Python2中ctypes实现较宽松,未触发明显错误;Python3的ctypes对调用栈校验更严格,两种约定混用导致栈结构损坏,最终引发内存访问冲突。- 若你的C函数用MSVC编译,默认是
cdecl约定(除非显式声明__stdcall),此时用WinDLL加载库本身就和函数约定不匹配,再叠加CFUNCTYPE的约定差异,直接触发错误。
修复方案
两种方式二选一,确保调用约定完全匹配:
方案1:使用对应WinDLL的原型类型
WinDLL加载的函数需用WINFUNCTYPE(默认绑定stdcall)定义原型,替换原有的CFUNCTYPE:
prototype = ctypes.WINFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p)
方案2:改用CDLL加载库(匹配cdecl约定)
若C函数是默认的cdecl约定,改用CDLL加载库,保持CFUNCTYPE原型不变:
# 修改库加载逻辑 if PY3: clibrary = ctypes.CDLL("mydll.dll", winmode=1) else: clibrary = ctypes.CDLL("mydll.dll")
额外优化:整数参数无需手动封装为ctypes.c_int,直接传数值0即可,ctypes会自动完成类型转换:
func(c1, 0, c2)
验证效果
修改后,Python2和Python3环境下均可正常调用C函数,不会再出现内存访问冲突错误。
内容的提问来源于stack exchange,提问作者ptatulea
相关产品推荐
相关产品推荐

