Gibt ein `struct` von einer Funktion in C zurück

Lesezeit: 8 Minuten

Benutzeravatar von mmirzadeh
mmirzadeh

Heute habe ich ein paar Freunden beigebracht, wie man C benutzt structs. Einer von ihnen fragte, ob Sie a zurückgeben könnten struct aus einer Funktion, auf die ich antwortete: “Nein! Sie würden Zeiger auf dynamisch zurückgeben malloced structs stattdessen.”

Da ich von jemandem komme, der hauptsächlich C++ macht, hatte ich erwartet, nicht zurückkehren zu können structs nach Werten. In C++ kann man die überladen operator = für Ihre Objekte und es ist absolut sinnvoll, eine Funktion zu haben, die Ihr Objekt nach Wert zurückgibt. In C haben Sie diese Option jedoch nicht, und deshalb habe ich 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 überladen operator = für Ihren Datentyp. Wie kommt es, dass a = foo(); kompiliert gut? Bedeutet es etwas anderes als struct b = a;? Vielleicht stellt sich die Frage: Was genau macht das return Erklärung in Verbindung mit = Zeichen tun?

  • struct b = a; ist ein Syntaxfehler. Was, wenn du es versuchst struct MyObj b = a;?

    – Greg Hewgill

    11. März 2012 um 7:00 Uhr

  • @GregHewgill: Du hast absolut recht. Ganz interessant aber struct MyObj b = a; scheint zu funktionieren 🙂

    – mmirzadeh

    11. März 2012 um 7:07 Uhr

Benutzeravatar von Carl Norum
Karl Norum

Sie können eine Struktur von einer Funktion zurückgeben (oder die = Bediener) ohne Probleme. Es ist ein klar definierter Teil der Sprache. Das einzige Problem mit struct b = a ist, dass Sie keinen vollständigen Typ angegeben haben. struct MyObj b = a wird gut funktionieren. Sie können Strukturen übergeben zu funktioniert auch – eine Struktur ist genau das Gleiche wie jeder eingebaute Typ zum Zweck der Parameterübergabe, Rückgabewerte und Zuweisung.

Hier ist ein einfaches Demonstrationsprogramm, das alle drei Aufgaben erfüllt – eine Struktur als Parameter übergibt, eine Struktur von einer Funktion zurückgibt und Strukturen in Zuweisungsanweisungen verwendet:

#include <stdio.h>

struct a {
   int i;
};

struct a f(struct a x)
{
   struct a r = x;
   return r;
}

int main(void)
{
   struct a x = { 12 };
   struct a y = f(x);
   printf("%d\n", y.i);
   return 0;
}

Das nächste Beispiel ist ziemlich genau dasselbe, verwendet aber die eingebaute int Typ zu Demonstrationszwecken. Die beiden Programme verhalten sich bzgl. Pass-by-Value bei Parameterübergabe, Zuweisung etc. gleich:

#include <stdio.h>

int f(int x) 
{
  int r = x;
  return r;
}

int main(void)
{
  int x = 12;
  int y = f(x);
  printf("%d\n", y);
  return 0;
}

  • Das ist ziemlich interessant. Ich hatte immer den Eindruck, dass Sie dafür Hinweise brauchen. Ich lag falsch 🙂

    – mmirzadeh

    11. März 2012 um 7:11 Uhr

  • Sie sicherlich nicht brauchen Zeiger. Das heißt, die meiste Zeit möchten Sie sie verwenden – die impliziten Speicherkopien, die stattfinden, um Strukturen nach Wert herumzuschleudern, können eine echte Verschwendung von CPU-Zyklen sein, ganz zu schweigen von Speicherbandbreite.

    – Karl Norum

    11. März 2012 um 7:15 Uhr

  • @CarlNorum wie groß muss eine Struktur werden, dass eine Kopie mehr kostet als malloc + free?

    – Josefx

    11. März 2012 um 15:56 Uhr

  • @josefx, eine einzelne Kopie? Wahrscheinlich riesig. Die Sache ist die, normalerweise, wenn Sie Strukturen nach Wert weitergeben, kopieren Sie sie viel. Ganz so einfach ist es jedenfalls nicht. Sie könnten lokale oder globale Strukturen herumreichen, in diesem Fall sind die Zuordnungskosten ziemlich kostenlos.

    – Karl Norum

    11. März 2012 um 16:30 Uhr


  • Sie benötigen Zeiger und Speicherzuweisung für den zurückgegebenen Wert außerhalb des Funktionskörpers, sobald die für einen Wert zugewiesene Speichermenge zur Kompilierzeit nicht bekannt ist. Es ist für Strukturen, also haben C-Funktionen kein Problem damit, sie zurückzugeben.

    – reinierpost

    1. Februar 2015 um 19:11 Uhr


