• Python如何向C指针传递数据


    在之前的文章Python调用C/C++动态链接库中,举例说明了Python访问C指针所指向数据的方式,主要是从指针所指向的内存中取数据。今天遇到的问题是,如何将Python产生的数组赋值给C指针。

    下面用一个小例子来说明一下。

    C从外部获取指针所指向的数据,并通过print_array()函数打印出来,C代码:

    1. #include "stdio.h"
    2. #include
    3. extern "C"
    4. {
    5. using namespace std;
    6. // 创建一个长度为len的int型指针
    7. int *get_pointer(int len)
    8. {
    9. int *ptr = new int[len];
    10. return ptr;
    11. }
    12. // 打印指针数据,len为打印的int型数据的长度
    13. void print_array(int *array_in, int len)
    14. {
    15. for(int i = 0; i < len; i++)
    16. {
    17. printf("array_in[%d] = %d \n", i, array_in[i]);
    18. }
    19. }
    20. // 释放指针
    21. void free_pointer(int *ptr)
    22. {
    23. if(ptr)
    24. {
    25. delete [] ptr;
    26. }
    27. }
    28. }

    编译C动态库:

    g++ -std=c++11 python2c.cpp -o libpy2c.so -shared -fPIC
    

    注意,不要加-c选项,否则编译出来的动态库加载时会报错:

    only ET_DYN and ET_EXEC can be loaded

    C动态库有了,接下来通过Python产生数据,并传递给C指针。传递数据的方式目前试用了两种,都能work。

    1. 方法1,逐元素拷贝

    1. # -*- coding: utf-8 -*-
    2. from ctypes import *
    3. import numpy as np
    4. lib_path = r'./libpy2c.so'
    5. solib = cdll.LoadLibrary(lib_path)
    6. # Create C pointer
    7. c_len = 10
    8. solib.get_pointer.argtypes = [c_int]
    9. solib.get_pointer.restype = POINTER(c_int)
    10. ptr = solib.get_pointer(c_len)
    11. print("ptr = {}".format(ptr))
    12. # Create a python array
    13. py_len = 10
    14. py_array = [i for i in range(py_len)]
    15. py_array = np.array(py_array).astype(np.int32)
    16. for i in range(py_len):
    17. print(py_array[i])
    18. # Method 1
    19. # 方法1,逐个元素拷贝,将Python数组逐个拷贝到ctypes pointer所指向的内存中。
    20. for i in range(py_len):
    21. ptr[i] = py_array[i]
    22. '''
    23. # Method 2
    24. # 方法2,通过cast方法将Python数组数据转换成指针形式。
    25. py_array = np.ascontiguousarray(py_array, dtype=np.int32)
    26. ptr = cast(py_array.ctypes.data, POINTER(c_int))
    27. print("ptr = {}".format(ptr))
    28. '''
    29. # Call C shared library to print pointer
    30. solib.print_array.argstype = [POINTER(c_int), c_int]
    31. solib.print_array.restype = c_void_p
    32. solib.print_array(ptr, py_len)
    33. solib.free_pointer.argstype = [POINTER(c_int)]
    34. solib.free_pointer.restype = c_void_p
    35. solib.free_pointer(ptr)

    执行结果:

    从以上结果中可以看到,数据成功地从Python数组传递给了C指针。

    2. 方法2,通过cast方式将数组转换成ctypes指针

    将Python代码中的方法1关闭,方法2打开:

    执行结果与第一种方法一致,都能成功将Python的数组数据传递给C语言的指针。 

    3. 指针越界测试

    如果在Python中创建的数组大小大于C语言为指针分配的内存大小,情况如何呢?在Python代码中,我们保持c_len=10不变,将py_len改为20,测试两种方法。

    为了截图方便,我们把Python中的数组打印关闭。 

    方法1结果:

    虽然完成了数据传递,但是出现了内存越界。

    我们再来看方法2:

     

    完成了数据传递,而且一切正常,从图上cast前后ptr值的变化来看,指针变量ptr实际上是指向了一个新的地址,也就是通过cast转换而得到的数据的地址。因此,这时不会再向原指针地址写数据,也就不会受原指针内存空间的限制。

    但以上代码这样写是有问题的,因为通过C开辟出来的空间是无法被释放的,因为最后传入free_pointer()的地址变化了。所以如果使用cast方式,由于是通过Python来创建指针,因此C库中的get_pointer()函数就无需调用了。

  • 相关阅读:
    Windows/Ubuntu安装frida和objection
    浅谈青岛啤酒厂事件—论智能视频监控的重要性和必要性
    浅谈! 几种 SpringBoot/SpringCloud 开源项目
    STM32 GPIO
    2.11 流程控制之for循环
    Mirror 镜像站点的使用
    MLIR笔记(2)
    vue-quill-editor富文本编辑器使用方法,最全,含部分源码解读,含图片上传,如果页面有多个富文本,图片上传解决方案
    redis实现分布式锁的原理
    众多OA办公协同系统,企业应如何选择?
  • 原文地址:https://blog.csdn.net/DeliaPu/article/details/126859941