2012-04-09 4 views
1

Also habe ich einen generischen Stapel in Plain C implementiert. Es sollte verschiedene Arten von Daten, inklusive Strukturen kopieren. Und durch Strukturen habe ich das Problem.Generic Stack in Ebene C kopiert Strukturen nicht korrekt

Also hier ist die Struktur des Stapels:

/* 
* Definite genStack as a structure. 
* Pointer elems points to the objects lying on the stack 
* The variable elemSize spiecifies the size of an element 
* The variable logLength specifies the number of actually 
* lying on the stack objects 
* The variable allocLenght specifies the allocated size 
*/ 

typedef struct{ 
void* elems; 
int elemSize; 
int logLength; 
int allocLength; 
}genStack; 

Push- und Pop-Funktionen:

void GenStackPush(genStack *s, const void *elemAddr) 
{ 
    /* if stack is full - allocates more memory */ 
    if (GenStackFull(s)) 
    { 
     GenStackAlloc(s, s->elemSize); 
    } 
    memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr)); 
    s->logLength++; 
} 

void GenStackPop(genStack *s, void *elemAddr) 
{ 
     if(GenStackEmpty(s)) 
     { 
     fprintf(stderr, "Can't pop element from stack: stack is empty.\n"); 
     } else 
     { 
     s->logLength--; 
     memcpy((void*) elemAddr, (s->elems)+(s->logLength), sizeof(s->elems[s->logLength])); 
     } 
} 

Einfache Strukturen Test:

gentest.h:

#ifndef GENTEST1_H 
#define GENTEST1_H 

typedef struct { 
    char* name; 
    int age; 
    char gender; 
}person; 

#endif 

Gentest.c:

#include <stdio.h> 
#include <stdlib.h> 
#include "gentest1.h" 
#include "genstacklib.h" 

int main(int argc, char* argv[]) 
{ 

    genStack StructStack; 
    person testPerson[5]; 
    person* newPerson; 
    person* test; 
    int i; 

    newPerson = (void*) malloc (sizeof(person)); 

    testPerson[0].name = "Alex"; 
    testPerson[0].age = 21; 
    testPerson[0].gender = 'm'; 

    testPerson[1].name = "Vanja"; 
    testPerson[1].age = 20; 
    testPerson[1].gender = 'm'; 

    testPerson[2].name = "sjrgsde"; 
    testPerson[2].age = 11; 
    testPerson[2].gender = 'w'; 

    testPerson[3].name = "wergsggsd"; 
    testPerson[3].age = 99; 
    testPerson[3].gender = 'y'; 

    testPerson[4].name = "adaasxx"; 
    testPerson[4].age = 13; 
    testPerson[4].gender = 'g'; 


    GenStackNew(&StructStack, sizeof(person)); 
    printf("sizeof(person) = %lu\n", sizeof(person)); 

    for (i = 0; i < 5; i++) { 
    newPerson = &testPerson[i]; 
     GenStackPush(&StructStack, newPerson); 
    printf("Pushed: %s, %d, %c\n", newPerson->name, newPerson->age, newPerson->gender); 
    } 

test = (void*) malloc (sizeof(person)); 
test->name = "test"; 
test->age = 0; 
test->gender = 't'; 
    while(!GenStackEmpty(&StructStack)) 
    { 
     GenStackPop(&StructStack, test); 
     printf("Popped: %s, %d, %c\n", test->name, test->age, test->gender); 
    } 

    GenStackDispose(&StructStack); 
    return 0; 
} 

Und hier ist die Ausgabe erhalte ich:

./gentest1 
elemSize = 16 GenStackInitialAlocationSize = 4 
sizeof(person) = 16 
Pushed: Alex, 21, m 
Pushed: Vanja, 20, m 
Pushed: sjrgsde, 11, w 
Pushed: wergsggsd, 99, y 
    New size of alloc = 8 
Pushed: adaasxx, 13, g 
Popped: adaasxx, 0, t 
Popped: wergsggsd, 0, t 
Popped: sjrgsde, 0, t 
Popped: Vanja, 0, t 
Popped: Alex, 0, t 

Wie Sie sehen können, ich Namen erhalten kann, aber kein Alter oder Geschlecht. Ich habe viele Optionen ausprobiert, aber immer noch Segmentierungsfehler oder die Ausgabe von oben. Für den Moment ist die Ausgabe oben die beste Ausgabe, die ich bekomme, aber immer noch nicht, was ich will.

Die Frage ist - wie kann ich die Ausgabe bekommen, die ich brauche? Vielen Dank im Voraus.

einige Fragen zu vermeiden: sizeof (Person) = s-> elemSize

Es durch die Schaffung des Stapels definiert:

genstacklib.c:

void GenStackNew(genStack *s, int elemSize) 
{ 
    void* newElems; 

    /* Allocate a new array to hold the contents. */ 
    newElems = (void*) malloc(elemSize * GenStackInitialAlocationSize); 
    printf("elemSize = %d\tGenStackInitialAlocationSize = %d\n", 
       elemSize, GenStackInitialAlocationSize); 
    if (newElems == NULL) 
    { 
     fprintf(stderr, "Error with allocating the stack.\n"); 
     exit(1); /* Exit, returning error code. */ 
    } 
    s->elems = newElems; 
    s->elemSize = elemSize; 
    s->allocLength = GenStackInitialAlocationSize; 
    s->logLength = 0; /*is empty*/ 

} 

gentest.c:

+2

Was haben Sie entdeckt, als Sie dies in einem Debugger durchgesehen haben? –

Antwort

0

Wie oben erwähnt, kopiert der Push die falsche Größe der Daten. Es sollte elemSize sein. Die memcpy überschreibt auch ihre eigenen Daten. So etwas sollte funktionieren.

