【问题标题】:Need to pass pointer-to-pointer to a C shared lib function from Python需要从 Python 将指针传递给 C 共享库函数
【发布时间】:2018-07-04 17:02:11
【问题描述】:

我有一个小的 Python 程序需要从我的 C 共享库中调用一个函数:

C程序优先:

#include <stdio.h>
#include <stdlib.h>

void myprint(const char*, char**);

void myprint(const char* input, char** output)
{
printf("hello world\n");
printf("input string: %s\n",input);
*output = (char*) malloc(20);
sprintf(*output,"Life is cheap\n");
printf("output string in C program: %s\n",*output);
}

编译成共享库:

gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC testlib.c

您会注意到该函数需要一个 char 指向指针的指针作为它的第二个参数。它将填充这个参数,我希望调用它的 python 程序能够打印它。

我试图通过将引用传递给一个指针来从调用者 python 程序中实现这一点:

import ctypes
mem = POINTER( c_ubyte )()
testlib = ctypes.CDLL('/home/amanral/testlib.so')
testlib.myprint("hell with the world",byref(mem))
#print mem   ===> This is where I want to print back the value filled by the C function

我知道 print mem 是错误的,因为它只是打印:

<__main__.LP_c_ubyte object at 0x7f6460246560>

是否甚至可以打印回存储在内存中的实际字符串? 有没有更好的解决方案?

【问题讨论】:

  • 对不起,如果这是题外话,但在 C 空间中分配内存似乎有泄漏。那段内存怎么释放? C和python分配内存的方式一样吗?
  • @billjamesdev - 你是对的。到目前为止,这是泄漏的。理想情况下,我想在 python 中分配内存并将其传递给 C 库函数。这可能吗?
  • @billjamesdev - 请参阅下面的答案。现在在 Python 程序中分配了缓冲内存,并将指针传递给 C 函数。希望这看起来更好吗?

标签: python c shared-libraries ctypes


【解决方案1】:

如果您在 Python 中分配内存,您可以更直接地实现如下。注意我使用 Python 3 并显式传递字节字符串。

test.c

#include <stdio.h>
#include <stdlib.h>

#define API __declspec(dllexport)  // Windows-specific export

API void myprint(const char* input, char* output)
{
    printf("hello world\n");
    printf("input string: %s\n",input);
    sprintf(output,"Life is cheap\n");
    printf("output string in C program: %s\n",output);
}

test.py

import ctypes
testlib = ctypes.CDLL('test')
mem = ctypes.create_string_buffer(32)
testlib.myprint(b'hell with the world',mem)
print(mem.value)

输出

hello world
input string: hell with the world
output string in C program: Life is cheap

b'Life is cheap\n'

如果你仍然想让 C 分配内存,你必须提供一个函数来释放它,如果你不想泄漏:

test.c

#include <stdio.h>
#include <stdlib.h>

#define API __declspec(dllexport)  // Windows-specific export

API void myprint(const char* input, char** output)
{
    printf("hello world\n");
    printf("input string: %s\n",input);
    *output = malloc(32);
    printf("output address: %p\n",*output);
    sprintf(*output,"Life is cheap\n");
    printf("output string in C program: %s\n",*output);
}

API void myfree(char* mem)
{
    printf("freeing %p\n",mem);
    free(mem);
}

test.py

import ctypes
testlib = ctypes.CDLL('test')

# allocate a pointer to hold the result
mem = ctypes.c_char_p()

# Pass it by reference to be modified
testlib.myprint(b'hell with the world',ctypes.byref(mem))
print(mem.value)
testlib.myfree(mem)

输出

hello world
input string: hell with the world
output address: 0000028CEE9BAE50
output string in C program: Life is cheap

b'Life is cheap\n'
freeing 0000028CEE9BAE50

【讨论】:

    【解决方案2】:

    通过在 python 程序中进行以下更改来解决问题:

    import ctypes
    from ctypes import *
    
    plainText_pswd = "hellWithTheWorld"
    encrypted_pswd = create_string_buffer(32)
    
    testlib = ctypes.CDLL('/home/amanral/testlib.so')
    testlib.myprint(plainText_pswd, pointer(pointer(encrypted_pswd)))
    print "Recvd encrypted password in python program:" + encrypted_pswd.value
    

    我的 C 库函数所需的 char 指针指向指针是通过使用实现的 pointer(pointer(encrypted_pswd)) 作为 C 函数的参数。我不确定这是否是正确的使用方式,但它可以满足我的要求。我可以将 C 函数返回的值打印为 encrypted_pswd.value

    仍然欢迎任何 cmets/建议。

    【讨论】:

    • 是的,C程序被修改为注释掉那里的malloc
    • 如果在Python中分配内存,使用char* output就足够了。然后使用encrypted_pswd 传递输出缓冲区。它已经是一个数组,它的地址将被传递给函数。
    • 我的 C 库是我无法修改的第三方代码,它希望输出是指向指针的字符串指针。它还期望调用程序分配内存。因此在python程序中使用了pointer(pointer(encrypted_pswd))
    • 第三方库做了不必要的工作,让 API 变得更加复杂。正如我所展示的,如果缓冲区是用户分配的,则不需要 char**。您提到注释掉暗示您可以更改代码的 malloc。
    猜你喜欢
    • 1970-01-01
    • 2015-10-12
    • 2021-03-10
    • 2011-05-22
    • 2014-02-20
    • 1970-01-01
    • 2012-07-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多