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

Zig中const引用传递给C函数为何触发段错误?

Zig编写Python C扩展的段错误原因及替代实现

问题重现

正常运行的代码

const c = @cImport({
    @cInclude("Python.h");
});

var module = c.PyModuleDef{
    .m_name = "__name__",
    .m_doc = "__doc__",
    .m_size = -1,
};

export fn PyInit_hello() [*c]c.PyObject {
    return c.PyModule_Create(&module);
}

编译运行命令:

$ zig build-lib -dynamic -femit-bin=hello.so -I/usr/include/python3.12 -L/usr/lib -lpython3.12 -lc hello.zig
$ python3.12 -c "import hello; print(hello.__doc__)"                                                        
__doc__

触发段错误的代码

将module改为const并使用@constCast转换:

const c = @cImport({
    @cInclude("Python.h");
});

const module = c.PyModuleDef{
    .m_name = "__name__",
    .m_doc = "__doc__",
    .m_size = -1,
};

export fn PyInit_hello() [*c]c.PyObject {
    return c.PyModule_Create(@constCast(&module));
}

编译运行结果:

$ zig build-lib -dynamic -femit-bin=hello.so -I/usr/include/python3.12 -L/usr/lib -lpython3.12 -lc hello.zig
$ python3.12 -c "import hello"
[1]    791627 segmentation fault (core dumped)

段错误原因

你的猜测完全正确:Zig会将全局const变量存入程序的只读内存段(比如Linux上的.rodata段)。而Python的PyModule_Create函数并非只读使用传入的PyModuleDef结构体——它内部会修改结构体中的一些字段(比如用于模块内部状态的相关成员)。当C代码试图向只读内存区域写入数据时,操作系统会触发段错误(SIGSEGV)来阻止非法写操作,这就是崩溃的直接原因。

无@constCast的内联实现可行性

你给出的内联写法存在严重问题:

const c = @cImport({
    @cInclude("Python.h");
});

export fn PyInit_hello() [*c]c.PyObject {
    return c.PyModule_Create(@constCast(&c.PyModuleDef{
        .m_name = "__name__",
        .m_doc = "__doc__",
        .m_size = -1,
    }));
}

这里的c.PyModuleDef结构体是在函数栈帧上创建的临时对象,函数执行完毕后栈帧会被销毁,对应的内存会被释放或覆盖。但PyModule_Create会保留指向该结构体的指针并在后续Python运行时中使用,这会导致悬空指针,引发未定义行为(可能是段错误,也可能是随机内存污染)。

正确的无@constCast实现方式是在函数内部定义一个var类型的结构体实例,确保它的内存是可写的:

const c = @cImport({
    @cInclude("Python.h");
});

export fn PyInit_hello() [*c]c.PyObject {
    var module_def = c.PyModuleDef{
        .m_name = "__name__",
        .m_doc = "__doc__",
        .m_size = -1,
    };
    return c.PyModule_Create(&module_def);
}

如果Python运行时会长期持有这个结构体的指针,函数内的栈变量可能不够安全——此时更稳妥的方式是使用全局var(就像你最初的正常代码),或者在堆上分配结构体(用Zig的内存分配器或者C的malloc)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 00:07:15