【问题标题】:How can I use ctypes to pass a byteArray into a C function that takes a char* as its argument?如何使用 ctypes 将 byteArray 传递给以 char* 作为参数的 C 函数?
【发布时间】:2016-09-22 04:53:04
【问题描述】:

我在 C 中创建了一个函数,它接受一个 int 大小和一个 char *buffer 作为参数。我想使用 ctypes 从 python 调用这个函数并传入一个 python byteArray。我知道首先必须将 C 文件编译成共享库(.so 文件)并使用 ctypes 调用该函数。这是我到目前为止的代码。

加密.c:

#include <stdio.h>
void encrypt(int size, unsigned char *buffer);
void decrypt(int size, unsigned char *buffer);

void encrypt(int size, unsigned char *buffer){
    for(int i=0; i<size; i++){
        unsigned char c = buffer[i];
        printf("%c",c);
    }
}
void decrypt(int size, unsigned char *buffer){
    for(int i=0; i<size; i++){
        unsigned char c = buffer[i];
        printf("%c",c);
    }
}

这是python文件:

import ctypes

encryptPy = ctypes.CDLL('/home/aradhak/Documents/libencrypt.so')
hello = "hello"
byteHello = bytearray(hello)
encryptPy.encrypt(5,byteHello)
encryptPy.decrypt(5,byteHello)

基本上我想从python调用C方法,传递一个python字节数组,让它遍历数组并打印每个元素

【问题讨论】:

    标签: python c shared-libraries ctypes


    【解决方案1】:

    您需要的最低要求(Python 2)是:

    hello = "hello"
    encryptPy.encrypt(5,hello)
    encryptPy.decrypt(5,hello)
    

    但最好也声明参数类型和返回值。完整程序:

    #!python2
    import ctypes
    
    encryptPy = ctypes.CDLL('/home/aradhak/Documents/libencrypt.so')
    
    encryptPy.encrypt.argtypes = (ctypes.c_int,ctypes.c_char_p)
    encryptPy.encrypt.restype = None
    encryptPy.decrypt.argtypes = (ctypes.c_int,ctypes.c_char_p)
    encryptPy.decrypt.restype = None
    
    hello = "hello"
    encryptPy.encrypt(len(hello),hello)
    encryptPy.decrypt(len(hello),hello)
    

    请注意,在传递 python 字节字符串时,请考虑缓冲区不可变。在这种情况下,您只是在读取缓冲区,但如果您需要允许 C 函数更改字符串,请使用:

    hello = ctypes.create_string_buffer('hello',5)
    

    以下也可以,但长度为 6。将包含终止的 null。

    hello = ctypes.create_string_buffer('hello')
    

    【讨论】:

    • 这很有帮助,谢谢。对于将来的python-ignorant,如果您的C函数只接受一个参数,请将argtype指定为[ctypes.some_type]而不是(ctypes.some_type)。后者显然被解释为单个项目,而不是包含单个项目的列表。
    • @orionelenzil 用逗号尝试(ctypes.some_type,)
    • @osvein 括号不是必需的,除非为了明确操作顺序。 .argtypes = c_int, 有效。逗号使它成为一个元组。
    【解决方案2】:

    Mark 的回答非常有帮助,因为它将字符数组传递给 C 函数,这是 OP 真正想要的,但如果有人在这里找到真正想要传递字节数组的方式,an approach seems to be to build a ctypes.c_char backed by the memory of your byte-array, and pass that .

    我在这里的示例忽略了 Mark 推荐的参数声明,这确实是个好主意。

    import ctypes
    
    # libFoo.c:
    # (don't forget to use extern "C" if this is a .cpp file)
    #
    # void foo(unsigned char* buf, size_t bufSize) {
    #   for (size_t n = 0; n < bufSize; ++n) {
    #     buf[n] = n;
    #   }
    # }
    
    fooLib = ctypes.cdll.LoadLibrary('./lib/libFoo.dylib')
    
    ba = bytearray(10)
    
    char_array = ctypes.c_char * len(ba)
    
    fooLib.foo(char_array.from_buffer(ba), len(ba))
    
    for b in ba:
      print b
    
    # 0
    # 1
    # 2
    # 3
    # 4
    # 5
    # 6
    # 7
    # 8
    # 9
    

    【讨论】:

    • 不幸的是,该链接指向不同的主题。但是无论如何感谢您的代码 - 它似乎可以工作(尽管我不确定最初分配 char_array 时是否没有分配内存。
    • TLDR:将您的字节数组转换为带有(ctypes.c_char*len(ba)).from_buffer(bytearray(ba)) 的字符数组,其中ba 是您的字节数组。
    • TLDR:在 Python 3 中,只需使用bytes(ba)(用于不可变缓冲区)或create_string_buffer(bytes(ba))(用于可变缓冲区)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 2016-02-22
    • 2016-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    相关资源
    最近更新 更多