Benutzeravatar von Greg Hewgill
Greg Hewgill

Bei einem Anruf wie z a = foo();könnte der Compiler die schieben die Anschrift der Ergebnisstruktur auf dem Stack und übergibt sie als “versteckten” Zeiger an die foo() Funktion. Effektiv könnte es so etwas werden wie:

void foo(MyObj *r) {
    struct MyObj a;
    // ...
    *r = a;
}

foo(&a);

Die genaue Implementierung davon hängt jedoch vom Compiler und/oder der Plattform ab. Wie Carl Norum feststellt, kann die Struktur, wenn sie klein genug ist, sogar vollständig in ein Register zurückgeführt werden.

  • Das ist völlig implementierungsabhängig. Beispielsweise übergibt armcc ausreichend kleine Strukturen in den regulären Parameterübergabe- (oder Rückgabewert-) Registern.

    – Karl Norum

    11. März 2012 um 7:04 Uhr

  • Würde das nicht einen Zeiger auf eine lokale Variable zurückgeben? Der Speicher für die zurückgegebene Struktur kann nicht Teil von sein foo Stapelrahmen. Es muss an einem Ort sein, der über die Rückkehr hinaus überlebt foo.

    – Anders Abel

    11. März 2012 um 7:07 Uhr

  • @AndersAbel: Ich denke, was Greg meint, ist, dass der Compiler einen Zeiger auf die Variable in der nimmt hauptsächlich Funktion und übergibt es an die Funktion foo. Innerhalb der Funktion fooSie erledigen nur die Aufgabe

    – mmirzadeh

    11. März 2012 um 7:16 Uhr

  • @AndersAbel: Die *r = a würde am Ende (effektiv) eine Kopie der lokalen Variablen in die Variable des Aufrufers erstellen. Ich sage “effektiv”, weil der Compiler implementieren könnte RVO und eliminieren Sie die lokale Variable a völlig.

    – Greg Hewgill

    11. März 2012 um 7:19 Uhr


  • Obwohl dies die Frage nicht direkt beantwortet, ist dies der Grund, warum viele Leute hier über Google landen werden c return struct: sie wissen das in cdecl eax als Wert zurückgegeben wird und Strukturen im Allgemeinen nicht hineinpassen eax. Das habe ich gesucht.

    – Ciro Santilli OurBigBook.com

    22. August 2013 um 5:22 Uhr

Das struct b Zeile funktioniert nicht, da es sich um einen Syntaxfehler handelt. Wenn Sie es um den Typ erweitern, funktioniert es einwandfrei

struct MyObj b = a;  // Runs fine

Was C hier tut, ist im Wesentlichen a memcpy von der Quellstruktur zum Ziel. Dies gilt sowohl für die Abtretung als auch für die Rückgabe struct Werte (und wirklich jeder andere Wert in C)

  • +1, tatsächlich geben viele Compiler tatsächlich einen wörtlichen Aufruf an aus memcpy in diesem Fall – zumindest, wenn die Struktur einigermaßen groß ist.

    – Karl Norum

    11. März 2012 um 7:14 Uhr


  • Während der Initialisierung eines Datentyps funktioniert also die memcpy-Funktion??

    – bhuwansahni

    11. März 2012 um 7:14 Uhr

  • @bhuwansahni Ich bin mir nicht ganz sicher, was du hier fragst. Könnten Sie etwas näher darauf eingehen?

    – JaredPar

    11. März 2012 um 7:15 Uhr

  • @JaredPar – Compiler tun dies oft buchstäblich anrufen das memcpy Funktion für die Struktursituationen. Sie können beispielsweise ein schnelles Testprogramm erstellen und GCC dabei zusehen. Bei integrierten Typen passiert das nicht – sie sind nicht groß genug, um diese Art von Optimierung auszulösen.

    – Karl Norum

    11. März 2012 um 7:25 Uhr


  • Es ist definitiv möglich, es zu verwirklichen – das Projekt, an dem ich arbeite, hat keine memcpy Symbol definiert, so dass wir oft auf “undefiniertes Symbol”-Linker-Fehler stoßen, wenn der Compiler beschließt, selbst einen auszuspucken.

    – Karl Norum

    11. März 2012 um 7:28 Uhr


