Übergeben eines Arrays als Argument an eine Funktion in C

Lesezeit: 13 Minuten

Benutzeravatar von Mohan Mahajan
Mohan Mahajan

Ich habe eine Funktion geschrieben, die ein Array als Argument enthält, und rufe sie auf, indem ich den Wert des Arrays wie folgt übergebe.

void arraytest(int a[])
{
    // changed the array a
    a[0] = a[0] + a[1];
    a[1] = a[0] - a[1];
    a[0] = a[0] - a[1];
}

void main()
{
    int arr[] = {1, 2};
    printf("%d \t %d", arr[0], arr[1]);
    arraytest(arr);
    printf("\n After calling fun arr contains: %d\t %d", arr[0], arr[1]);
}

Was ich gefunden habe ist, obwohl ich anrufe arraytest() Funktion durch Übergabe von Werten, die ursprüngliche Kopie von int arr[] ist geändert.

Können Sie bitte erklären, warum?

  • Sie übergeben das Array als Referenz, aber Sie ändern seinen Inhalt – daher sehen Sie eine Änderung in den Daten

    – Shaun Wilde

    4. Juli 2011 um 5:58 Uhr

  • main() muss zurückkehren int.

    – Unterstrich_d

    11. Juni 2020 um 9:02 Uhr

  • Hier ist die natürliche Erweiterung dieser Frage: Wie man ein mehrdimensionales Array an eine Funktion in C und C++ übergibt. Und hier sind einige meiner Herangehensweisen an dieses Problem.

    – Gabriel Staples

    4. Juni 2021 um 19:01 Uhr


Bei der Übergabe eines Arrays als Parameter wird this

void arraytest(int a[])

bedeutet genau dasselbe wie

void arraytest(int *a)

also du sind Ändern der Werte in main.

Aus historischen Gründen sind Arrays keine Bürger erster Klasse und können nicht als Wert übergeben werden.

  • Welche Notation ist unter welchen Umständen besser?

    – Heberto Mayorquin

    4. September 2015 um 12:07 Uhr

  • @Ramon – Ich würde die zweite Option verwenden, da sie weniger verwirrend erscheint und besser darauf hinweist, dass Sie keine Kopie des Arrays erhalten.

    – Bo Persson

    4. September 2015 um 12:20 Uhr

  • Können Sie die “historischen Gründe” erklären? Ich nehme an, dass das Übergeben von Werten eine Kopie und damit eine Verschwendung von Speicher erfordern würde. Danke

    – Jacquelyn.Marquardt

    1. November 2016 um 20:15 Uhr

  • @lucapozzobon – Ursprünglich hatte C außer Einzelwerten keinen Pass-by-Wert. Es war nicht bis struct wurde der Sprache hinzugefügt, dass dies geändert wurde. Und dann wurde es als zu spät angesehen, die Regeln für Arrays zu ändern. Es gab bereits 10 Benutzer. 🙂

    – Bo Persson

    2. November 2016 um 9:26 Uhr

  • …bedeutet genau dasselbe wie void arraytest(int a[1000]) etc etc. Erweiterte Antwort hier: stackoverflow.com/a/51527502/4561887.

    – Gabriel Staples

    25. Juli 2018 um 20:56 Uhr

Benutzeravatar von Gabriel Staples
Gabriel Staples

Um stattdessen 2D- (oder höher multidimensionale) Arrays zu übergeben, siehe meine anderen Antworten hier:

  1. So übergeben Sie eine multidimensionale [C-style] Array zu einer Funktion in C und C++, und hier:
  2. So übergeben Sie ein mehrdimensionales Array nur in C++ an eine Funktion, via std::vector<std::vector<int>>&

Übergeben von 1D-Arrays als Funktionsparameter in C (und C++)

1. Standard-Array-Verwendung in C mit natürlichem Typzerfall (Anpassung) von Array zu Ptr

@Bo Persson sagt in seiner großartigen Antwort hier richtig:

Bei der Übergabe eines Arrays als Parameter wird this

void arraytest(int a[])

bedeutet genau dasselbe wie

void arraytest(int *a)

