it-swarm.com.de

Rückgabe einer `struct` von einer Funktion in C

Heute brachte ich ein paar Freunden bei, wie man C structs verwendet. Einer von ihnen fragte, ob Sie eine struct von einer Funktion zurückgeben könnten, auf die ich antwortete: "Nein! Sie würden stattdessen Zeiger auf dynamisch malloced structs zurückgeben."

Da ich von jemandem stamme, der hauptsächlich C++ betreibt, hatte ich erwartet, dass er structs nicht mit Werten zurückgeben kann. In C++ können Sie den operator = für Ihre Objekte überladen, und es ist absolut sinnvoll, eine Funktion zu haben, um Ihr Objekt nach Wert zurückzugeben. In C steht Ihnen diese Option jedoch nicht zur Verfügung, und ich habe darüber nachgedacht, was der Compiler tatsächlich macht. 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; nicht funktionieren sollte - Sie können operator = für Ihren Datentyp nicht überladen. Wie kommt es, dass a = foo(); gut kompiliert wird? Bedeutet es etwas anderes als struct b = a;? Vielleicht stellt sich die Frage: Was genau macht die Anweisung return in Verbindung mit dem Zeichen =?

[edit]: Ok, ich wurde nur darauf hingewiesen, struct b = a ist ein Syntaxfehler - das ist richtig und ich bin ein Idiot! Das macht es aber noch komplizierter! Die Verwendung von struct MyObj b = a funktioniert tatsächlich! Was fehlt mir hier?

135
GradGuy

Sie können eine Struktur problemlos von einer Funktion zurückgeben (oder den Operator = verwenden). 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 auch Strukturen an - Funktionen übergeben. Eine Struktur ist für die Parameterübergabe, für Rückgabewerte und für die Zuweisung genau dasselbe wie jeder eingebaute Typ.

Hier ist ein einfaches Demonstrationsprogramm, das alle drei Funktionen ausführt - eine Struktur als Parameter übergibt, eine Struktur aus 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 jedoch zu Demonstrationszwecken den integrierten int-Typ. Die beiden Programme verhalten sich hinsichtlich der Parameterübergabe, -zuweisung usw. in Bezug auf die Weitergabe von Werten 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;
}
169
Carl Norum

Bei einem Aufruf wie a = foo(); kann der Compiler die address der Ergebnisstruktur auf den Stack schieben und als "versteckten" Zeiger an die foo()-Funktion übergeben. In der Tat könnte es so etwas werden:

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. Wenn die Struktur klein genug ist, könnte sie, wie Carl Norum feststellt, sogar vollständig in einem Register zurückgegeben werden.

27
Greg Hewgill

Die struct b-Zeile funktioniert nicht, da es sich um einen Syntaxfehler handelt. Wenn Sie es erweitern, um den Typ einzubeziehen, wird es gut funktionieren

struct MyObj b = a;  // Runs fine

Was 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 von struct-Werten (und für jeden anderen Wert in C).

12
JaredPar

ja, es ist möglich, dass wir auch Struktur übergeben und Struktur zurückgeben können. Sie hatten recht, aber Sie haben tatsächlich den Datentyp nicht übergeben, der so aussehen sollte: myObj b = a. 

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

Nun folgt das gleiche Beispiel, das die Abweichung eines Schülers über den 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;
}
6
Aman

Sie könnenkönnen/ Strukturen in C zuweisen. a = b; ist eine gültige Syntax.

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

3
DigitalRoss

Soweit ich mich erinnern kann, durften die ersten Versionen von C nur einen Wert zurückgeben, der in ein Prozessorregister passt. Das bedeutet, dass Sie nur einen Zeiger auf Eine Struktur zurückgeben können. Dieselbe Einschränkung gilt für Funktionsargumente.

Neuere Versionen erlauben es, größere Datenobjekte wie structs herumzugeben. Ich denke, dieses Feature war bereits in den achtziger oder frühen neunziger Jahren üblich.

Arrays können jedoch immer noch als Zeiger übergeben und zurückgegeben werden.

3
Giorgio

Es gibt kein Problem bei der Rückgabe einer Struktur. Es wird als Wert übergeben

Was aber, wenn die Struktur ein Element enthält, das die 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);
}

Nun enthält hier e1.name eine lokale Speicheradresse für die Funktion get () . Wenn get () zurückgegeben wird, wäre die lokale Adresse für den Namen ..__ Wenn Sie auf diese Adresse zugreifen, kann dies zu Segmentierungsfehlern führen, da wir eine freigegebene Adresse versuchen. Das ist schlecht..

Wobei die e1.id vollkommen gültig ist, da ihr Wert in die e2.id kopiert wird

Wir sollten also immer versuchen, lokale Speicheradressen einer Funktion nicht zurückzugeben.

Alles, was Sie malloced haben, kann wie gewünscht zurückgegeben werden

2
Jagan

die Strukturvariable e2-Adresse, die als Argument an den Aufruf-Stack gesendet wurde, und die Werte werden dort zugewiesen. Tatsächlich gibt get () die Adresse von e2 in eax reg zurück. Dies funktioniert wie ein Referenzruf.

1
Bala
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);
}

funktioniert gut mit neueren Versionen von Compilern ..__ Wie bei id wird der Inhalt des Namens in die zugewiesene Strukturvariable kopiert.

0
saroj panda