Benutzeravatar von Giorgio
Giorgio

Soweit ich mich erinnern kann, durften die ersten Versionen von C nur einen Wert zurückgeben, der in ein Prozessorregister passen konnte, was bedeutet, dass Sie nur einen Zeiger auf eine Struktur zurückgeben konnten. Die gleiche Einschränkung galt für Funktionsargumente.

Neuere Versionen erlauben die Weitergabe größerer Datenobjekte wie Strukturen. Ich glaube, dieses Feature war schon in den Achtzigern oder frühen Neunzigern üblich.

Arrays können jedoch weiterhin nur als Zeiger übergeben und zurückgegeben werden.

Amans Benutzeravatar
Ein Mann

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 nicht übergeben, der wie folgt aussehen sollte: struct MyObj b = a.

Eigentlich habe ich es auch erfahren, als ich versuchte, eine bessere Lösung zu finden, um mehr als einen Wert für die Funktion zurückzugeben ohne Zeiger oder globale Variable zu verwenden.

Unten ist nun das Beispiel für dasselbe, das die Abweichung einer Schülernote vom Durchschnitt berechnet.

#include<stdio.h>
struct marks{
    int maths;
    int physics;
    int chem;
};

struct marks deviation(struct marks student1 , struct marks student2 );

int main(){

    struct marks student;
    student.maths= 87;
    student.chem = 67;
    student.physics=96;

    struct marks avg;
    avg.maths= 55;
    avg.chem = 45;
    avg.physics=34;
    //struct marks dev;
    struct marks dev= deviation(student, avg );
    printf("%d %d %d" ,dev.maths,dev.chem,dev.physics);

    return 0;
 }

struct marks deviation(struct marks student , struct marks student2 ){
    struct marks dev;

    dev.maths = student.maths-student2.maths;
    dev.chem = student.chem-student2.chem;
    dev.physics = student.physics-student2.physics; 

    return dev;
}

Benutzeravatar von Neuron
Neuron

Es ist kein Problem, eine Struktur zurückzugeben. Es wird als Wert übergeben

Was aber, wenn die Struktur ein beliebiges Mitglied enthält, das eine Adresse einer lokalen Variablen hat?

struct emp {
    int id;
    char *name;
};

struct emp get() {
    char *name = "John";

    struct emp e1 = {100, name};

    return (e1);
}

int main() {

    struct emp e2 = get();

    printf("%s\n", e2.name);
}

Jetzt hier e1.name enthält eine für die Funktion lokale Speicheradresse get(). Einmal get() zurückgibt, wäre die lokale Adresse für name freigegeben worden. Wenn wir also versuchen, auf diese Adresse zuzugreifen, kann dies im Aufrufer zu einem Segmentierungsfehler führen, da wir versuchen, eine freigegebene Adresse zu verwenden. Das ist schlecht..

Während die e1.id ist vollkommen gültig, da sein Wert kopiert wird e2.id

Wir sollten also immer versuchen, die Rückgabe lokaler Speicheradressen einer Funktion zu vermeiden.

Alles, was malloced ist, kann nach Belieben zurückgegeben werden

Du kann Strukturen in C zuweisen. a = b; ist gültige Syntax.

Sie haben einfach einen Teil des Typs – das struct-Tag – in Ihrer Zeile weggelassen, was nicht funktioniert.

1425950cookie-checkGibt ein `struct` von einer Funktion in C zurück

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy