web-dev-qa-db-fra.com

Différence entre tableau de passage et pointeur de tableau en fonction en C

Quelle est la différence entre les deux fonctions en C?

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

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

Si je devais appeler les fonctions sur un tableau sensiblement long, ces deux fonctions se comporteraient-elles différemment, prendraient-elles plus d'espace sur la pile?

101
Kaushik Shankar

Tout d'abord, quelques standardese :

6.7.5.3 Déclarateurs de fonctions (y compris les prototypes)
...
7 Une déclaration de paramètre comme ‘'tableau de type'' Doit être réglé sur '' 'pointeur qualifié pour type’’, Où les qualificatifs de type (le cas échéant) sont ceux spécifiés dans le [ et ] de la dérivation du type de tableau. Si le mot clé static apparaît également dans le champ [ et ] de la dérivation du type de tableau, puis, pour chaque appel à la fonction, la valeur de l'argument réel correspondant doit permettre d'accéder au premier élément d'un tableau avec au moins autant d'éléments que ceux spécifiés par l'expression de taille.

En bref, tout paramètre de fonction déclaré comme T a[] ou T a[N] est traité comme si il avait été déclaré T *a.

Alors, pourquoi les paramètres de tableau sont-ils traités comme s'ils avaient été déclarés comme des pointeurs? Voici pourquoi:

6.3.2.1 Lvalues, tableaux et indicatifs de fonctions
...
3 Sauf s'il s'agit de l'opérande de l'opérateur sizeof ou du unaire & opérateur, ou est un littéral utilisé pour initialiser un tableau, une expression de type '' tableau de type'' Est converti en une expression de type '' 'pointeur sur type’’ Qui pointe sur l’élément initial de l’objet tableau et n’est pas une valeur. Si l'objet tableau a une classe de stockage de registre, le comportement n'est pas défini.

Étant donné le code suivant:

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

Dans l'appel à foo, l'expression de tableau arr n'est pas un opérande de sizeof ou &, son type est donc implicitement converti du "tableau à 10 éléments de int" en "pointeur sur int" conformément au 6.2.3.1/3. Ainsi, foo recevra une valeur de pointeur, plutôt qu'une valeur de tableau.

En raison de 6.7.5.3/7, vous pouvez écrire foo comme

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

mais il sera interprété comme

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

Ainsi, les deux formes sont identiques.

La dernière phrase du 6.7.5.3/7 a été introduite avec C99 et signifie fondamentalement que si vous avez une déclaration de paramètre comme

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

le paramètre réel correspondant à a doit être un tableau avec au moins 10 éléments.

107
John Bode

La différence est purement syntaxique. En C, lorsque la notation de tableau est utilisée pour un paramètre de fonction, il est automatiquement transformé en une déclaration de pointeur.

28
Thomas Pornin

Non, il n'y a pas de différence entre eux. Pour tester, j'ai écrit ce code C dans le compilateur Dev C++ (mingw):

#include <stdio.h>

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

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

Lorsque je désassemble principal la fonction .exe des deux versions du fichier binaire appelant dans IDA, le code d'assemblage est identique à celui présenté ci-dessous:

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

Donc, il n'y a pas de différence entre les deux versions de cet appel, du moins le compilateur les menace également.

0
mcaaltuntas