Wie gebe ich mehrere Werte von einer Funktion in C zurück?

Lesezeit: 8 Minuten

Benutzeravatar von Tony Stark
Tony Stark

Wenn ich eine Funktion habe, die ein Ergebnis erzeugt int und ein Ergebnis stringwie gebe ich sie beide von einer Funktion zurück?

Soweit ich das beurteilen kann, kann ich nur eine Sache zurückgeben, die durch den Typ bestimmt wird, der dem Funktionsnamen vorangestellt ist.

  • Durch string meinst du “Ich benutze C++ und das ist die std::string Klasse” oder “Ich verwende C und dies ist eine char * Zeiger bzw char[] Anordnung.”

    – Chris Lutz

    12. April 2010 um 6:15 Uhr

  • Nun, in meinem speziellen Fall waren es zwei Ints: eine für die „Punktzahl“ dessen, was ich verglichen habe, und eine für den „Index“, wo diese maximale Punktzahl gefunden wurde. Ich wollte hier nur für den allgemeineren Fall ein String-Beispiel verwenden

    – Tony Stark

    12. April 2010 um 6:22 Uhr

  • Übergeben Sie die Zeichenfolge als Referenz und geben Sie das int zurück. Schnellste Weg. Keine Strukturen erforderlich.

    – Stefan Steiger

    12. April 2010 um 7:34 Uhr


  • Tut eine Funktion, die 2 Ergebnisse zurückgibt, nicht mehr als eine Sache? Was würde Onkel Bob sagen?

    – Duncan

    12. April 2010 um 7:37 Uhr

Benutzeravatar von Travis Gockel
Travis Göckel

Ich weiß nicht, was du bist string ist, aber ich gehe davon aus, dass es seinen eigenen Speicher verwaltet.

Sie haben zwei Lösungen:

1: Rückkehr a struct die alle Typen enthält, die Sie benötigen.

struct Tuple {
    int a;
    string b;
};

struct Tuple getPair() {
    Tuple r = { 1, getString() };
    return r;
}

void foo() {
    struct Tuple t = getPair();
}

2: Verwenden Sie Zeiger, um Werte zu übergeben.

void getPair(int* a, string* b) {
    // Check that these are not pointing to NULL
    assert(a);
    assert(b);
    *a = 1;
    *b = getString();
}

void foo() {
    int a, b;
    getPair(&a, &b);
}

Welche Sie wählen, hängt weitgehend von Ihren persönlichen Vorlieben ab, welche Semantik Ihnen besser gefällt.

  • Ich denke, es hängt mehr davon ab, wie verwandt die Rückgabewerte sind. Wenn int ein Fehlercode und der String ein Ergebnis ist, dann sollten diese nicht in einer Struktur zusammengefügt werden. Das ist einfach albern. In diesem Fall würde ich das int zurückgeben und den String als a übergeben char * und ein size_t für die Länge, es sei denn, es ist absolut wichtig, dass die Funktion ihre eigene Zeichenfolge zuweist und / oder zurückgibt NULL.

    – Chris Lutz

    12. April 2010 um 6:14 Uhr

  • @Chris Ich stimme dir vollkommen zu, aber ich habe keine Ahnung von der Verwendungssemantik der Variablen, die er benötigt.

    – Travis Gockel

    12. April 2010 um 6:16 Uhr

  • Guter Punkt Chris. Eine andere Sache, die meiner Meinung nach erwähnenswert ist, ist Wert vs. Referenz. Wenn ich mich nicht irre, bedeutet die Rückgabe der Struktur wie im Beispiel gezeigt, dass bei der Rückgabe eine Kopie erstellt wird, ist das richtig? (Ich bin ein bisschen wackelig in C) Während die andere Methode Pass-by-Reference verwendet und daher nicht mehr Speicher zuweisen muss. Natürlich könnte die Struktur über einen Zeiger zurückgegeben werden und denselben Nutzen haben, oder? (Stellen Sie sicher, dass Sie den Speicher richtig zuweisen und all das natürlich)

    – RTHarston

    9. Februar 2019 um 6:58 Uhr

  • @BobVicktor: C hat überhaupt keine Referenzsemantik (das ist exklusiv für C ++), also ist alles ein Wert. Die Zwei-Zeiger-Lösung (Nr. 2) besteht darin, Kopien der Zeiger an eine Funktion zu übergeben, die getPair dann Dereferenzen. Je nachdem, was Sie tun (OP hat nie geklärt, ob dies tatsächlich eine C-Frage ist), kann die Zuweisung ein Problem darstellen, befindet sich jedoch normalerweise nicht im C++-Land (Rückgabewertoptimierung speichert all dies) und im C-Land, Daten Kopien passieren normalerweise explizit (via strncpy oder Wasauchimmer).

    – Travis Gockel

    12. Februar 2019 um 0:41 Uhr

  • @TravisGockel Danke für die Korrektur. Ich bezog mich auf die Tatsache, dass Zeiger verwendet werden, also werden die Werte nicht kopiert, sondern nur das geteilt, was bereits zugewiesen wurde. Aber Sie haben Recht, wenn Sie sagen, dass dies in C nicht richtig als Pass-by-Reference bezeichnet wird. Und danke auch für die anderen großartigen Wissensnuggets. Ich liebe es, diese kleinen Dinge über Sprachen zu lernen. 🙂

    – RTHarston

    22. Oktober 2019 um 21:51 Uhr