Lassen Sie mich einige Kommentare hinzufügen, um diese beiden Codeausschnitte klarer zu machen:

// param is array of ints; the arg passed automatically "adjusts" (frequently said
// informally as "decays") from `int []` (array of ints) to `int *` 
// (ptr to int)
void arraytest(int a[])

// ptr to int
void arraytest(int *a)

Lassen Sie mich jedoch auch hinzufügen, dass die beiden obigen Formen auch:

  1. genau dasselbe bedeuten wie

     // array of 0 ints; automatically adjusts (decays) from `int [0]`
     // (array of zero ints) to `int *` (ptr to int)
     void arraytest(int a[0])
    
  2. was genau dasselbe bedeutet wie

     // array of 1 int; automatically adjusts (decays) from `int [1]`
     // (array of 1 int) to `int *` (ptr to int)
     void arraytest(int a[1])
    
  3. was genau dasselbe bedeutet wie

     // array of 2 ints; automatically adjusts (decays) from `int [2]`
     // (array of 2 ints) to `int *` (ptr to int)
     void arraytest(int a[2])
    
  4. was genau dasselbe bedeutet wie

     // array of 1000 ints; automatically adjusts (decays) from `int [1000]`
     // (array of 1000 ints) to `int *` (ptr to int)
     void arraytest(int a[1000])
    
  5. usw.

In jedem einzelnen der obigen Array-Beispiele und wie in den Beispielaufrufen im Code direkt unten gezeigt, passt sich der Typ des Eingabeparameters an an an (zerfällt). int *und kann ohne Warnungen und ohne Fehler aufgerufen werden, sogar mit Build-Optionen -Wall -Wextra -Werror eingeschaltet (vgl mein Repo hier für Details zu diesen 3 Build-Optionen), wie folgt:

int array1[2];
int * array2 = array1;

// works fine because `array1` automatically decays from an array type
// to a pointer type: `int *`
arraytest(array1);
// works fine because `array2` is already an `int *` 
arraytest(array2);

Tatsächlich ist der Wert “Größe” ([0], [1], [2], [1000]usw.) innerhalb des Array-Parameters hier dient anscheinend nur ästhetischen/Selbstdokumentationszwecken und kann jede positive Ganzzahl sein (size_t Typ, denke ich) Sie wollen!

In der Praxis sollten Sie es jedoch verwenden, um die Mindestgröße des Arrays anzugeben, das Sie von der Funktion erwarten, damit Sie es beim Schreiben von Code leicht nachverfolgen und überprüfen können. Das MISRA-C-2012 Standart (Kaufen/laden Sie hier die 236-Seiten-PDF-Version 2012 des Standards für £15,00 herunter) geht so weit zu sagen (Hervorhebung hinzugefügt):

Regel 17.5 Das Funktionsargument, das einem als Array-Typ deklarierten Parameter entspricht, muss eine angemessene Anzahl von Elementen haben.

Wenn ein Parameter als Array mit einer bestimmten Größe deklariert wird, sollte das entsprechende Argument in jedem Funktionsaufruf auf ein Objekt zeigen, das mindestens so viele Elemente wie das Array hat.

Die Verwendung eines Array-Deklarators für einen Funktionsparameter spezifiziert die Funktionsschnittstelle eindeutiger als die Verwendung eines Zeigers. Die Mindestanzahl der von der Funktion erwarteten Elemente wird explizit angegeben, während dies bei einem Zeiger nicht möglich ist.

Mit anderen Worten, sie empfehlen die Verwendung des expliziten Größenformats, obwohl der C-Standard dies technisch nicht erzwingt —Es hilft zumindest Ihnen als Entwickler und anderen, die den Code verwenden, zu verdeutlichen, welche Größe des Arrays die Funktion von Ihnen erwartet.


2. Typsicherheit auf Arrays in C erzwingen

(Nicht empfohlen (Korrektur: manchmal empfohlen, insbesondere für mehrdimensionale Arrays mit fester Größe), aber möglich. Siehe mein kurzes Argument dagegen am Ende. Auch für mein mehrdimensionales Array [ex: 2D array] Version davon, siehe meine Antwort hier.)

