2016-07-11 19 views
0

Ich implementiere einen generischen Stapel in C für Lernzwecke. Dies ist die Pop-Funktion davon:Funktion obere Stapelimplementierung in C

void* StackPop(Stack *s) { 
    assert(s != NULL); 
    assert(s->logicalLen > 0); // there must be at least on element 
    void *object = (char*) s->elems + (s->logicalLen--) * s->elemSize; // decrement logical length 
                     // on the fly 
    return object; 
} 

In diesem Fall (StackPop) ist mir klar, dass ich das Eigentum an dem Objekt an der Spitze an den Anrufer übertragen müssen. Das Zurückgeben eines generischen Zeigers ist also in Ordnung, da der Aufrufer entscheiden soll, was mit dem Objekt zu tun ist. Auf der anderen Seite möchte ich eine StackTop() -Funktion schreiben, um das oberste Element zurückzugeben. Hier kommt so Ungewissheit zu mir: Ich weiß, dass beide Funktionen sehr ähnlich sein sollten mit dem Unterschied, dass ich die Größe des Stapels nicht verringern oder einen Zeiger zurückgeben sollte, da ich nicht möchte, dass der Client es ändert. Wie übertrage ich dann nur eine Kopie des obersten Elements? Akzeptiere ich einen generischen Zeiger als Argument, meine einzige Option, und mache eine tiefe Kopie in diese Adresse?

void StackPop(void *target) { 
    // make a deep copy into target address with memcopy or whatever? 
} 

Gibt es einen besseren Ansatz?

+0

Unrelated, würde ich anfangen zu schreiben "StackPop", um einige Anschein von Verstand zu zeigen und nach einem leeren Stapel vor all den anderen Calisthenics zu überprüfen.Und wähle eine Sprache: Die Passung und Form suggeriert C; nicht C++. – WhozCraig

+0

@WhozCraig wahr, ich habe es nur schnell geschrieben. Ich achte momentan nicht sehr auf die Fehlerüberprüfung. Ich werde es korrigieren. – blade

+1

Ein "richtiger" Weg, um Ihr Problem zu lösen, ist, dass Ihre Pop-Funktion * nichts * zurückgibt (außer vielleicht ein Fehlerzustand). Erlauben Sie nur den Zugriff auf das "Top" -Objekt (falls vorhanden) über die Top-Funktion. Wenn der Anrufer eine Kopie des Objekts möchte, so sei es; sie können einen vor dem Knallen machen. Das ist nicht zufällig die Art und Weise, wie der C++ - Standardbibliotheks-Containeradapter "std :: stack " funktioniert. Und außerdem wird "Assert" zu einem No-Op-Code, so dass Sie immer noch eine logische Fehlerüberprüfung benötigen, nur für den Fall, dass Sie sich dessen nicht bewusst waren. – WhozCraig

Antwort

1

Ich weiß, dass beide Funktionen sehr ähnlich ...

sein sollte Wer sagt das?

Sie entwerfen den Stapel und Sie können daher alle Regeln für die Verwendung des Stapels definieren. Zum Beispiel dürfen Sie alle Prüfungen für NULL-Zeiger überspringen und nur in die Dokumentation schreiben, dass die aufrufende Funktion xxx mit einem NULL-Zeiger zu undefiniertem Verhalten führt. Am Ende hängt es davon ab, was Sie wollen - hohe Leistung, sicher zu verwenden, einfach zu bedienen, etc.

Und das gleiche gilt für die StackPop und StackTop Funktionen. Sie können die Regeln festlegen.

Für mich scheint Ihre StackPop logisch falsch. Die Rückgabe eines Zeigers an einen Speicherbereich, der durch Dekrementieren logicalLen logisch freigegeben wurde, ist nicht das, was ich erwarten würde. Aber Sie können einfach in die Dokumentation schreiben, dass die von StackPop zurückgegebenen Zeiger beim nächsten StackPush ungültig werden. (In C++ haben einige Container solche Beschränkungen für Iteratoren/Zeiger).

Und das gleiche gilt für StackTop - Sie können einfach einen Zeiger auf das oberste Element zurückgeben und angeben, dass es nur bis zum nächsten StackPop gültig ist.

Es ist dein Design, deine Regeln.

würde ich lieber tun, wie folgt:

int push(stack* s, void* o) 
{ 
    // memcpy contents of o to next free location 
    // increment number of elements held 
} 

int top(stack* s, void* o) 
{ 
    // memcpy top element to o 
} 

int pop(stack* s) 
{ 
    // decrement number of elements held 
} 

In allen drei Fällen die int ist Fehlerwerte für die Rückgabe.

Für die Leistung könnten Sie eine Funktion bereitstellen, z. ptop, die einen Zeiger auf das oberste Element zurückgab (nur gültig bis zum nächsten Pop).

In Bezug auf deep copy:

Es gibt keine Art und Weise ist, können Sie tiefe Kopie in einem generischen Stapel tun. Wenn ein Benutzer etwas in den Stapel legt, der Zeiger enthält, muss der Benutzer selbst damit umgehen.

+0

Sie könnten die tiefe Kopie durchführen, wenn der Benutzer einen Funktionszeiger angibt, der angibt, wie er ausgeführt werden soll. – Hurkyl

+0

@Hurkyl - ja, das wäre möglich. Persönlich würde ich es dem Benutzer überlassen, aber es ist eine Frage des Designs und - wie bereits erwähnt - als Designer, Sie legen die Regeln fest. Darin besteht kein Richtig oder Falsch - nur eine Frage dessen, was am besten zu Ihren aktuellen Bedürfnissen passt. – 4386427