Tenho uma biblioteca my_lib
com uma função C que pega um char***
parâmetro, um ponteiro para um array que char*
é alocado pela função. Aqui está um exemplo mínimo reproduzível de tal função:
void getArrayOfStrings(char*** paramPtr)
{
(*paramPtr) = (char**) malloc(3*sizeof(char*));
(*paramPtr)[0] = (char*) malloc(strlen("Foo")+1);
strcpy((*paramPtr)[0], "Foo");
(*paramPtr)[1] = (char*) malloc(strlen("Bar")+1);
strcpy((*paramPtr)[1], "Bar");
(*paramPtr)[2] = 0;
}
Ele define o último elemento do array como 0, para que o chamador possa identificá-lo (em vez de fornecer o tamanho como um segundo parâmetro). Note que uma função separada é fornecida para liberar a memória.
Eu corro ctypesgen
para gerar uma ligação Python para esta função. Ela gera este código:
getArrayOfStrings = _lib.get("getArrayOfStrings", "cdecl")
getArrayOfStrings.argtypes = [POINTER(POINTER(POINTER(c_char)))]
getArrayOfStrings.restype = None
Essa ligação gerada pode ser chamada a partir do script Python abaixo:
import my_lib
import ctypes
names = ctypes.POINTER(ctypes.POINTER(ctypes.c_char))()
my_lib.getArrayOfStrings(names)
if names:
for name in names:
name_str = my_lib.String(name)
if name_str:
print("Got name: " + str(name_str))
else:
break
Funciona perfeitamente e imprime "Foo\nBar\n"
Estou apenas me perguntando por que usar ctypes.POINTER(ctypes.POINTER(ctypes.c_char))
, que eu entendo como sendo um "ponto para ponteiro para char", então um char**
, funciona. Por que eu não deveria usar um ctypes.POINTER(ctypes.POINTER(ctypes.POINTER(ctypes.c_char)))
?
Testei com ctypes.POINTER(ctypes.POINTER(ctypes.POINTER(ctypes.c_char)))
, o mesmo código produz o erro:
my_lib.getArrayOfStrings(names)
OSError: exception: access violation writing 0x0000000000000000
Porque
ctypes
sabe que a função levachar***
devido a.argtypes
ser definida, passando umchar**
implicactypes.byref
(pega o endereço de), que passanames
comochar***
. Aqui está um exemplo funcional com um explícitobyref
e usando seu código C como uma DLL:Saída:
Quando
names = ct.POINTER(ct.POINTER(ct.POINTER(ct.c_char)))()
é usado,ctypes
não precisa adicionar o implícitobyref
e assume que você sabe o que está fazendo, mas neste caso ochar***
isnull
. Como ele está sendo usado como um parâmetro de saída, C tenta escrever nele e falha.Para sua informação,
ctypes
também hác_char_p
um manipulador especial para strings de bytes terminadas em nulo (char*
), então isso funciona sem a extração de string extra:(mesma saída)