Zugriff auf Array via Retval einer Funktion, die den Arrayzeiger verändert

16/09/2007 - 03:05 von Bodo Thiesen | Report spam
Hallo NG.

Ist etwas her, seit ich das letzte mal hier war, aber ich lebe noch.
Ist auch schön, die meisten bekannten Regulars von früher hier noch
vorzufinden ;)

Ok, rede ich mal nicht um den heißen Brei herum. Qualitativ habe ich
folgendes Problem:

char * array=malloc(...) oder NULL;

int return_index(int index,char * * array_pointer,...) {
*array_pointer=realloc(*array_pointer,...);
if (!*array_pointer) exit(2);
return index;
}

char c=array[return_index(0,&array)];

Die Zuweisung von c löst bei mir ein SIGSEGV aus. Aus dem Assembler
Listing, den einem gcc netterweise gibt, wenn man ihn nur nett genug
fragt, geht hervor, daß die Adresse von array ermittelt wird, danach
return_index aufgerufen wird und schlußendlich über den *alten* Wert
(der noch vor dem Aufruf ermittelt wurde) der Zugriff durchgeführt
wurde. Daher meine Frage: Ist mein Code kaputt oder ist gcc kaputt? Ich
bildete mir ein, daß der Aufruf von return_index einen Sequence-Point
darstellt, und somit der Ausdruck in Ordnung sein sollte. Irre ich mich
da?

Die Beschreibung des eigentlichen Problems ist hier zu Ende ;),
allerdings folgt eine vollstàndigere Beschreibung dessen, was
ich eigentlich will, so daß Alternativvorschlàge gemacht werden
könnten, die das Problem eventuell umgehen.

Hintergrund: Das Problem trat auf, nachdem ich meine
vasiar-Implementation etwas geàndert habe (vasiar=VAriable SIzed
ARray).

Das API dafür war bisher:

DECLARE_VASIAR(type,var): Deklariert ein VASIAR

Folgendermaßen Implementiert:

#define DECLARE_VASIAR(type,var) struct { \
type * ptr; \
int max; \
int num; \
} var;

Dazu kommt ACCESS(type,var,idx), das den Zugriff auf das Element an
Position idx ermöglicht.

Folgendermaßen Implementiert:

#define ACCESS(type,var,idx) *(type*)access_vasiar( \
(void**)&var.ptr,&var.max,&var.num,sizeof(type),idx)

access_vasiar (gehört nicht zum API) liefert den Zeiger auf
var.ptr[idx] zurück, sorgt aber vor allem auch dafür, daß das Array ggf
vergrößert wird, und num mindestens idx-1 ist.

ACCESS(type,var,idx) wollte ich durch VAACCESS(var,idx) ersetzen.
Dazu habe ich type * ptr im Struct durch union {
type * vartype; void * nontype; } addr ersetzt. DECLARE_VASIAR sieht
jetzt wie folgt aus:

#define DECLARE_VASIAR(type,var) struct { \
union { \
type * vartype; \
void * nontype; \
} addr; \
int max; \
int num; \
} var;

VAACCESS(var,idx) mit gleicher Wirkung wie ACCESS(type,var,idx) ist
jetzt:

#define VAACCESS(var,idx) var.addr.vartype[vasiar_access( \
&var.addr.nontype, \
&var.num, \
&var.max, \
sizeof(*var.addr.vartype), \
idx \
)]

access_vasiar liefert jetzt idx und nicht mehr var.ptr+sizeof(type)*idx
zurück.

Leider funktioniert das nicht s.o.

Vorschlàge, wie man das anders realisieren kann (aber ohne type an
VAACCESS übergeben zu müssen - das hatte ich ja bereits) sind herzlich
willkommen.

Ach ja: Zu erwàhnen ist: &VAACCESS sollte natürlich funktionieren, um
die Adresse des Wertes zu ermitteln, daher fàllt (access_vasiar(
&var.addr.nontype,&var.max,&var.num,sizeof(*var.addr.vartype),idx
),var.addr.vartype[idx]) leider flach (war übrigens mein erster
Versuch).

Gruß, Bodo
 

Lesen sie die antworten

#1 Erich Fruehstueck
16/09/2007 - 10:46 | Warnen spam
Am Sun, 16 Sep 2007 03:05:38 +0200 schrieb Bodo Thiesen:

Hallo NG.

Ist etwas her, seit ich das letzte mal hier war, aber ich lebe noch. Ist
auch schön, die meisten bekannten Regulars von früher hier noch
vorzufinden ;)

Ok, rede ich mal nicht um den heißen Brei herum. Qualitativ habe ich
folgendes Problem:

char * array=malloc(...) oder NULL;

int return_index(int index,char * * array_pointer,...) {
*array_pointer=realloc(*array_pointer,...); if (!*array_pointer)
exit(2);
return index;
}

char c=array[return_index(0,&array)];

Die Zuweisung von c löst bei mir ein SIGSEGV aus. Aus dem Assembler
Listing, den einem gcc netterweise gibt, wenn man ihn nur nett genug
fragt, geht hervor, daß die Adresse von array ermittelt wird, danach
return_index aufgerufen wird und schlußendlich über den *alten* Wert
(der noch vor dem Aufruf ermittelt wurde) der Zugriff durchgeführt
wurde. Daher meine Frage: Ist mein Code kaputt oder ist gcc kaputt? Ich
bildete mir ein, daß der Aufruf von return_index einen Sequence-Point
darstellt, und somit der Ausdruck in Ordnung sein sollte. Irre ich mich
da?



Aber a[b] ist gleichwertig zu *(a + b), und hier ist kein Sequence-Point.

Hintergrund: Das Problem trat auf, nachdem ich meine
vasiar-Implementation etwas geàndert habe (vasiar=VAriable SIzed ARray).

Das API dafür war bisher:



[...]

Leider funktioniert das nicht s.o.

Vorschlàge, wie man das anders realisieren kann (aber ohne type an
VAACCESS übergeben zu müssen - das hatte ich ja bereits) sind herzlich
willkommen.



Ich habe folgende Implementation für ein VLA anzubieten:

http://efeu.cybertec.at/efeu-3.1/sr...efm/vecbuf

Aus vecbuf.h:

typedef struct {
size_t blksize; /* Blockgröße zur Buffervergrößerung */
size_t elsize; /* Größe eines Datenelementes */
size_t size; /* Aktuelle Größe des Buffers */
size_t used; /* Zahl der benützten Elemente */
void *data; /* Datenbuffer */
} VecBuf;

Die einzige Information, die ich über einen Datentyp benötige, ist seine
Speichergröße. Diese wird in der Struktur gespeichert.

Zur Initialisierung von Variablen habe ich folgende Makros:

#define VB_DATA(blk, size) { blk, size, 0, 0, NULL }
#define VECBUF(name, blk, size) VecBuf name = VB_DATA(blk, size)

Verwendung:

static VECBUF(buf, 1024, sizeof(int));

Vergleiche dazu auch die Dokumentation:
http://efeu.cybertec.at/efeu/doc/de/ps/efm.ps.gz
Seite 187 und 229.

Grüße
Erich

EFEU 3.1 is released!
Get the open source from http://efeu.cybertec.at.

Ähnliche fragen