版权声明:本文为博主原创文章,转载请注明原文链接。 https://blog.csdn.net/Frank_Abagnale/article/details/82901276
python 调用C语言的方式主要有以下3种:
1.通过python的ctypes模块,调用预编好的C语言动态链接库中的C语言函数。(适合测试)
2.python执行过程中调用(执行)C语言可执行程序。
3.利用模板包装C语言函数,生成Python扩展模块,被python直接调用(正式用法,但比较复杂)
这里先介绍第一种ctypes的用法:
# apt install python-ctypeslib
import ctypes
## @1 基础
'''
功能:实现字符串的反转;
本次调用的C语言动态链接库为:libreversal.so
源码:
char * reverse(char *s)
{
// printf("len of s: %lu\n",strlen(s));
// printf("sizeof s: %lu\n",sizeof(s));
/*功能*/
register char t, /* tmp */ /*register 请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。*/
*p = s, /* 字符串头地址 */
*q = (s + (strlen(s) - 1)); /* 字符串尾地址 */
while (p < q) /* if p < q */
{
t = *p; /* swap & move ptrs */
*p++ = *q; // *p = *q ; p++
*q-- = t; // *q = t ; q++
}
/*关于函数参数的含义*/
// printf("s: %p\n",s);
// printf("&s: %p\n",&s);
return(s) ;
}
编译:gcc -o ./lib/libreversal.so -shared -fPIC ./src/reversal.c
'''
def call_libreversal():
print "========== @1 In function call_libreversal =========="
## 使用ctypes 模块加载动态链接库
loadlib = ctypes.cdll.LoadLibrary
pycall = loadlib("./lib/libreversal.so")
str1 = "123456789"
pstr2 = ctypes.c_char_p(None)
print "src str1:",str1," src pstr2:",pstr2
#-------------------------------------------------------
# 这里注意两点:
# 1>
# argtypes s--->(,)或[]
# restype 单数---> 不要序列化
# 2>
# 默认返回都是int型,如果不是,则需要提前声明返回值类型
#-------------------------------------------------------
pycall.reverse.argtypes = (ctypes.c_char_p,)
pycall.reverse.restype = ctypes.c_char_p
pstr2 = pycall.reverse(str1)
print "rev str1:",str1," return pstr2",pstr2
return
## @2 C语言函数入参出参为结构体时python的调用方式;
'''
功能:传入结构体student,打印student的信息,返回结构体中新增了平均成绩
本次调用的C语言动态链接库为:libstructPar.so
源码:
#include <stdio.h>
#include <string.h>
struct student {
int id;
int age;
char name[16];
float score[5];
float avg;
};
struct student get_avg_score(struct student people){
printf("**** In C function ****\n");
printf("id : %d\n",people.id);
printf("name : %s\n",people.name);
printf("age : %d\n",people.age);
float sum = 0.0;
for (int i=0;i<5;i++){
sum += people.score[i] ;
printf("score %d is %f\n",i,people.score[i]);
}
people.avg = sum / 5;
printf("**** Out of C function ****\n");
return people;
}
编译:gcc -o ./lib/libstructPar.so -shared -fPIC ./src/structPar.c
'''
## python 使用类与C/C++的结构体对接
class student_info(ctypes.Structure):
_fields_ = [("id",ctypes.c_int),
("age",ctypes.c_int),
("name",ctypes.c_char * 16),
("score",ctypes.c_float * 5),
("avg",ctypes.c_float)
]
def call_libstructPar():
print "========== @2 In function call_libstructPar =========="
loadlib = ctypes.cdll.LoadLibrary
pycall = loadlib("./lib/libstructPar.so")
py_struct = student_info()
py_struct.id = 10086
py_struct.name = "zhangsan"
py_struct.age = 20
py_struct.score = (98.5,89,80,92.5,95.5)
## 关于数组的赋值还可以使用如下方法:
# py_struct.score[0] = 90.5
# py_struct.score[1] = 91.5
# py_struct.score[2] = 92.5
# py_struct.score[3] = 93.5
# py_struct.score[4] = 94.5
pycall.get_avg_score.argtypes = (student_info,)
pycall.get_avg_score.restype = student_info
ret = pycall.get_avg_score(py_struct)
print "ret: ",ret," type:",type(ret)
print "ret.id:",ret.id
print "ret.name:",ret.name
print "ret.avg:",ret.avg
## @3 地址传递问题
# 涉及指针、地址的问题,可以参考一以下内容
# byref(x [, offset]) 返回 x 的地址,x 必须为 ctypes 类型的一个实例。相当于 c 的 &x 。 offset 表示偏移量。
# pointer(x) 创建并返回一个指向 x 的指针实例, x 是一个实例对象。
# POINTER(type) 返回一个类型,这个类型是指向 type 类型的指针类型, type 是 ctypes 的一个类型。
'''
功能:将传入的两int数交换
本次调用的C语言动态链接库为:libaddrtransmit.so
源码:
void change_value(int * x,int * y){
int t;
t = *x;
*x = *y;
*y = t;
return;
}
编译:gcc -o ./lib/libaddrtransmit.so -shared -fPIC ./src/addrtransmit.c
'''
def call_libaddrtransmit():
print "========== @3 In function call_libaddrtransmit =========="
loadlib = ctypes.cdll.LoadLibrary
pycall = loadlib("./lib/libaddrtransmit.so")
num1 = ctypes.c_int(1111)
num2 = ctypes.c_int(2222)
print "befor: num1: ",num1," num2:",num2
pycall.change_value.argtypes = (ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
pycall.change_value(ctypes.byref(num1), ctypes.pointer(num2)) ## 注意这里的用法ctypes.byref(num1)和ctypes.pointer(num2)是一样的
print "after: num1: ", num1, " num2:", num2
## @4 函数参数的引用传递 (这部分是C++的语法,但是作为对址传递的一种补充,在这里记述一下。)
'''
功能:将传入的两int数交换
本次调用的C语言动态链接库为:libquoteTransmit.so
源码:
#include <iostream>
#include <stdio.h>
// 引用传递传的是实际的变量;
// 引用传递是c++对C的补充,是C++特有的,所以编译要用g++
// g++ -o ./lib/libquoteTransmit.so -shared -fPIC ./src/quoteTransmit.cpp
void cpp_quote_transmit(int &x, int &y){
int t;
t = x;
x = y;
y = t;
return;
}
extern "C" { // 至于这里为什么要转一下,参考Python调C++的方法
void quote_transmit(int &x, int &y){
cpp_quote_transmit(x,y);
return;
}
}
编译: g++ -o ./lib/libquoteTransmit.so -shared -fPIC ./src/quoteTransmit.cpp
'''
def call_quoteTransmit():
print "========== @4 In function call_quoteTransmit =========="
loadlib = ctypes.cdll.LoadLibrary
pycall = loadlib("./lib/libquoteTransmit.so")
num1 = ctypes.c_int(1111)
num2 = ctypes.c_int(2222)
print "befor: num1: ", num1, " num2:", num2
# pycall.quote_transmit.argtypes = (ctypes.c_int,ctypes.c_int)
# pycall.quote_transmit(num1, num2)
pycall.quote_transmit.argtypes = (ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
pycall.quote_transmit(ctypes.byref(num1),ctypes.byref(num2))
print "after: num1: ", num1, " num2:", num2
if __name__ == "__main__":
# @1
## 基础测试
call_libreversal()
'''
执行结果:
src str1: 123456789 src pstr2: c_char_p(None)
rev str1: 987654321 return pstr2 987654321
'''
# @2
call_libstructPar()
'''
**** In C function ****
id : 10086
name : zhangsan
age : 20
score 0 is 98.500000
score 1 is 89.000000
score 2 is 80.000000
score 3 is 92.500000
score 4 is 95.500000
**** Out of C function ****
ret: <__main__.student_info object at 0x7f059a5ddb00> type: <class '__main__.student_info'>
ret.id: 10086
ret.name: zhangsan
ret.avg: 91.0999984741
'''
# @3
call_libaddrtransmit()
'''
befor: num1: c_int(1111) num2: c_int(2222)
after: num1: c_int(2222) num2: c_int(1111)
'''
# @4
call_quoteTransmit()
'''
befor: num1: c_int(1111) num2: c_int(2222)
after: num1: c_int(2222) num2: c_int(1111)
'''