web-dev-qa-db-fra.com

Passer un tableau en tant qu'argument à une fonction en C

J'ai écrit une fonction contenant un tableau en argument, .__ et je l'appelle en lui transmettant la valeur du tableau comme suit.

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]);
}

Ce que j’ai trouvé, c’est que j’appelle la fonction arraytest() en lui transmettant des valeurs. La copie originale de int arr[] est modifiée.

Pouvez-vous s'il vous plaît expliquer pourquoi?

60
Mohan Mahajan

En passant un tableau en paramètre, cette

void arraytest(int a[])

signifie exactement la même chose que

void arraytest(int *a)

donc vous êtes modifiez les valeurs dans main.

Pour des raisons historiques, les tableaux ne sont pas des citoyens de première classe et ne peuvent pas être passés par valeur.

91
Bo Persson

Vous ne transmettez pas le tableau en tant que copie. Ce n'est qu'un pointeur pointant vers l'adresse où le premier élément est en mémoire.

6
fyr

Vous passez l'adresse du premier élément du tableau

6
obo

En C, sauf quelques cas particuliers, une référence à un tableau "se désintègre" toujours en un pointeur sur le premier élément du tableau. Par conséquent, il n'est pas possible de passer un tableau "par valeur". Un tableau dans un appel de fonction sera transmis à la fonction en tant que pointeur, ce qui revient au passage du tableau par référence.

EDIT: Il existe trois cas particuliers dans lesquels un tableau ne se décompose pas en un pointeur sur son premier élément:

  1. sizeof a n'est pas la même chose que sizeof (&a[0]).
  2. &a n'est pas la même chose que &(&a[0]) (et pas tout à fait la même chose que &a[0]).
  3. char b[] = "foo" n'est pas la même chose que char b[] = &("foo").
6
Thom Smith

Vous transmettez la valeur de l'emplacement mémoire du premier membre du tableau.

Par conséquent, lorsque vous commencez à modifier le tableau dans la fonction, vous modifiez le tableau d'origine.

Rappelez-vous que a[1] est *(a+1)

4
alex

Les tableaux en C sont convertis, dans la plupart des cas, en un pointeur sur le premier élément du tableau lui-même. Et plus en détail, les tableaux passés en fonctions sont toujours convertis en pointeurs. 

Voici une citation de K & R2nd :

Lorsqu'un nom de tableau est transmis à une fonction, ce qui est transmis est le emplacement de l'élément initial. Dans la fonction appelée, ceci argument est une variable locale et un paramètre de nom de tableau est donc un pointeur, c'est-à-dire une variable contenant une adresse. 

L'écriture:

void arraytest(int a[])

a le même sens qu'écrire:

void arraytest(int *a)

Donc, même si vous n’écrivez pas explicitement, c’est comme si vous passiez un pointeur et que vous modifiez donc les valeurs dans l’essentiel.

Pour plus je suggère vraiment de lire this .

De plus, vous pouvez trouver d’autres réponses sur SO ici

3
granmirupa

Si vous voulez passer un tableau à une dimension en tant qu'argument dans une fonction , vous devez déclarer un paramètre formel de l'une des trois manières suivantes et les trois méthodes de déclaration produisent des résultats similaires car chaque indique au compilateur qu'un pointeur entier va être reçu .

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

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

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

Alors, vous modifiez les valeurs d'origine.

Merci !!!

3

Passer un tableau multidimensionnel en argument d'une fonction. Passer un tableau à une variable en argument est plus ou moins trivial . Jetons un coup d'œil sur le cas plus intéressant de passer à un tableau à 2 dimensions . En C, vous ne pouvez pas utiliser de pointeur à pointeur construct (int **) au lieu de 2 dim array ..__, faisons un exemple:

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;
        }
    }

Ici, j'ai spécifié une fonction qui prend comme premier argument un pointeur sur un tableau de 5 ints. Je peux passer en argument n’importe quel tableau de 2 dim qui a 5 colonnes:

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

Vous pouvez venir à une idée pour définir une fonction plus générale pouvant accepter tout tableau à 2 dimensions et modifier la signature de la fonction comme suit:

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;
        }
    }
}

Ce code serait compilé, mais vous obtiendrez une erreur d’exécution en essayant d’attribuer les valeurs de la même manière que dans la première fonction . Ainsi, en C, les tableaux multidimensionnels ne sont pas identiques aux pointeurs pointeurs. Un int (* arr) [5] est un pointeur sur un tableau de 5 éléments, Un int (* arr) [6] est un pointeur sur un tableau de 6 éléments, et ils pointent vers différents types!

Eh bien, comment définir les arguments des fonctions pour les dimensions supérieures? Simple, nous suivons simplement le modèle! Hier est la même fonction ajustée pour prendre un tableau de 3 dimensions:

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;
            }
        }
    }
}

Comme vous vous en doutez, cela peut prendre comme argument n'importe quel tableau de 3 dimensions comportant 4 éléments dans la deuxième dimension et 5 éléments dans la troisième dimension. Quelque chose comme ça serait ok:

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

Mais nous devons spécifier toutes les dimensions, jusqu’à la première.

2
Andrushenko Alexander

@Bo Persson déclare correctement dans sa grande réponse ici

==============================================

En passant un tableau en paramètre, cette

void arraytest(int a[])

signifie exactement la même chose que

void arraytest(int *a)

==============================================

Cependant, permettez-moi d'ajouter aussi que cela:

signifie exactement la même chose que

void arraytest(int a[0])

ce qui signifie exactement la même chose que

void arraytest(int a[1])

ce qui signifie exactement la même chose que

void arraytest(int a[2])

ce qui signifie exactement la même chose que

void arraytest(int a[1000])

etc.

En fait, la valeur de "taille" à l'intérieur du paramètre tableau est apparemment juste pour des raisons esthétiques/d'auto-documentation, et peut être n'importe quel entier positif (type size_t je pense) que vous voulez! 

En pratique, cependant, vous devriez l'utiliser pour spécifier la taille minimale du tableau que vous souhaitez que la fonction reçoive, afin de faciliter le suivi et la vérification lors de l'écriture de code. Le MISRA-C-2012 standard ( acheter/télécharger la version de 236-pg 2012 PDF de la norme pour £ 15,00 ici ) va jusqu'à dire: 

Règle 17.5 L'argument de la fonction correspondant à un paramètre déclaré comme ayant un type de tableau doit avoir un nombre approprié d'éléments.

...

Si un paramètre est déclaré en tant que tableau avec une taille spécifiée, l'argument correspondant dans chaque appel de fonction doit pointer vers un objet comportant au moins autant d'éléments que le tableau.

...

L'utilisation d'un déclarateur de tableau pour un paramètre de fonction spécifie l'interface de la fonction plus clairement que l'utilisation d'un pointeur. Le nombre minimum d'éléments attendus par la fonction est explicitement indiqué, alors que cela n'est pas possible avec un pointeur. [emphase ajoutée]

En d’autres termes, ils recommandent d’utiliser le format de taille explicite, même si techniquement, le standard C ne l’applique pas --cela aide au moins à clarifier, en tant que développeur, et la fonction attend votre passage.

0
Gabriel Staples