Wie @Winger Sendon in einem Kommentar unter meiner Antwort hervorhebt, können wir C zwingen, ein Array zu behandeln Typ je nach Array unterschiedlich sein Größe!

Zuerst müssen Sie das in meinem Beispiel oben erkennen, indem Sie die verwenden int array1[2]; so was: arraytest(array1); verursacht array1 automatisch in ein zerfallen int *. JEDOCH, wenn du die nimmst Adresse von array1 stattdessen und rufen Sie an arraytest(&array1)erhalten Sie ein völlig anderes Verhalten! Jetzt zerfällt es NICHT in ein int *! Dies liegt daran, wenn Sie die nehmen Adresse von ein Array dann Sie schon haben einen Zeigertyp, und Zeigertypen passen sich NICHT an andere Zeigertypen an. Nur Array-Typen passen sich an Zeigertypen an. Also stattdessen die Art von &array1 ist int (*)[2]was bedeutet “Zeiger auf ein Array der Größe 2 von int”oder “Zeiger auf ein Array der Größe 2 vom Typ int”oder sagte auch als “Zeiger auf ein Array von 2 Ints”. Sie können also C zwingen, die Typsicherheit eines Arrays zu überprüfen, indem Sie explizite Zeiger auf Arrays übergeben, wie folgt:

// `a` is of type `int (*)[2]`, which means "pointer to array of 2 ints"; 
// since it is already a ptr, it can NOT automatically decay further
// to any other type of ptr 
void arraytest(int (*a)[2])
{
    // my function here
}

Diese Syntax ist schwer zu lesen, ähnelt aber der von a Funktionszeiger. Das Online-Tool, cdeklsagt uns das int (*a)[2] meint: “deklariere a als Zeiger auf Array 2 von int” (Zeiger auf Array von 2 ints). Verwechseln Sie dies NICHT mit der Version ohne Klammern: int * a[2]was bedeutet: “deklariere a als Array 2 des Zeigers auf int” (AKA: Array von 2 Zeiger zu intAKA: Array von 2 int*s).

Diese Funktion erfordert nun, dass Sie sie mit dem Adressoperator (&) so, indem Sie als Eingabeparameter einen POINTER TO A ARRAY OF THE CORRECTE SIZE verwenden!:

int array1[2];

// ok, since the type of `array1` is `int (*)[2]` (ptr to array of 
// 2 ints)
arraytest(&array1); // you must use the & operator here to prevent
                    // `array1` from otherwise automatically decaying
                    // into `int *`, which is the WRONG input type here!

Dies wird jedoch eine Warnung erzeugen:

int array1[2];

// WARNING! Wrong type since the type of `array1` decays to `int *`:
//      main.c:32:15: warning: passing argument 1 of ‘arraytest’ from 
//      incompatible pointer type [-Wincompatible-pointer-types]                                                            
//      main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
arraytest(array1); // (missing & operator)

Du könntest Testen Sie diesen Code hier.

Um den C-Compiler zu zwingen, diese Warnung in einen Fehler umzuwandeln, MÜSSEN Sie immer aufrufen arraytest(&array1); Verwenden Sie nur ein Eingabearray der korrekten Größe und Typ (int array1[2]; in diesem Fall), hinzufügen -Werror zu Ihren Build-Optionen. Wenn Sie den obigen Testcode auf onlinegdb.com ausführen, tun Sie dies, indem Sie auf das Zahnradsymbol oben rechts klicken und auf „Extra Compiler Flags“ klicken, um diese Option einzugeben. Jetzt diese Warnung:

main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]                                                            
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’    

wird sich in diesen Build-Fehler verwandeln:

main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
     arraytest(array1); // warning!
               ^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
 void arraytest(int (*a)[2])
      ^~~~~~~~~
cc1: all warnings being treated as errors

Beachten Sie, dass Sie auch “typsichere” Zeiger auf Arrays einer bestimmten Größe erstellen können, wie folgt:

int array[2]; // variable `array` is of type `int [2]`, or "array of 2 ints"

