Heute habe ich ein paar Freunden beigebracht, wie man C struct
s benutzt. Einer von ihnen fragte, ob Sie struct
eine Funktion von einer Funktion zurückgeben könnten , auf die ich antwortete: "Nein! Sie würden stattdessen Zeiger auf dynamisch malloc
ed struct
s zurückgeben."
Ich kam von jemandem, der hauptsächlich C ++ macht, und hatte erwartet, dass er struct
s nicht nach Werten zurückgeben kann. In C ++ können Sie die operator =
für Ihre Objekte überladen und es ist absolut sinnvoll, eine Funktion zu haben, um Ihr Objekt nach Wert zurückzugeben. In C haben Sie diese Option jedoch nicht und ich habe darüber nachgedacht, was der Compiler tatsächlich tut. Folgendes berücksichtigen:
struct MyObj{
double x, y;
};
struct MyObj foo(){
struct MyObj a;
a.x = 10;
a.y = 10;
return a;
}
int main () {
struct MyObj a;
a = foo(); // This DOES work
struct b = a; // This does not work
return 0;
}
Ich verstehe, warum struct b = a;
sollte nicht funktionieren - Sie können nicht operator =
für Ihren Datentyp überladen . Wie ist es, dass gut a = foo();
kompiliert? Bedeutet es etwas anderes als struct b = a;
? Vielleicht lautet die Frage: Was genau macht die return
Aussage in Verbindung mit dem =
Unterschreiben?
[bearbeiten]: Ok, ich wurde nur darauf hingewiesen, dass struct b = a
es sich um einen Syntaxfehler handelt - das ist richtig und ich bin ein Idiot! Das macht es aber noch komplizierter! Verwenden struct MyObj b = a
funktioniert in der Tat! Was fehlt mir hier?
struct b = a;
ist ein Syntaxfehler. Was ist, wenn Sie es versuchenstruct MyObj b = a;
?struct MyObj b = a;
scheint es jedoch zu funktionieren :)Antworten:
Sie können
=
problemlos eine Struktur aus einer Funktion zurückgeben (oder den Operator verwenden). Es ist ein klar definierter Teil der Sprache. Das einzige Problem dabeistruct b = a
ist, dass Sie keinen vollständigen Typ angegeben haben.struct MyObj b = a
wird gut funktionieren. Sie können Strukturen auch an Funktionen übergeben - eine Struktur ist genau die gleiche wie jeder integrierte Typ, um Parameter zu übergeben, Werte zurückzugeben und zuzuweisen.Hier ist ein einfaches Demonstrationsprogramm, das alle drei Aufgaben ausführt: Übergibt eine Struktur als Parameter, gibt eine Struktur aus einer Funktion zurück und verwendet Strukturen in Zuweisungsanweisungen:
Das nächste Beispiel ist ziemlich genau das gleiche, verwendet jedoch den eingebauten
int
Typ zu Demonstrationszwecken. Die beiden Programme haben das gleiche Verhalten in Bezug auf die Wertübergabe für die Parameterübergabe, Zuweisung usw.:quelle
Wenn Sie einen Aufruf wie z. B.
a = foo();
ausführen, überträgt der Compiler möglicherweise die Adresse der Ergebnisstruktur auf den Stapel und übergibt sie als "versteckten" Zeiger an diefoo()
Funktion. Tatsächlich könnte es so etwas wie:Die genaue Implementierung hängt jedoch vom Compiler und / oder der Plattform ab. Wie Carl Norum bemerkt, kann die Struktur, wenn sie klein genug ist, sogar vollständig in einem Register zurückgegeben werden.
quelle
foo
Stapelrahmens sein. Es muss an einem Ort sein, der nach der Rückkehr von überlebtfoo
.foo
. Innerhalb der Funktionfoo
erledigen Sie einfach die Aufgabe*r = a
am Ende würde (effektiv) eine Kopie der lokalen Variablen in die Variable des Aufrufers erstellen . Ich sage "effektiv", weil der Compiler RVO implementieren und die lokale Variablea
vollständig entfernen könnte .c return struct
: Sie wissen, dass in cdecl dereax
Wert zurückgegeben wird und dass Strukturen im Allgemeinen nicht hineinpasseneax
. Das habe ich gesucht.Die
struct b
Zeile funktioniert nicht, da es sich um einen Syntaxfehler handelt. Wenn Sie es um den Typ erweitern, funktioniert es einwandfreiWas C hier tut, ist im Wesentlichen eine
memcpy
von der Quellstruktur zum Ziel. Dies gilt sowohl für die Zuweisung als auch für die Rückgabe vonstruct
Werten (und wirklich für jeden anderen Wert in C).quelle
memcpy
in diesem Fall tatsächlich einen wörtlichen Aufruf an - zumindest wenn die Struktur einigermaßen groß ist.memcpy
Funktion für die Struktursituationen häufig buchstäblich auf . Sie können ein schnelles Testprogramm erstellen und beispielsweise von GCC ausführen lassen. Für integrierte Typen, die nicht vorkommen - sie sind nicht groß genug, um diese Art der Optimierung auszulösen.memcpy
Symbol definiert. Daher treten häufig Linkerfehler mit "undefinierten Symbolen" auf, wenn der Compiler beschließt, eines selbst auszuspucken.Ja, es ist möglich, dass wir auch die Struktur übergeben und die Struktur zurückgeben können. Sie hatten Recht, aber Sie haben den Datentyp, der wie diese Struktur aussehen sollte, tatsächlich nicht übergeben. MyObj b = a.
Eigentlich habe ich auch erfahren, als ich versucht habe, eine bessere Lösung zu finden, um mehr als einen Wert für die Funktion ohne Verwendung eines Zeigers oder einer globalen Variablen zurückzugeben.
Im Folgenden finden Sie ein Beispiel dafür, in dem die Abweichung der Durchschnittsnoten eines Schülers berechnet wird.
quelle
Soweit ich mich erinnern kann, durften die ersten Versionen von C nur einen Wert zurückgeben, der in ein Prozessorregister passen könnte, was bedeutet, dass Sie nur einen Zeiger auf eine Struktur zurückgeben konnten. Die gleiche Einschränkung gilt für Funktionsargumente.
Neuere Versionen ermöglichen die Weitergabe größerer Datenobjekte wie Strukturen. Ich denke, diese Funktion war bereits in den achtziger oder frühen neunziger Jahren üblich.
Arrays können jedoch weiterhin nur als Zeiger übergeben und zurückgegeben werden.
quelle
Sie können Strukturen in C zuweisen. Dies
a = b;
ist eine gültige Syntax.Sie haben einfach einen Teil des Typs - das struct-Tag - in Ihrer Zeile weggelassen, der nicht funktioniert.
quelle
Es ist kein Problem, eine Struktur zurückzugeben. Es wird als Wert übergeben
Was aber, wenn die Struktur ein Mitglied enthält, das die Adresse einer lokalen Variablen hat?
Hier enthält e1.name eine Speicheradresse, die lokal für die Funktion get () ist. Sobald get () zurückkehrt, wäre die lokale Adresse für den Namen freigegeben worden. Wenn wir im Anrufer versuchen, auf diese Adresse zuzugreifen, kann dies zu einem Segmentierungsfehler führen, da wir versuchen, eine freigegebene Adresse zu verwenden. Das ist schlecht..
Wobei die e1.id vollkommen gültig ist, da ihr Wert nach e2.id kopiert wird
Wir sollten daher immer versuchen, die Rückgabe lokaler Speicheradressen einer Funktion zu vermeiden.
Alles, was malloced ist, kann nach Bedarf zurückgegeben werden
quelle
funktioniert gut mit neueren Versionen von Compilern. Genau wie bei id wird der Inhalt des Namens in die zugewiesene Strukturvariable kopiert.
quelle
Die Adresse struct var e2 wird als arg an den Angerufenenstapel gesendet, und dort werden Werte zugewiesen. Tatsächlich gibt get () die Adresse von e2 in eax reg zurück. Dies funktioniert wie ein Aufruf per Referenz.
quelle