python调用C语言方式——1.

版权声明:本文为博主原创文章,转载请注明原文链接。 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)
  '''

猜你喜欢

转载自blog.csdn.net/Frank_Abagnale/article/details/82901276