web-dev-qa-db-fra.com

Passer un tableau par référence en C

Je suis nouveau à C et j'ai un doute.

Étant donné que les fonctions C créent des copies locales de ses arguments, je me demande pourquoi le code suivant fonctionne comme prévu:

void function(int array[]){

    array[0] = 4;
    array[1] = 5;
    array[2] = 6;   
}

int main(){

    int array[] = {1,2,3};

    function(array);

    printf("%d %d %d",array[0],array[1],array[2]);

    return 0;
}

La sortie ligne étant de 4 5 6.

Pourquoi cela fonctionne-t-il alors que ce qui suit ne fonctionne pas?

void function(int integer){

    integer = 2;
}

int main(){

    int integer = 1;

    function(integer);

    printf("%d",integer);

    return 0;
}

La sortie est juste 1 dans ce cas.

Version courte: pourquoi les fonctions peuvent-elles modifier les valeurs de leurs variables parentes si elles sont passées sous forme de tableau?

Merci à tous!

18
Costagero

Cela est dû au fait que les tableaux ont tendance à se désintégrer en pointeurs.

int a[] = { 1, 2, 3 };
int* p = a; // valid: p is now the address of a[0]
a = p;  // NOT valid.

printf("a = %p\n", a);
printf("p = %p\n", p); // prints same address as a

a et p imprimeront la même valeur.

Contrairement à ce que d'autres ont dit, a n'est pas pas un pointeur, il peut simplement se désintégrer en un. http://c-faq.com/aryptr/aryptrequiv.html

Dans votre premier function() ce qui est transmis est l'adresse du premier élément du tableau, et le corps de la fonction en déréférence. En fait, le compilateur traite le prototype de fonction comme suit:

void function(int* array /*you wrote int array[]*/){
    array[0] = 4;
    array[1] = 5;
    array[2] = 6;   
}

function(&array[0]);

Cela doit arriver parce que vous avez dit "tableau de taille inconnue" (int array []). Le compilateur ne pouvait pas garantir de déduire la quantité de pile requise pour passer par valeur, il se désintègre donc en un pointeur.

---- Modifier ----

Permet de combiner vos deux exemples et d'utiliser des noms plus distinctifs pour rendre les choses plus claires.

#include <stdio.h>

void func1(int dynArray[]) {
    printf("func1: dynArray = %p, &dynArray[0] = %p, dynArray[0] = %d\n",
             dynArray, &dynArray[0], dynArray[0]);
}

void func2(int* intPtr) {
    printf("func2: intPtr = %p, &intPtr[0] = %p, intPtr[0] = %d\n",
             intPtr, &intPtr[0], intPtr[0]);
}

void func3(int intVal) {
    printf("func3: intVal = %d, &intValue = %p\n",
             intVal, &intVal);
}

int main() {
    int mainArray[3] = { 1, 2, 3 };
    int mainInt = 10;

    printf("mainArray = %p, &mainArray[0] = %p, mainArray[0] = %d\n",
             mainArray, &mainArray, mainArray[0]);
    func1(mainArray);
    func2(mainArray);

    printf("mainInt = %d, &mainInt = %p\n",
             mainInt, &mainInt);
    func3(mainInt);

    return 0;
}

Démo en direct sur ideone: http://ideone.com/P8C1f4

mainArray = 0xbf806ad4, &mainArray[0] = 0xbf806ad4, mainArray[0] = 1
func1: dynArray = 0xbf806ad4, &dynArray[0] = 0xbf806ad4, dynArray[0] = 1
func2: intPtr = 0xbf806ad4, &intPtr[0] = 0xbf806ad4, intPtr[0] = 1

mainInt = 10, &mainInt = 0xbf806acc
func3: intVal = 10, &intValue = 0xbf806ad0

Dans func1 Et func2 "DynArray" et "intPtr" sont des variables locales, mais ce sont des variables de pointeur dans lesquelles ils reçoivent l'adresse de "mainArray" de main.

Ce comportement est spécifique aux tableaux. Si vous deviez placer le tableau dans une structure, vous seriez en mesure de le transmettre par valeur.

20
kfsone

n tableau passé à une fonction est converti en un pointeur. Lorsque vous passez un pointeur comme argument à une fonction, vous donnez simplement l'adresse de la variable dans la mémoire. Ainsi, lorsque vous modifiez la valeur de la cellule du tableau, vous modifiez la valeur sous l'adresse donnée à la fonction.

Lorsque vous passez un entier simple à une fonction, l'entier est copié dans la pile, lorsque vous modifiez l'entier dans la fonction, vous modifiez la copie de l'entier, pas l'original.

Rappel des différents types de mémoire en C

En C, nous pouvons utiliser trois types de mémoire:

  • la pile, utilisée pour les variables locales et les appels de fonctions: lorsque nous créons une variable dans main (), nous utilisons la pile pour stocker la variable, et lorsqu'une fonction est appelée, les paramètres donnés à la méthode sont enregistrés dans la pile. Lorsque nous quittons une fonction, nous "pop" ces paramètres pour revenir à l'état d'origine, avec la variable utilisée avant l'appel de la fonction. (anecdote: un stackoverflow consiste à pirater la pile pour utiliser les variables précédentes dans une fonction sans la passer en paramètre)
  • le tas qui correspond à la mémoire allouée dynamiquement: lorsque nous avons besoin d'une grande quantité de données, nous utilisons ce tas car la pile est limitée à quelques mégaoctets.
  • le code où sont stockées les instructions du programme

Dans le cas de ce tableau passé par une fonction, qui est un pointeur (adresse vers une autre variable), il est stocké dans la pile, lorsque nous appelons la fonction, nous copions le pointeur dans la pile.

Dans le cas de l'entier, il est également stocké dans la pile, lorsque nous appelons la fonction, nous copions l'entier.

Si nous voulons modifier l'entier, nous pouvons passer l'adresse de l'entier pour modifier la valeur sous le pointeur, comme ceci:

void function(int *integer)
{
    *integer = 2;
}

int main()
{
    int integer = 1;
    function(&integer);

    printf("%d", integer);

    return 0;
}
3
StarkOverflow

Il y a une différence entre "passer par référence" et "passer par valeur"

Le passage par référence mène à un emplacement dans la mémoire où le passage par valeur passe directement la valeur, une variable de tableau est toujours une référence, donc elle pointe vers un emplacement dans la mémoire. Les entiers passeront par valeur par défaut

1
Mazzy

Dans le premier code, vous transmettez l'adresse du tableau pointant vers l'élément supérieur du tableau. Ainsi, lorsque vous modifiez la valeur dans la fonction et revenez à la fonction principale, vous accédez toujours au même tableau qui se trouve à la même adresse. Cela s'appelle passer par référence.

Cependant, dans le second cas, la valeur de l'entier est copiée de la fonction principale vers la fonction appelée. En d'autres termes, les deux entiers sont dans une adresse différente dans la mémoire. Donc, modifier l'un ne modifie pas l'autre.

1
user2502921

Le nom du tableau est un pointeur sur le premier élément du tableau. Dans le premier exemple de code, vous avez passé un pointeur vers l'emplacement de mémoire contenant le premier élément du tableau. Dans le deuxième exemple de code, vous avez passé un entier par valeur, il n'a donc rien à voir avec la variable locale nommée "entier"

vérifier ce lien

Passer par référence et passer par valeur

Passer par référence/valeur en C++

0
Rami