Zeiger auf Zeiger ?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
TelefonMann

Moin @ all,

gitb es in Python eine Möglichekeit, einen Zeiger auf einen Zeiger zu erzeugen ?


Danke schonmal im Vorraus !

Gruß,
M
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

In Python gibt es keine Zeiger (Pointer).

Was willst du denn machen?

Gruss
TelefonMann

Also, ich möchte von Python aus auf eine C Funktion zugreifen, die in einer DLL gespeichert ist.

Dank des "cType" Moduls und einigen Threads hier im Forum funktionieren die meisten anderen Funktionen aus der DLL auch ganz hervorragend, aber die Aktuelle Fkt will halt einen Zeiger auf einen Zeiger von mir als Argument übergeben bekommen:

Code: Alles auswählen

 STATUS Report_i(unsigned short id_us, unsigned char number_puc, char** entry_ppc) 
Die Übergabe mittels ctypes und

Code: Alles auswählen

pointer (c_ubyte(number_puc))
klappt auch bei anderen Funkionen, nur wie ich den Zeiger auf den Zeiger auf den Charakter realisieren soll bin ich überfordert :-(

Ich habe schon probiert, der DLL einen Zeiger auf ein Array mit chars zu übergeben, aber dann erhalte ich eine "Access Violation":

Code: Alles auswählen

from ctypes import *
from array import array

lib = cdll.LoadLibrary("d:\meine.dll")
var1 = c_ubyte()
var2 = array('c',' '*256)
lib.Report_i(c_ubyte(10),pointer(var1),var2.buffer_info()[0])
Soweit ich weiß, erwartet die DLL ein zweidimensionales Array, auf das sie mittels des "char**" zugreifen kann.

Gibt es für mich nicht irgendeine Möglichkeit, von Python aus eine solche Struktur an C zu übergeben ?

Gruß,
M[/code]
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Schau dir mal das Struct-Modul an, damit solltest du dein Array in ein C-freundliches Format bekommen.
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

Ich glaube das Array Modul passt hier besser. Du musst dann wohl einen Array von chars erzeugen...
BlackJack

ProgChild hat geschrieben:Ich glaube das Array Modul passt hier besser. Du musst dann wohl einen Array von chars erzeugen...
Das wird nicht helfen. Was er braucht ist ein Array mit Zeigern wo jeder Zeiger wiederum auf ein Array mit Zeichen verweist.
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

BlackJack hat geschrieben:
ProgChild hat geschrieben:Ich glaube das Array Modul passt hier besser. Du musst dann wohl einen Array von chars erzeugen...
Das wird nicht helfen. Was er braucht ist ein Array mit Zeigern wo jeder Zeiger wiederum auf ein Array mit Zeichen verweist.
Warum wird das nicht helfen. Ich dachte an so was...

Code: Alles auswählen

>>> import array
>>> import struct
>>> 
>>> a = array.array('c', 'Text')
>>> (address,t) = a.buffer_info()
>>> address
134906760
>>> struct.pack('P',address)
'\x88\x83\n\x08'
Ich hab aber keine Ahnung, ob das klappt...
TelefonMann

Hallo !

Vielen Dank für die Hilfe !
Ich hab mal n bisschen rumprobiert und es nun auf folgende Art und Weise gelöst:

Code: Alles auswählen

from array import array
from ctypes import *

libc = cdll.LoadLibrary('d:\meine.dll')
Var1 = c_ubyte()
Var2 = array('c',' '*256)
Var2_zeig = c_ulong(Var2.buffer_info()[0])
libc.Report_i(c_ushort(10),pointer(Var1),pointer(Var2_zeig)) 

return [int(Var1.value),Var2.tostring()]
Funktioniert auch, nur weiß ich nicht, ob das eine so "saubere" Lösung ist...

Vielen Dank auf jeden Fall nochmal!

Gruß,
M
BlackJack

Ich denke das hier ist "sauberer":

Code: Alles auswählen

from ctypes import cdll, c_char_p, c_uint, ARRAY, POINTER

dll = cdll.LoadLibrary('./char_array.so')

test = dll.test
test.argtypes = (c_uint, POINTER(c_char_p))
test.restype = None

def c_string_array(strings):
    char_p_array = ARRAY(c_char_p, len(strings))()
    for i, string in enumerate(strings):
        char_p_array[i] = string
    return char_p_array

data = ('spam', 'eggs', 'vikings')
test(len(data), c_string_array(data))
Und das C Programm zum Testen:

Code: Alles auswählen

/* gcc -o char_array.so -shared char_array.c */
#include <stdio.h>

void test(unsigned int count, const char **strings)
{
    int i;
    for (i = 0; i < count; ++i) {
        puts(strings[i]);
    }
}
TelefonMann

Moin,

nach ner kleinen Pause hab ichs mal getestet :-)

Code: Alles auswählen

from ctypes import *

dll = cdll.LoadLibrary("d:\meine.dll")

