C语言程序通过Python的C API(一个头文件和一组库函数)来嵌入一个Python解释器,这使得C程序可以执行Python代码、调用Python函数、并传递和接收数据。

(图片来源网络,侵删)
下面我将从最简单到最复杂的场景,分步讲解如何实现。
准备工作
- 安装Python:确保你的系统上已经安装了Python,你需要知道Python的头文件(
.h)和库文件(.lib或.so/.dylib)的位置。 - 获取Python路径:
- 在Windows上,Python安装路径通常就是你需要包含的路径和链接的库路径。
- 在Linux/macOS上,你可以使用以下命令找到路径:
# 查找头文件路径 python3-config --includes # 查找库文件路径 python3-config --ldflags
在C中执行一段简单的Python代码
这是最基础的用法,C程序直接执行一段Python脚本字符串。
步骤:
- 包含Python头文件:
#include <Python.h> - 初始化Python解释器:
Py_Initialize() - 执行Python代码:
PyRun_SimpleString() - 关闭Python解释器:
Py_Finalize()
示例代码 (main.c)
#include <Python.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
// 1. 初始化Python解释器
Py_Initialize();
if (!Py_IsInitialized()) {
fprintf(stderr, "Error: Failed to initialize the Python interpreter.\n");
return 1;
}
// 2. 执行一段简单的Python代码
// 打印 "Hello from Python!"
PyRun_SimpleString("print('Hello from Python!')");
// 3. 执行一个稍微复杂的Python代码
PyRun_SimpleString("a = 10\n"
"b = 20\n"
"print(f'The sum of a and b is: {a + b}')");
// 4. 关闭Python解释器
Py_Finalize();
printf("C program finished.\n");
return 0;
}
如何编译和运行 (以Linux为例)
你需要链接Python的库,使用 python3-config --ldflags 可以得到所需的链接参数。
# 编译 gcc main.c -o main $(python3-config --ldflags) # 运行 ./main
预期输出:

(图片来源网络,侵删)
Hello from Python!
The sum of a and b is: 30
C program finished.
调用Python脚本中的函数并传递参数
这是更实用的场景,C程序调用一个独立的 .py 文件中的函数。
Python脚本 (my_module.py)
这个脚本定义了一个函数,可以接收参数并返回结果。
# my_module.py
def greet(name):
"""返回一个问候语"""
return f"Hello, {name}! Welcome to the world of C and Python."
def add_numbers(a, b):
"""返回两个数的和"""
return a + b
C语言程序 (call_func.c)
C程序需要:
- 导入Python模块 (
my_module)。 - 将C数据类型转换为Python对象 (
PyUnicode_FromString,PyLong_FromLong)。 - 调用Python函数 (
PyObject_CallObject)。 - 将Python返回结果转换回C数据类型 (
PyUnicode_AsUTF8,PyLong_AsLong)。 - 记得释放所有创建的Python对象 (
Py_DECREF),以避免内存泄漏。
#include <Python.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
Py_Initialize();
// 1. 导入Python模块
PyObject *pName = PyUnicode_DecodeFSDefault("my_module"); // 模块名
PyObject *pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (!pModule) {
PyErr_Print();
fprintf(stderr, "Error importing module my_module\n");
return 1;
}
// --- 调用 greet("Alice") 函数 ---
// 2. 获取模块中的函数
PyObject *pGreetFunc = PyObject_GetAttrString(pModule, "greet");
if (!pGreetFunc || !PyCallable_Check(pGreetFunc)) {
if (PyErr_Occurred()) PyErr_Print();
fprintf(stderr, "Cannot find function greet\n");
Py_DECREF(pModule);
return 1;
}
// 3. 准备参数 (必须是元组)
PyObject *pArgs = PyTuple_New(1);
PyObject *pValue = PyUnicode_FromString("Alice");
PyTuple_SetItem(pArgs, 0, pValue); // 注意: SetItem "steals" a reference, so no DECREF needed here
// 4. 调用函数
PyObject *pGreetResult = PyObject_CallObject(pGreetFunc, pArgs);
Py_DECREF(pArgs);
Py_DECREF(pGreetFunc);
if (pGreetResult != NULL) {
// 5. 处理返回结果
const char *result_str = PyUnicode_AsUTF8(pGreetResult);
printf("C received from Python: %s\n", result_str);
Py_DECREF(pGreetResult);
} else {
PyErr_Print();
fprintf(stderr, "Call failed\n");
}
// --- 调用 add_numbers(5, 7) 函数 ---
PyObject *pAddFunc = PyObject_GetAttrString(pModule, "add_numbers");
if (!pAddFunc || !PyCallable_Check(pAddFunc)) {
if (PyErr_Occurred()) PyErr_Print();
fprintf(stderr, "Cannot find function add_numbers\n");
Py_DECREF(pModule);
return 1;
}
pArgs = PyTuple_New(2);
PyTuple_SetItem(pArgs, 0, PyLong_FromLong(5));
PyTuple_SetItem(pArgs, 1, PyLong_FromLong(7));
PyObject *pAddResult = PyObject_CallObject(pAddFunc, pArgs);
Py_DECREF(pArgs);
Py_DECREF(pAddFunc);
if (pAddResult != NULL) {
long result_long = PyLong_AsLong(pAddResult);
printf("C received from Python: %ld\n", result_long);
Py_DECREF(pAddResult);
} else {
PyErr_Print();
fprintf(stderr, "Call failed\n");
}
Py_DECREF(pModule);
Py_Finalize();
return 0;
}
如何编译和运行
确保 my_module.py 和 call_func.c 在同一目录下。

