2010-10-22 4 views
9

Lorsque nous passons un élément d'un tableau à une fonction, il est traité comme une variable normale et la fonction appelée crée une copie de l'argument réel et opère dessus. Toute modification apportée aux arguments formels n'affecte pas les arguments réels.Passage d'un tableau entier à une fonction

Mais ce n'est pas le cas quand on passe un tableau entier. Dans ce cas, la fonction (appelée function) accède aux arguments réels et toute modification apportée aux arguments formels affecte les arguments réels. Pourquoi cela arrive?

+2

Je crois que ma réponse (détaillée) à cette autre question http://stackoverflow.com/questions/3613302/passing-array-of-struc tures-to-function-c/3613350 # 3613350 couvre également celui-ci. Fondamentalement, c'est une différence entre le tableau et le problème des pointeurs. – kriss

+1

Vous ne pouvez pas utiliser const, si vous ne voulez pas modifier le contenu du tableau? – Nyan

Répondre

16

Le tableau est transmis en tant que pointeur vers les éléments.

Si je vous écris:

void doStuff(int *ptr) 
{ 
    ptr[1] = 5; 
} 

int main() 
{ 
    int arr[] = {0, 1, 2}; 
    doStuff(arr); 
    printf("%d %d %d\n", arr[0], arr[1], arr[2]); 
} 

la sortie sera "0 5 2". C'est parce que C passe tout ce qui est réellement le paramètre en valeur. Le paramètre est un pointeur sur un int. Donc, un pointeur vers un int est passé par valeur. Ainsi, doStuff obtient une copie d'un pointeur vers la mémoire dans le cadre de la pile principale. Quand il déréférence ce pointeur avec ptr[1], il suit le pointeur vers la mémoire de main et modifie le tableau là.

C ne peut que passer par la valeur, mais il le fait "peu profond". Si vous lui demandez de passer un int *, il passera un int *. Il copie uniquement la valeur du pointeur, pas les valeurs de tout ce qu'il pointe vers.

Si vous voulez doStuff pour obtenir sa propre copie du tableau, soit envelopper le tableau dans une struct comme d'autres l'ont suggéré, ou utiliser memcpy pour copier en profondeur manuellement le tableau comme ceci:

void doStuff(int *ptr, int nElems) 
{ 
    int myCopyOfTheArray[nElems]; 
    memcpy(myCopyOfTheArray, ptr, sizeof(int) * nElems); 
    /* do stuff with the local copy */ 
} 

Contrairement à l'utilisation une structure, l'approche memcpy fonctionne si nElems n'est connu qu'à l'exécution.

+0

Le 2ème extrait de code, est-il valide C ou juste pseudo C ??? – Nyan

+0

Ok, Quel compilateur utilisez-vous? juste curieux. – Nyan

+0

gcc version 4.1.2 – AlcubierreDrive

0

Un tableau dans C peut être traité comme un pointeur vers le premier élément du tableau. Le pointeur est passé par valeur (vous ne pouvez pas modifier la valeur transmise), mais vous pouvez le déréférencer et modifier la mémoire vers laquelle il pointe.

+4

Un tableau dans C n'est pas un pointeur, il est souvent * rétrogradé * à un pointeur. –

+0

@Matteo, @paxdiablo Correction. – Jonathan

8

Les tableaux ne peuvent pas être transmis par valeur en C. Ils sont rétrogradés en pointeurs. Ainsi, la fonction appelée voit un pointeur sur le tableau (transmis par référence) et opère dessus. Si vous voulez faire une copie, vous devez le faire explicitement, ou placer votre tableau dans une structure (qui peut être transmise par valeur aux fonctions.)

0

Parce que lorsque vous passez un tableau entier à la fonction pointeur vers ce tableau, ce qui signifie que vous donnez à la fonction une place en mémoire où se trouve ce tableau. Donc, lorsque vous modifiez le tableau dans la fonction Vous modifiez également le tableau d'origine.

5

Il est coûteux de faire une copie d'un tableau entier, en général. De retour aux jours où C a été créé, il pourrait facilement épuiser les ressources de la machine (pile en particulier).

Si vous voulez vraiment passer un tableau, l'envelopper dans une structure:

struct wrap { int array[100]; }; 

int somefunc(struct wrap large) { ... } 

void anotherfunc(void) 
{ 
    struct wrap a; 
    ...add data to array... 
    printf("%d\n", somefunc(a)); 
} 
2

« passer par référence » est un hareng rouge C. Tous les arguments d'une fonction sont « passés par valeur » seulement . Dans le cas de tableaux, vous passez l'adresse du premier élément sous la forme d'un pointeur. Vous pouvez ensuite utiliser ce pointeur pour faire référence à l'emplacement de mémoire souhaité & lire ou écrire des valeurs.

2

De l'online C standard:

6.3.2.1 Lvalues, les tableaux et la fonction désignateurs

... 3, sauf quand il est l'opérande de l'opérateur sizeof ou le unaire opérateur &, ou est une chaîne de caractères utilisée pour initialiser un tableau, une expression qui a le type "tableau de type" est converti en une expression avec le type "pointeur vers le type" qui pointe vers l'élément initial de l'objet tableau et n'est pas un lvalue. Si l'objet tableau a une classe de stockage de registre, le comportement n'est pas défini.

Supposons le code suivant:

int a[10]; 
... 
foo(a); 

Dans l'appel à foo, le type de l'expression a est "matrice 10 élément de int". Cependant, puisque l'expression n'est pas l'opérande des opérateurs sizeof ou &, et comme ce n'est pas un littéral de chaîne utilisé pour initialiser un autre tableau dans une déclaration, son type est implicitement converti ("decays") de "10- tableau d'éléments de int "à" pointeur vers int ", et sa valeur sera l'adresse du premier élément dans le tableau (par exemple &a[0]).

Par conséquent, ce que foo reçoit est un pointeur vers int, pas un tableau. Le déréférencement ou l'indexation de ce pointeur vous permet de modifier les valeurs du tableau.

Ceci est une caractéristique du langage C; les tableaux ne sont pas des objets de première classe. En fait, dans la plupart des cas (y compris l'indice), les expressions du tableau seront converties en types de pointeurs.

Je garde en insistant sur le mot expression de faire la distinction entre le véritable tableau objet (qui est toujours et pour toujours un type de tableau) et toute référence à cet objet dans le code, ce qui peut être converti en un pointeur type.

3

Je n'ai pas encore vu de réponse à la question. D'après ce que je vois, il semble que la question demande quelque chose comme ceci:

Vu le code suivant:

int a[5]; 

foo(a); 
bar(a[0]); 

Pourquoi peut foo manipuler les valeurs d'origine alors que bar ne reçoit qu'une copie de la valeur transmise dans ? Ceci est dû à l'utilisation de la notation matricielle: l'opérateur [] déréférence l'élément auquel il fait référence dans le tableau et transmet la valeur à cet emplacement.

Alors

int a[5]; 
bar(a[0]); 

est différent de:

int* a; 
bar(a); 

Si vous voulez passer une référence à un élément spécifique dans un tableau, vous devez utiliser l'opérateur &:

bar(&a[5]); 
Questions connexes