Unterschied zwischen der Übergabe von Arrays und Array-Zeigern an Funktionen in C

Lesezeit: 3 Minuten

Was ist der Unterschied zwischen den beiden Funktionen in C?

void f1(double a[]) {
   //...
}

void f2(double *a) {
   //...
}

Wenn ich die Funktionen in einem sehr langen Array aufrufen würde, würden sich diese beiden Funktionen anders verhalten, würden sie mehr Platz auf dem Stapel einnehmen?

Zuerst einige Standardesisch:

6.7.5.3 Funktionsdeklaratoren (einschließlich Prototypen)


7 Eine Deklaration eines Parameters als „array of Typ” soll auf ”qualifizierter Zeiger auf angepasst werden
Typ”, wobei die Typqualifizierer (falls vorhanden) diejenigen sind, die in der angegeben sind [ and ] der Array-Typ-Ableitung. Wenn das Schlüsselwort static erscheint auch innerhalb der [ and ] der Array-Typ-Ableitung, dann muss für jeden Aufruf der Funktion der Wert des entsprechenden tatsächlichen Arguments den Zugriff auf das erste Element eines Arrays mit mindestens so vielen Elementen ermöglichen, wie durch den Größenausdruck angegeben.

Kurz gesagt, jeder Funktionsparameter, der als deklariert ist T a[] oder T a[N] wird behandelt als ob es wurde erklärt T *a.

Warum werden Array-Parameter also so behandelt, als wären sie als Zeiger deklariert? Hier ist der Grund:

6.3.2.1 Lvalues, Arrays und Funktionsbezeichner


3 Außer wenn es der Operand von ist sizeof Operator oder das Unäre & -Operator oder ist ein Zeichenfolgenliteral, das zum Initialisieren eines Arrays verwendet wird, eines Ausdrucks vom Typ „Array of Typ” wird in einen Ausdruck vom Typ ”Zeiger auf Typ”, das auf das Anfangselement des Array-Objekts zeigt und kein Lvalue ist. Wenn das Array-Objekt eine Registerspeicherklasse hat, ist das Verhalten undefiniert.

Angesichts des folgenden Codes:

int main(void)
{
  int arr[10];
  foo(arr);
  ...
}

Im Aufruf an fooder Array-Ausdruck arr ist auch kein Operand von sizeof oder &also wird sein Typ implizit von “10-Element-Array von int” bis “Zeiger auf int” nach 6.2.3.1/3. Somit gilt foo erhält einen Zeigerwert anstelle eines Array-Werts.

Aufgrund von 6.7.5.3/7 können Sie schreiben foo wie

void foo(int a[]) // or int a[10]
{
  ...
}

aber es wird interpretiert als

void foo(int *a)
{
  ...
}

Somit sind die beiden Formen identisch.

Der letzte Satz in 6.7.5.3/7 wurde mit C99 eingeführt und bedeutet im Grunde, dass wenn Sie eine Parameterdeklaration wie

void foo(int a[static 10])
{
  ...
}

der tatsächliche Parameter entspricht a muss ein Array mit sein wenigstens 10 Elemente.

  • Es gibt einen Unterschied bei der Verwendung (zumindest einiger älterer) MSVC-C++-Compiler, da der Compiler den Funktionsnamen in beiden Fällen fälschlicherweise anders verstümmelt (während er erkennt, dass sie ansonsten gleich sind), was zu Verknüpfungsproblemen führt. Siehe Fehlerbericht „Wird nicht behoben“ hier connect.microsoft.com/VisualStudio/feedback/details/326874/…

    – Gregor

    12. Dezember 2013 um 17:02 Uhr

Benutzeravatar von Thomas Pornin
Thomas Pornin

Der Unterschied ist rein syntaktisch. Wenn in C die Array-Notation für einen Funktionsparameter verwendet wird, wird sie automatisch in eine Zeigerdeklaration umgewandelt.

  • @Kaushik: Obwohl sie in diesem Fall gleich sind, denken Sie daran, dass sie im allgemeinen Fall nicht gleich sind

    – BlueRaja – Danny Pflughoeft

    6. April 2011 um 21:55 Uhr


  • @BlueRaja: ja, es ist eine der Fallstricke von C. Die Deklaration von Funktionsparametern ist sehr ähnlich zur Deklaration lokaler Variablen, aber es gibt ein paar subtile Unterschiede (wie diese automatische Array-zu-Zeiger-Transformation), die den unvorsichtigen Programmierer leicht stören können.

    – Thomas Pornin

    7. April 2011 um 12:07 Uhr

Benutzeravatar von caltuntas
Caluntas

Nein, es gibt keinen Unterschied zwischen ihnen. Zum Testen habe ich diesen C-Code im Dev C++(mingw)-Compiler geschrieben:

#include <stdio.h>

void function(int* array) {
     int a =5;
}

void main() {  
     int array[]={2,4};
     function(array);
     getch();
}

Wenn ich zerlege hauptsächlich Funktion in .exe beider aufrufenden Versionen der Binärdatei in IDA bekomme ich genau den gleichen Assembler-Code wie unten:

push    ebp
mov     ebp, esp
sub     esp, 18h
and     esp, 0FFFFFFF0h
mov     eax, 0
add     eax, 0Fh
add     eax, 0Fh
shr     eax, 4
shl     eax, 4
mov     [ebp+var_C], eax
mov     eax, [ebp+var_C]
call    sub_401730
call    sub_4013D0
mov     [ebp+var_8], 2
mov     [ebp+var_4], 4
lea     eax, [ebp+var_8]
mov     [esp+18h+var_18], eax
call    sub_401290
call    _getch
leave
retn

Es gibt also keinen Unterschied zwischen den beiden Versionen dieses Aufrufs, zumindest bedroht der Compiler sie gleichermaßen.

  • Entschuldigung, aber dies beweist nur, dass einige Versionen von gcc für beide die gleiche Assembly auf x86 generieren. Richtige Antwort, falsche Erklärung.

    – Lambdapower

    1. Oktober 2012 um 22:16 Uhr

1423140cookie-checkUnterschied zwischen der Übergabe von Arrays und Array-Zeigern an Funktionen in C

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

Privacy policy