Benutzeravatar von codaddict
codaddict

Option 1: Deklarieren Sie eine Struktur mit einem int und einem String und geben Sie eine Strukturvariable zurück.

struct foo {    
 int bar1;
 char bar2[MAX];
};

struct foo fun() {
 struct foo fooObj;
 ...
 return fooObj;
}

Option 2: Sie können einen der beiden per Zeiger übergeben und Änderungen am aktuellen Parameter durch den Zeiger vornehmen und den anderen wie gewohnt zurückgeben:

int fun(char **param) {
 int bar;
 ...
 strcpy(*param,"....");
 return bar;
}

oder

 char* fun(int *param) {
 char *str = /* malloc suitably.*/
 ...
 strcpy(str,"....");
 *param = /* some value */
 return str;
}

Option 3: Ähnlich wie bei Option 2. Sie können sowohl per Pointer übergeben als auch nichts von der Funktion zurückgeben:

void fun(char **param1,int *param2) {
 strcpy(*param1,"....");
 *param2 = /* some calculated value */
}

  • Bei Option 2 sollten Sie auch die Länge der Zeichenfolge übergeben. int fun(char *param, size_t len)

    – Chris Lutz

    12. April 2010 um 6:16 Uhr

  • Das hängt jetzt davon ab, was die Funktion macht. Wenn wir wissen, welche Art von Ergebnis die Funktion in das char-Array schreibt, könnten wir einfach genug Platz dafür zuweisen und es an die Funktion übergeben. Keine Notwendigkeit, die Länge zu übergeben. Etwas Ähnliches wie wir verwenden strcpy zum Beispiel.

    – codaddict

    12. April 2010 um 6:31 Uhr

Benutzeravatar von Jesse Beder
Jess Beder

Da einer Ihrer Ergebnistypen ein String ist (und Sie C und nicht C++ verwenden), empfehle ich, Zeiger als Ausgabeparameter zu übergeben. Verwenden:

void foo(int *a, char *s, int size);

und nenne es so:

int a;
char *s = (char *)malloc(100); /* I never know how much to allocate :) */
foo(&a, s, 100);

Generell sollte man die Zuordnung lieber in der vornehmen Berufung Funktion, nicht innerhalb der Funktion selbst, damit Sie möglichst offen für unterschiedliche Allokationsstrategien sein können.

Benutzeravatar von zs2020
zs2020

Erstellen Sie eine Struktur und legen Sie zwei Werte darin fest und geben Sie die Strukturvariable zurück.

struct result {
    int a;
    char *string;
}

Sie müssen Platz für die zuweisen char * in deinem Programm.

Zwei unterschiedliche Ansätze:

  1. Übergeben Sie Ihre Rückgabewerte per Zeiger und ändern Sie sie innerhalb der Funktion. Sie deklarieren Ihre Funktion als void, aber sie kehrt über die als Zeiger übergebenen Werte zurück.
  2. Definieren Sie eine Struktur, die Ihre Rückgabewerte aggregiert.

Ich denke, dass #1 etwas offensichtlicher ist, was vor sich geht, obwohl es langweilig werden kann, wenn Sie zu viele Rückgabewerte haben. In diesem Fall funktioniert Option Nr. 2 ziemlich gut, obwohl die Erstellung spezieller Strukturen für diesen Zweck mit einigem mentalen Aufwand verbunden ist.

  • C hat keine Referenzen ;-), obwohl seit dem verwendeten Poster stringes könnte sicher sein, C ++ anzunehmen …

    – Travis Gockel

    12. April 2010 um 6:11 Uhr


  • Das ganz vergessen! Ich habe meine Antwort geändert, um Zeiger zu verwenden, aber ich war eindeutig zu lange im C++-Land. 🙂

    – James Thompson

    12. April 2010 um 6:55 Uhr

Benutzeravatar von Bloodlee
Bloodlee

Verwenden Sie Zeiger als Funktionsparameter. Verwenden Sie sie dann, um mehrere Werte zurückzugeben.

  • C hat keine Referenzen ;-), obwohl seit dem verwendeten Poster stringes könnte sicher sein, C ++ anzunehmen …

    – Travis Gockel

    12. April 2010 um 6:11 Uhr


  • Das ganz vergessen! Ich habe meine Antwort geändert, um Zeiger zu verwenden, aber ich war eindeutig zu lange im C++-Land. 🙂

    – James Thompson

    12. April 2010 um 6:55 Uhr

