Forum: Compiler & IDEs struct und char[] als Rückgabe


von Henrik (Gast)


Lesenswert?

Hallo,
ich habe eine Funktion, die ein struct zurückliefert.
In diesem struct liegt ein String:

typedef struct{
  uint8_t id,
    parent,
    child,
    next,
    prev;
    uint8_t type;
    char name[MENU_STRING_LENGTH];
} menu_entry_t;


menu_entry_t get_menu_entry(uint8_t id){
  menu_entry_t res;
  strcpy(res.name, test_menu[id].name);
  res.id = test_menu[id].id;
  res.next = test_menu[id].next;
  res.prev = test_menu[id].prev;
  res.parent = test_menu[id].parent;
  res.child = test_menu[id].child;
  res.type = test_menu[id].type;

  return res;
}

Kann das so klappen? Vorallem bezüglich des Strings bin ich mir absolut 
nicht sicher, ob ich das so machen darf.

MfG
Henrik

von Karl H. (kbuchegg)


Lesenswert?

Henrik wrote:

> Kann das so klappen?

Ja, kann und wird.

> Vorallem bezüglich des Strings bin ich mir absolut
> nicht sicher, ob ich das so machen darf.

Doch, doch, ist schon in Ordnung.
Der Knackpunkt ist, dass alles in einer Struktur eingebettet
ist. Dadurch wird eine 1:1 byteweise Kopie bei der Übergabe
gemacht. Egal ob die Struktur in eine Funktion hinein übergeben
wird oder ob sie als Returnwert geliefert wird.

von Henrik (Gast)


Lesenswert?

Ist dann das strcpy() in der Funktion, die das struct zurückliefern 
soll, überhaupt nötig?

MfG
Henrik

von Karl H. (kbuchegg)


Lesenswert?

Henrik wrote:
> Ist dann das strcpy() in der Funktion, die das struct zurückliefern
> soll, überhaupt nötig?

Ein test_menu Eintrag ist vom Datentyp menu_entry_t ?

Wenn ja, dann ist das nicht nötig. Strukturen sind, anders
als Arrays, zuweisbar und man hätte die Funktion auch so
schreiben können:
1
menu_entry_t get_menu_entry(uint8_t id){
2
  .... // ev. hier Fehlerabfrage der id
3
  return test_menu[id];
4
}

Struktur Elemente verhalten sich in C, so wie man sich das
vorstellt: Sie können ohne Probleme als Ganzes zugewiesen
werden und taugen daher auch als 'Call by Value' Argumente
bzw. Returnwerte.

Lediglich Arrays sind in C die große Ausnahme.

Edit:  Allerdings sollte man immer Bedenken, daß hier tatsächlich
ein Kopiervorgang stattfindet. Man könnte sich daher überlegen,
ob man den Prozessor nicht entlasten möchte und ganz einfach
einen Pointer retourniert. Dsa hätte dann auch den Vorteil, daß
sich der Fehlerfall durch die Rückgabe von NULL elegant erledigen
lässt.

von Henrik (Gast)


Lesenswert?

Wenn ich einen Zeiger zurückgeben möchte, dann kann ich die Variable res 
aber nicht in der Funktion deklarieren, oder? Weil dann ist sie doch nur 
lokal, und somit ist die Adresse, die ich auf dieses struct zurückgeben 
würde, doch außerhalb der Funktion nicht unbedingt gültig.

MfG
Henrik

von Karl H. (kbuchegg)


Lesenswert?

Henrik wrote:
> Wenn ich einen Zeiger zurückgeben möchte, dann kann ich die Variable res
> aber nicht in der Funktion deklarieren, oder? Weil dann ist sie doch nur
> lokal, und somit ist die Adresse, die ich auf dieses struct zurückgeben
> würde, doch außerhalb der Funktion nicht unbedingt gültig.

Das ist richtig.

Solange du aber nur einen menu_entry_t aus deinem Array
zurückliefern willst ohne ihn zb. zu modifizieren, benötigst
du die Variable 'res' aber auch gar nicht.
1
menu_entry_t* get_menu_entry(uint8_t id)
2
{
3
  if( id > MAX_NR_ENTRIES )
4
    return NULL;
5
6
  return &test_menu[id];
7
}

kniffliger wird es, wenn der Eintrag aus dem Array vor der
Rückgabe noch modifiziert werden soll. Dann, wie du richtig
sagst, geht das so nicht.
Dann musst du dich entscheiden: Wenn die Funktion einen Fehler
in id nicht melden muss (weil du zb fehlerfrei programmierst)
dann geht:
1
menu_entry_t get_menu_entry(uint8_t id){
2
  menu_entry_t res;
3
4
  res = test_menu[id];
5
6
  res.type = WAS_WEIS_ICH_WAS;
7
8
  return res;
9
}

Beachte: Ich lasse zuerst die komplette Struktur zuweisen
und modifiziere erst dann den einen Wert. Das ist grundsätzlich
besser, als alle Werte einzeln zu kopieren. Schon alleine aus
dem Grund, weil ich keinen Struktur Member vergessen kann.
1
  res = test_menu[id];
bedeutet: kopiere das komplette Strukturobjekt, egal welche
und wieviele Member das sind. Bei einer Strukturänderung ( zb.
Member kommt hinzu) muss ich daher nicht darauf achten, dass
diese Funktion diese Änderung berücksichtigen muss. Der Compiler
kümmert sich darum, dass auch wirklich das komplette Struktur-Objekt
kopiert wird.

Wenn die Funktion aber in der Lage sein soll, einen Fehler zu
melden, dann macht man das so, dass die Funktion bereits
einen Eingangspointer erwartet, der auf eine Variable zeigen
muss, in dem die Funktion das Ergebnis ablegen soll. Zusätzlich
benutzt man dann den Returnwert um Erfolg oder Misserfolg anzuzeigen:
1
menu_entry_t* get_menu_entry(uint8_t id, menu_entry_t* res)
2
{
3
  if( id > MAX_NR_ENTRIES )
4
    return NULL;
5
6
  *res = test_menu[id];
7
  res->type = WAS_WEIS_ICH_WAS;
8
9
  return res;
10
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.