memcpy((char*) (s->elems)+(s->logLength)*elemSize, elemAddr, elemSize); s->logLength++;

+0

Das einfach und richtig! Danke – Bfcm

0

Sie verwenden nicht elemSize in allen die relevanten Orte ...

+0

Ich benutze elemSize um Speicher zu reservieren. Wenn ich versuche, s-> elemSize Bytes zu kopieren (nach Person ist es 16 Bytes), erhalte ich Segmentierungsfehler – Bfcm

+0

@Bfcm: Dann haben Sie mehr als einen Fehler. Sie sollten den Debugger verwenden, um herauszufinden, was es ist. –

+0

@Bfcm: Wo immer Sie Dinge mit der Größe 'elemSize' bearbeiten müssen (Kopieren, Anpassen von Zeigern usw.). –

1

Ihre Push-Funktion ist das Kopieren sizeof(*elemAddr) und das ist ein void *, so hat es die Größe eines Zeigers nicht die die bestimmungsgemäßen Größe einer person Struktur. So kopieren Sie wahrscheinlich nur die ersten 4 Bytes

+0

Wenn ich versuche, s-> elemSize Bytes zu kopieren (nach Person ist es 16 Bytes), erhalte ich Segmentierungsfehler – Bfcm

+0

machen Sie eine 'sizeof (Person)' Sie werden durch die Ergebnisse überrascht sein, weil es wahrscheinlich 12. Ein Zeiger ist ist nur eine Variable mit einer Adresse. Recherchieren Sie mehr zu einem Thema namens Deep Copy (im Gegensatz zu Shallow Copy) –

+0

@hexa: Wenn dies eine 64-Bit-Plattform ist, dann ist es fast sicher nicht 12. –

0
void GenStackPush(genStack *s, const void *elemAddr) 
{ 
    ...  
    memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr)); 
                 ^^^^^^^^^^^^^^^^^ 

Dies ist sehr falsch; Der Typ des Ausdrucks *elemAddr ist void, der eine Integritätsbedingung ist (sizeof darf nicht für einen unvollständigen Ausdruck aufgerufen werden, und void ist ein unvollständiger Typ). Sie sollten die Warnstufe auf Ihrem Compiler aufdrehen. Ich schrieb ein Testprogramm, um sizeof auf Ausdrücke vom Typ void * und void zu berechnen, und ich erhalte eine Warnung mit gcc -pedantic.Wenn ich die -pedantic fallen lasse, bekomme ich keine Warnung, aber das Ergebnis, das ich für sizeof (void) bekomme, ist 1, das ich ziemlich sicher bin nicht die Größe von person. Warum benutzt du nicht s->elemSize hier?

Zweitens, warum werfen Sie s->elems zu char *?

EDIT

Wenn ich einige Ratschläge anbieten kann, ich habe ein paar generischen Container in der Vergangenheit womped auf, und hier sind die Lektionen, die ich gekommen bin weg mit:

Zuerst Delegierter alle typenbewusste Operationen (Zuordnen, Freigabe, Kopieren, Vergleichen, Anzeigen usw.) zu separaten Funktionen, die über Funktionszeiger aufgerufen werden, die als Parameter an die Funktionen des generischen Containers übergeben werden; das heißt, würde ein Push wie

GenStackPush(genStack *stack, const void *data, void *(*copy)(const void *)) 
{ 
    stack->elems[++stack->logLength] = copy(data); 
} 
... 
void *myIntCopyFunc(const void *data) 
{ 
    const int *inputData = (const int *) data; 
    int *copy = malloc(sizeof *copy); 
    if (copy) 
    *copy = *inputData; 
    return copy; 
} 
... 
GenStackPush(&myIntStack, &intVal, myIntCopyFunc); 

Ein Problem definiert werden Sie mit Ihrem person Art haben, ist, dass Sie nicht eine tiefe Kopie des name Mitglied tun; Sie kopieren nur einen Zeigerwert auf den Stapel. In diesem Fall ist es keine große Sache, da Sie mit String-Literalen arbeiten, aber wenn Sie beispielsweise eine lokale char [] verwenden, hätten Sie Probleme. Indem Sie eine separate Kopierfunktion für jeden Typ schreiben, können Sie diese Art von Problemen behandeln, anstatt zu versuchen, eine Einheitsallokation in der Containerfunktion selbst durchzuführen.

Zweitens, rufen Sie nicht Ihre allgemeinen Containerfunktionen direkt auf; legt einen Art-aware Schnittstelle zwischen Ihnen und dem Behälter (im Grunde, die Version des armen Mannes von Funktion Überlastung):

void pushInt(GenStack *stack, int intVal) 
{ 
    GenStackPush(stack, &intVal, myIntCopyFunc); 
} 
... 
genStack myIntStack; 
... 
pushInt(&myIntStack, 5); 

Dies gibt Ihnen zwei Vorteile; Erstens können Sie Literalwerte als Parameter übergeben (was mit Parametern vom Typ void * nicht möglich ist). Zweitens gibt es Ihnen eine Möglichkeit, die Typsicherheit für Ihren Container durchzusetzen. Sie können nicht versehentlich einen Wert des falschen Typs auf diese Weise ausgeben.

Ist das eine Menge zusätzlicher Arbeit? Oh mein Ja. Es gibt ein Los von Magie, die unter der Haube passieren muss, damit generische Behältertypen richtig funktionieren. Wenn Sie versuchen, die gleiche Art von Funktionalität zu replizieren, die Sie mit dem C++ std::stack Containertyp erhalten, werden Sie eine Los Code schreiben.

+0

Vielen Dank! – Bfcm