(图片来源网络,侵删)
# 编译 gcc call_func.c -o call_func $(python3-config --ldflags) # 运行 ./call_func
预期输出:
C received from Python: Hello, Alice! Welcome to the world of C and Python.
C received from Python: 12
处理更复杂的数据类型(如列表、字典)
Python和C之间的数据转换是关键。Python.h 提供了丰富的宏和函数来处理基本类型和容器类型。
示例:传递列表并获取字典
Python脚本 (data_module.py)
# data_module.py
def process_data(data_list):
"""接收一个列表,返回一个包含统计信息的字典"""
return {
"sum": sum(data_list),
"max": max(data_list),
"min": min(data_list),
"length": len(data_list)
}
C语言程序 (handle_data.c)
#include <Python.h>
#include <stdio.h>
int main() {
Py_Initialize();
PyObject *pModule = PyImport_ImportModule("data_module");
if (!pModule) {
PyErr_Print();
return 1;
}
PyObject *pFunc = PyObject_GetAttrString(pModule, "process_data");
if (!pFunc || !PyCallable_Check(pFunc)) {
PyErr_Print();
Py_DECREF(pModule);
return 1;
}
// 1. 创建一个Python列表 [10, 20, 30, 40]
PyObject *pList = PyList_New(4);
PyList_SetItem(pList, 0, PyLong_FromLong(10));
PyList_SetItem(pList, 1, PyLong_FromLong(20));
PyList_SetItem(pList, 2, PyLong_FromLong(30));
PyList_SetItem(pList, 3, PyLong_FromLong(40));
// 2. 准备参数
PyObject *pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, pList); // PyTuple_SetItem steals the reference
// 3. 调用函数
PyObject *pResult = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
Py_DECREF(pFunc);
if (pResult && PyDict_Check(pResult)) {
// 4. 从返回的字典中获取值
PyObject *pSum = PyDict_GetItemString(pResult, "sum");
PyObject *pMax = PyDict_GetItemString(pResult, "max");
if (pSum && pMax) {
long sum_val = PyLong_AsLong(pSum);
long max_val = PyLong_AsLong(pMax);
printf("Sum: %ld, Max: %ld\n", sum_val, max_val);
}
Py_DECREF(pResult);
} else {
PyErr_Print();
fprintf(stderr, "Failed to get dictionary result.\n");
}
Py_DECREF(pModule);
Py_Finalize();
return 0;
}
编译与运行
gcc handle_data.c -o handle_data $(python3-config --ldflags) ./handle_data
预期输出:
Sum: 100, Max: 40
重要注意事项
- 内存管理:这是使用Python C API最容易出错的地方。*每一个通过
Py_INCREF创建或返回的Python对象(`PyObject),在不使用时都必须通过Py_DECREF释放**,忘记释放会导致内存泄漏,像PyTuple_SetItem和PyList_SetItem这样的函数会“窃取”引用,所以你不需要再对传入的对象调用Py_DECREF`。 - GIL (全局解释器锁):Python的GIL保证了同一时间只有一个线程能执行Python字节码,这意味着,即使在多线程C程序中调用Python,Python代码本身也是串行执行的,如果你的C程序是多线程的,并且需要频繁调用Python,这可能会成为性能瓶颈。
- 错误处理:Python函数调用可能会抛出异常,在C中,你需要检查返回的
PyObject*是否为NULL,如果是,则用PyErr_Print()打印Python的异常堆栈信息。 - 跨平台:Windows和Linux/macOS在编译链接时略有不同,Windows通常需要明确指定Python的
include和lib目录,以及python3x.lib库文件,Linux/macOS使用python3-config工具是最简单的方式。
替代方案:subprocess
如果你的需求只是“执行”一个Python脚本,并且不需要和它进行复杂的交互(比如频繁地交换数据),那么使用C语言的 system() 或 popen() 函数(在Linux/macOS上)或 CreateProcess(在Windows上)来启动一个独立的Python进程会更简单。
示例 (subprocess_example.c)
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("C program is about to call a Python script.\n");
// 使用 system() 调用
// 注意:在Windows上,命令可能是 "python my_script.py"
int status = system("python3 my_module.py greet Bob");
if (status == -1) {
perror("system() call failed");
} else {
printf("Python script finished with status: %d\n", status);
}
return 0;
}
Python脚本 (my_module.py) - 需要增加命令行参数处理
import sys
def greet(name):
print(f"Hello, {name}! I'm a Python script called from C.")
if __name__ == "__main__":
if len(sys.argv) > 1:
greet(sys.argv[1])
else:
print("Usage: python3 my_module.py <name>")
编译与运行
gcc subprocess_example.c -o subprocess_example ./subprocess_example
预期输出
C program is about to call a Python script.
Hello, Bob! I'm a Python script called from C.
Python script finished with status: 0
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Python C API | 性能高,内存共享,可以深度交互 | 复杂,手动内存管理,有GIL限制,跨平台编译麻烦 | 需要高性能、频繁、复杂的数据交换,将Python作为库嵌入到C程序中。 |
subprocess |
简单,无需了解Python C API,天然隔离 | 性能低,进程创建开销大,数据交换通过文本/文件,交互不便 | 简单的一次性脚本执行,或者将Python程序作为独立的子进程来运行。 |
对于大多数需要集成的场景,Python C API 是更强大的选择,但你需要仔细处理其复杂性,对于简单的调用,subprocess 是一个快速且可靠的备选方案。