// `array_p` is a "type safe" ptr to array of size 2 of int; ie: its type
// is `int (*)[2]`, which can also be stated: "ptr to array of 2 ints"
int (*array_p)[2] = &array;

…Aber ich mache nicht Notwendig empfehle dies (unter Verwendung dieser “typsicheren” Arrays in C), da es mich sehr an die C++-Mätzchen erinnert, die verwendet werden, um Typsicherheit überall zu erzwingen, zu den außergewöhnlich hohen Kosten der Sprachsyntaxkomplexität, Ausführlichkeit und Schwierigkeit bei der Architektur von Code und welche Ich mag es nicht und habe schon oft darüber geschimpft (z. B.: siehe “Meine Gedanken zu C++” hier).


Weitere Tests und Experimente finden Sie auch unter dem Link unten.

Verweise

Siehe Links oben. Ebenfalls:

  1. Meine Code-Experimente online: https://onlinegdb.com/B1RsrBDFD

Siehe auch:

  1. Meine Antwort zu mehrdimensionalen Arrays (z. B. 2D-Arrays), die das oben Gesagte erläutert und den Ansatz der “Typsicherheit” für mehrdimensionale Arrays verwendet, wo es sinnvoll ist: So übergeben Sie ein mehrdimensionales Array an eine Funktion in C und C++

  • void arraytest(int (*a)[1000]) ist besser, weil der Compiler dann Fehler macht, wenn die Größe falsch ist.

    – Flügelspieler

    31. Juli 2018 um 15:10 Uhr

  • @WingerSendon, ich wusste, dass ich hier einige Feinheiten überprüfen musste, und dass die Syntax verwirrend ist (wie die Syntax einer Funktion ptr verwirrend ist), also habe ich mir Zeit genommen und meine Antwort endlich mit einem großen neuen Abschnitt mit dem Titel aktualisiert Forcing type safety on arrays in Cum Ihren Punkt abzudecken.

    – Gabriel Staples

    9. November 2020 um 22:35 Uhr

  • @GabrielStaples, danke. Ihre Antwort ist sehr hilfreich. Können Sie mir eine Referenz nennen, um fortgeschrittenes c auf diese Weise zu lernen?

    – daryooosh

    15. Dezember 2020 um 4:39 Uhr


  • @daryooosh, leider kann ich nicht. Ich habe keine großartigen Referenzen. Ich habe das ein bisschen hier, ein bisschen dort aufgegriffen, indem ich über viele Jahre tief gegraben habe. Das Beste, was ich tun kann, ist Ihnen zu sagen, dass ich gelegentlich etwas von dem, was ich so lerne, in meine fallen lasse eRCaGuy_hello_world Repo hier. Denken Sie jedoch daran, dass das oben verwendete C-Typ-Sicherheitsmaterial SEHR sparsam verwendet werden sollte. Es wird Ihren Code verkomplizieren und die Lesbarkeit erheblich verringern, und es lohnt sich nicht. Konzentrieren Sie sich wo möglich auf eine einfache Syntax und machen Sie die Dinge lesbar.

    – Gabriel Staples

    15. Dezember 2020 um 5:29 Uhr


  • Beachten Sie auch, dass das kanonische klassische C-Lehrbuch dieses K&R ist Die Programmiersprache C Buchen: en.wikipedia.org/wiki/The_C_Programming_Language.

    – Gabriel Staples

    15. Dezember 2020 um 5:30 Uhr

Wenn Sie wollen Übergeben Sie ein eindimensionales Array als Argument in einer Funktionmüssten Sie einen formalen Parameter auf eine der folgenden drei Arten deklarieren, und alle drei Deklarationsmethoden führen zu ähnlichen Ergebnissen, da jede teilt dem Compiler mit, dass ein ganzzahliger Zeiger empfangen wird.

int func(int arr[], ...){
    .
    .
    .
}

int func(int arr[SIZE], ...){
    .
    .
    .
}

int func(int* arr, ...){
    .
    .
    .
}

Sie ändern also die ursprünglichen Werte.