Ein Ansatz ist die Verwendung von Makros. Platzieren Sie dies in einer Header-Datei multitype.h

#include <stdlib.h>

/* ============================= HELPER MACROS ============================= */

/* __typeof__(V) abbreviation */

#define TOF(V) __typeof__(V)

/* Expand variables list to list of typeof and variable names */

#define TO3(_0,_1,_2,_3) TOF(_0) v0; TOF(_1) v1; TOF(_2) v2; TOF(_3) v3;
#define TO2(_0,_1,_2)    TOF(_0) v0; TOF(_1) v1; TOF(_2) v2;
#define TO1(_0,_1)       TOF(_0) v0; TOF(_1) v1;
#define TO0(_0)          TOF(_0) v0;

#define TO_(_0,_1,_2,_3,TO_MACRO,...) TO_MACRO

#define TO(...) TO_(__VA_ARGS__,TO3,TO2,TO1,TO0)(__VA_ARGS__)

/* Assign to multitype */

#define MTA3(_0,_1,_2,_3) _0 = mtr.v0; _1 = mtr.v1; _2 = mtr.v2; _3 = mtr.v3;
#define MTA2(_0,_1,_2)    _0 = mtr.v0; _1 = mtr.v1; _2 = mtr.v2;
#define MTA1(_0,_1)       _0 = mtr.v0; _1 = mtr.v1;
#define MTA0(_0)          _0 = mtr.v0;

#define MTA_(_0,_1,_2,_3,MTA_MACRO,...) MTA_MACRO

#define MTA(...) MTA_(__VA_ARGS__,MTA3,MTA2,MTA1,MTA0)(__VA_ARGS__)

/* Return multitype if multiple arguments, return normally if only one */

#define MTR1(...) {                                                           \
    typedef struct mtr_s {                                                    \
      TO(__VA_ARGS__)                                                         \
    } mtr_t;                                                                  \
    mtr_t *mtr = malloc(sizeof(mtr_t));                                       \
    *mtr = (mtr_t){__VA_ARGS__};                                              \
    return mtr;                                                               \
  }

#define MTR0(_0) return(_0)

#define MTR_(_0,_1,_2,_3,MTR_MACRO,...) MTR_MACRO

/* ============================== API MACROS =============================== */

/* Declare return type before function */

typedef void* multitype;

#define multitype(...) multitype

/* Assign return values to variables */

#define let(...)                                                              \
  for(int mti = 0; !mti;)                                                     \
    for(multitype mt; mti < 2; mti++)                                         \
      if(mti) {                                                               \
        typedef struct mtr_s {                                                \
          TO(__VA_ARGS__)                                                     \
        } mtr_t;                                                              \
        mtr_t mtr = *(mtr_t*)mt;                                              \
        MTA(__VA_ARGS__)                                                      \
        free(mt);                                                             \
      } else                                                                  \
        mt

/* Return */

#define RETURN(...) MTR_(__VA_ARGS__,MTR1,MTR1,MTR1,MTR0)(__VA_ARGS__)

Dadurch ist es möglich, bis zu vier Variablen aus einer Funktion zurückzugeben und bis zu vier Variablen zuzuweisen. Als Beispiel können Sie sie wie folgt verwenden:

multitype (int,float,double) fun() {
    int a = 55;
    float b = 3.9;
    double c = 24.15;

    RETURN (a,b,c);
}

int main(int argc, char *argv[]) {
    int x;
    float y;
    double z;

    let (x,y,z) = fun();

    printf("(%d, %f, %g\n)", x, y, z);

    return 0;
}

Das ist, was es druckt:

(55, 3.9, 24.15)

Die Lösung ist möglicherweise nicht so portabel, da sie C99 oder höher für variadische Makros und for-Statement-Variablendeklarationen erfordert. Aber ich denke, es war interessant genug, hier zu posten. Ein weiteres Problem ist, dass der Compiler Sie nicht warnt, wenn Sie ihnen die falschen Werte zuweisen, also müssen Sie vorsichtig sein.

Weitere Beispiele und eine Stack-basierte Version des Codes mit Unions sind unter my verfügbar Github-Repository.

  • Ein größeres Portabilitätsproblem ist die Nicht-Standard-Funktion __typeof__

    – MM

    19. November 2017 um 23:02 Uhr

  • Anstelle von typeof könnten Sie wahrscheinlich sizeof verwenden. Holen Sie sich die Größe der Rückgabewerte und weisen Sie ein Array zu, das groß genug ist, um sie alle zu speichern. Dann kannst du es zurückgeben.

    – Klaus. S

    21. November 2017 um 11:57 Uhr

1421310cookie-checkWie gebe ich mehrere Werte 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