CPython中ctypes.c_int与ctypes.py_object(整数)的内部差异
CPython 3.9.6中整数PyObject与ctypes.c_int的内部表示差异
1. ctypes.c_int对象的内存解析
你执行的代码:
>>> x = 1234 >>> c1 = c_int(x) >>> id(c1) 4339302720 >>> type(c1) <class 'ctypes.c_int'> >>> c1.value 1234 >>> cast(addressof(c1),POINTER(c_int)).contents.value 1234
c1是Python层面的ctypes.c_int对象,id(c1)返回的是这个PyObject在Python堆中的内存地址。ctypes的基础数值类型(如c_int)内部布局里,直接嵌入了对应C类型的数值存储区域。addressof(c1)获取的就是这个嵌入的C整数的内存起始地址,所以用POINTER(c_int)解析后能直接读取到1234,你的推测正确——c1.value内部就是直接读取这块C数值存储区域的值。
2. ctypes.py_object对象的内存解析
你执行的代码:
>>> c2 = cast(id(x),py_object) >>> id(c2) 4337584448 >>> type(c2) <class 'ctypes.py_object'> >>> c2.value 1234 >>> cast(addressof(c2),POINTER(c_int)).contents.value 43544656
针对你的疑问逐一解答:
c2确实是ctypes.py_object类型的PyObject,id(c2)是它在Python堆中的内存地址,和c1一样是独立的Python对象。ctypes.py_object的核心作用是存储指向另一个PyObject的指针,而非直接存储数值。addressof(c2)拿到的是这个指针的存储地址,你用POINTER(c_int)解析得到的43544656,本质就是原整数对象x的内存地址(即id(x)的数值)——如果是64位系统,这里读取的是指针的低4字节(小端模式下),但核心是这个值是x的地址,而非1234本身。- 当访问
c2.value时,ctypes.py_object会把内部存储的指针当作目标PyObject的地址,直接引用对应的Python对象(也就是x),所以返回的结果就是1234。
核心差异总结
- ctypes.c_int:是C语言
int类型的Python包装器,内部嵌入C风格的数值存储,和Python原生整数是完全独立的实体,value直接对应C数值。 - Python原生整数(PyLongObject):CPython中所有整数都是
PyLongObject结构体实例,包含PyObject通用头部(引用计数、类型指针等),后续字段存储整数的实际数值(多位数用数组存储),id(x)返回的是这个结构体的内存起始地址。 - ctypes.py_object:是
ctypes用于传递Python对象指针的包装类型,内部存储目标PyObject的地址,value属性通过该地址取出对应的Python对象。
权威参考依据
- CPython官方文档的
ctypes模块章节,明确了各类ctypes类型的设计目的,其中py_object用于持有Python对象引用。 - CPython源码:
Include/longobject.h定义了PyLongObject的结构;Modules/_ctypes/ctypes.h及相关实现文件,定义了ctypes包装对象的底层结构,包括py_object对应的内部存储逻辑。
内容的提问来源于stack exchange,提问作者neowise
相关产品推荐
相关产品推荐

