You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ctypes技术问询:安全返回Python函数对象且不改变指针地址

Alright, let's tackle this problem step by step. The core issue here is that your buffer variable in the func function is a local object—once the function finishes running, Python's garbage collector will clean it up, leaving the data pointer in your G struct pointing to freed memory (a dangling pointer). To fix this, we need to tie the buffer's lifecycle to the G instance so they stick around together.

The Fix: Bind the Buffer to the G Instance

We'll add a regular Python attribute to the G class to hold a reference to the buffer. This ensures the buffer won't be garbage-collected as long as the G instance exists, keeping the data pointer valid and its address unchanged.

Here's the revised code:

import ctypes

class G(ctypes.Structure):
    _fields_ = [("data", ctypes.POINTER(ctypes.c_ubyte)), ("length", ctypes.c_int)]
    
    def __init__(self, length):
        self.length = length
        # Create the buffer and store a reference to it in the instance
        self._buffer = (ctypes.c_ubyte * length)()
        # Cast the buffer to the required pointer type once—address stays fixed
        self.data = ctypes.cast(self._buffer, ctypes.POINTER(ctypes.c_ubyte))

def func(filename):
    g = G(500)
    # Read file content directly into the buffer tied to the G instance
    with open(filename, 'rb') as f:
        f.readinto(g._buffer)
    return g

h = func('your_file_path_here')

Key Explanations

  • Buffer Lifecycle Binding: The _buffer attribute is a regular Python attribute (not part of the ctypes _fields_, since ctypes doesn't need to interact with it). As long as the G instance (h in your example) exists, _buffer maintains a reference to the memory block, so Python won't free it.
  • Fixed Pointer Address: We cast the buffer to the data pointer once in G's __init__. The buffer's memory address never changes, so data will always point to the same valid location.
  • No Local Buffer Risk: We no longer use a local buffer variable in func—instead, we use the buffer attached directly to the G instance, eliminating the chance of it being cleaned up when the function returns.

If you prefer not to modify the G class's __init__ method, you can alternatively attach the buffer in the func function like this:

def func(filename):
    g = G(500)
    # Attach the buffer to the G instance to preserve it
    g._buffer = (ctypes.c_ubyte * g.length)()
    with open(filename, 'rb') as f:
        f.readinto(g._buffer)
    g.data = ctypes.cast(g._buffer, ctypes.POINTER(ctypes.c_ubyte))
    return g

The core principle here is simple: any memory pointed to by a ctypes pointer must have a corresponding Python object holding a reference to it—otherwise, Python will free that memory out from under you, leading to crashes or undefined behavior.

内容的提问来源于stack exchange,提问作者jtlz2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:51:10