test = dll.Report_i
test .argtypes = (POINTER(c_ubyte),POINTER(c_char_p))
number = pointer(c_ubyte())
pointerarray = ARRAY(c_char_p,64)() # das zu erwartende Array ist max. 64 Zeichen lang
test(number,pointerarray)
Diese Lösung ist natürlich sehr elegant, funktioniert aber leider nicht.
Nach der letzten Zeile (Ausführen von Test) bekomme ich eine "Access Violation" von Windows zurück...
Kann das daran liegen, daß die C - Bibliothek das Array nicht einliest, sondern versucht, darauf zu schreiben ?

Gruß,
m
Daveron

Arrays in c sind konstante Startadressen. Desweiteren lassen sich in c Arrays nicht übergeben, man hat lediglich die Möglichkeit einen Pointer auf die Startadresse des Arrays oder die Adresse selbst zu übergeben. Demzufolge arbeitet man immer mit dem Original. Wenn die Funktion also irgendwo etwas in das Array schreibt kann es schon sein, dass du auf Speicherbereiche zugreifen willst, für die du keine Berechtigung hast.
BlackJack

TelefonMann hat geschrieben:

Code: Alles auswählen

from ctypes import *

dll = cdll.LoadLibrary("d:\meine.dll")

test = dll.Report_i
test .argtypes = (POINTER(c_ubyte),POINTER(c_char_p))
number = pointer(c_ubyte())
pointerarray = ARRAY(c_char_p,64)() # das zu erwartende Array ist max. 64 Zeichen lang
test(number,pointerarray)
Frage Nummer 1: Ist der erste Parameter wirklich ein Pointer auf ein Byte?

Frage Nummer 2: Der Kommentar ist verwirrend: Das ist kein Array von Zeichen sondern ein Array von Zeigern auf Zeichen. Also bekommst Du dort maximal 64 Pointer zurück!?
TelefonMann

Moin,

ich glaube ich weiß mittlerweile wo der Fehler liegt, aber zunächst erstmal ein paar Antworten :-)

1.) Ja, definitiv. Der Header der C-Funktion sieht so aus:

Code: Alles auswählen

 
int Report_i(unsigned short id_us,unsigned char* number_puc, char** entry_ppc) 
(war oben falsch, sri!)
Die Funktion schreibt mir in das Array eine Anzahl Bytes und übergibt mir in id_us, wieviele Zeichen sie ins Array geschrieben hat.

2.) Richtig, natürlich. Ich bekomme maximal 64 Pointer zurück.

Der Fehler, warum ich eine Access Violation bekomme liegt glaube ich darin, daß ich das array erst "initialisieren" muss, da sonst die C Funktion versucht auf einen Pointer zuzugreifen, der nach "None" zeigt, oder ?

Gruß,
M
Gast

TelefonMann hat geschrieben: Die Funktion schreibt mir in das Array eine Anzahl Bytes und übergibt mir in id_us, wieviele Zeichen sie ins Array geschrieben hat.
Blödsinn, erst denken, dann tippen:
natürlich schreibt mir die Funktion die Anzahl der Bytes in "number_puc"
BlackJack

Ich habe jetzt mal den umgekehrten Weg von meinem ersten Testprogramm probiert:

Code: Alles auswählen

from ctypes import cdll, c_char_p, c_uint, ARRAY, POINTER, c_byte, byref

dll = cdll.LoadLibrary('./char_array.so')

test = dll.test
test.argtypes = (c_uint, POINTER(c_char_p))
test.restype = None

test2 = dll.test2
test2.argtypes = (POINTER(c_byte), POINTER(c_char_p))
test2.restype = None

def c_string_array(strings):
    char_p_array = ARRAY(c_char_p, len(strings))()
    for i, string in enumerate(strings):
        char_p_array[i] = string
    return char_p_array

data = ('spam', 'eggs', 'vikings')
test(len(data), c_string_array(data))

result = ARRAY(c_char_p, 10)()  # Result is max. 10 pointers.
result_count = c_byte()
test2(byref(result_count), result)
for i in xrange(result_count.value):
    print result[i]
Und die dazugehörige C-Bibliothek:

Code: Alles auswählen

/* gcc -o char_array.so -shared char_array.c */
#include <stdio.h>

static char *foo[3] = { "foo", "bar", "baz" };

void test(unsigned int count, const char **strings)
{
    int i;
    for (i = 0; i < count; ++i) {
        puts(strings[i]);
    }
}

void test2(unsigned char *count, char **strings)
{
    int i;
    for (i = 0; i < 3; ++i) {
        strings[i] = foo[i];
    }
    *count = i;
}
Das funktioniert so ganz gut.

Das Array initialisierst Du bei `ctypes` schon in gewisser Weise. `None` ist der Wert den `ctypes` als NULL-Pointer verwendet, d.h. die C-Bibliothek sieht dort NULL-Pointer. Wenn sie auf so einen zugreifen möchte, dann kracht es natürlich, aber ich dachte sie schreibt die Zeiger in das Array hinein und liest sie nicht aus!?
Antworten