Vielen Dank !!!

  • Ich habe nach Ihrem zweiten Beispiel gesucht. Können Sie die Vorteile der einzelnen Methoden erläutern?

    – Pucken

    16. Juni 2020 um 7:16 Uhr

Benutzeravatar von Andrushenko Alexander
Andruschenko Alexander

Übergeben eines mehrdimensionalen Arrays als Argument an eine Funktion.
Das Übergeben eines Ein-Dim-Arrays als Argument ist mehr oder weniger trivial. Werfen wir einen Blick auf einen interessanteren Fall der Übergabe eines 2-Dim-Arrays. In C können Sie kein Zeiger-zu-Zeiger-Konstrukt verwenden (int **) anstelle von 2 Dim-Arrays. Machen wir ein Beispiel:

void assignZeros(int(*arr)[5], const int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 5; j++) {
            *(*(arr + i) + j) = 0;
            // or equivalent assignment
            arr[i][j] = 0;
        }
    }

Hier habe ich eine Funktion spezifiziert, die als erstes Argument einen Zeiger auf ein Array von 5 ganzen Zahlen nimmt. Ich kann als Argument jedes 2-Dim-Array mit 5 Spalten übergeben:

int arr1[1][5]
int arr1[2][5]
...
int arr1[20][5]
...

Sie könnten auf die Idee kommen, eine allgemeinere Funktion zu definieren, die jedes beliebige 2-Dim-Array akzeptieren und die Funktionssignatur wie folgt ändern kann:

void assignZeros(int ** arr, const int rows, const int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            *(*(arr + i) + j) = 0;
        }
    }
}

Dieser Code würde kompilieren, aber Sie erhalten einen Laufzeitfehler, wenn Sie versuchen, die Werte auf die gleiche Weise wie in der ersten Funktion zuzuweisen. In C sind also mehrdimensionale Arrays nicht dasselbe wie Zeiger auf Zeiger … auf Zeiger. Ein int(*arr)[5] ist ein Zeiger auf ein Array aus 5 Elementen, an int(*arr)[6] ist ein Zeiger auf ein Array von 6 Elementen, und sie sind Zeiger auf verschiedene Typen!

Nun, wie definiert man Funktionsargumente für höhere Dimensionen? Einfach, wir folgen einfach dem Muster! Hier ist die gleiche Funktion angepasst, um ein Array von 3 Dimensionen zu nehmen:

void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) {
    for (int i = 0; i < dim1; i++) {
        for (int j = 0; j < dim2; j++) {
            for (int k = 0; k < dim3; k++) {
                *(*(*(arr + i) + j) + k) = 0;
                // or equivalent assignment
                arr[i][j][k] = 0;
            }
        }
    }
}

Wie zu erwarten, kann es als Argument beliebige 3-Dim-Arrays verwenden, die in der zweiten Dimension 4 Elemente und in der dritten Dimension 5 Elemente haben. So etwas wäre in Ordnung:

arr[1][4][5]
arr[2][4][5]
...
arr[10][4][5]
...

Aber wir müssen alle Maßgrößen bis auf die erste angeben.

Benutzeravatar von fyr
fyr

Sie übergeben das Array nicht als Kopie. Es ist nur ein Zeiger, der auf die Adresse zeigt, an der sich das erste Element des Arrays im Speicher befindet.

Benutzeravatar von ob_dev
ob_dev

Sie übergeben die Adresse des ersten Elements des Arrays

Sie übergeben den Wert des Speicherplatzes des ersten Mitglieds des Arrays.

Wenn Sie also beginnen, das Array innerhalb der Funktion zu ändern, ändern Sie das ursprüngliche Array.

Erinnere dich daran a[1] ist *(a+1).

  • Ich nehme an, es fehlen () für * a + 1 sollte * (a + 1) sein

    – Shin Takezou

    4. Juli 2011 um 6:17 Uhr

  • @Shin Danke, es ist schon eine Weile her, seit ich mit C gespielt habe.

    – Alex

    4. Juli 2011 um 6:18 Uhr

1422420cookie-checkÜbergeben eines Arrays als Argument an eine Funktion in C

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

Privacy policy