Passage de valeurs et d'adresses en C

Le concept fondamental en programmation est la gestion de la mémoire. Pour manipuler des ressources, il est essentiel de connaître leur emplacement en mémoire, c'est-à-dire leur adresse, avant de pouvoir accéder ou modifier les données qu'elles contiennent.

Examinons la différence entre le passage de valeurs (pass-by-value) et le passage d'adresses (pass-by-reference) à l'aide d'un exemple simple d'échange de deux variables entières en C.

  1. Passage par valeur
#include <stdio.h>

// Déclaration de la fonction d'échange par valeur
void echangerParValeur(int var1, int var2);

int main() {
    int premier = 100;
    int second = 200;

    printf("Avant l'échange (main) - premier: %d, second: %d\n", premier, second);

    // Appel de la fonction d'échange par valeur
    echangerParValeur(premier, second);

    printf("Après l'échange (main) - premier: %d, second: %d\n", premier, second);

    getchar(); // Pause pour voir le résultat
    return 0;
}

// Implémentation de la fonction d'échange par valeur
void echangerParValeur(int var1, int var2) {
    int temporaire;
    temporaire = var1;
    var1 = var2;
    var2 = temporaire;
    printf("Dans echangerParValeur - var1: %d, var2: %d\n", var1, var2);
}

Résultat de l'exécution :

Avant l'échange (main) - premier: 100, second: 200
Dans echangerParValeur - var1: 200, var2: 100
Après l'échange (main) - premier: 100, second: 200

Dans le passage par valeur, une copie des variables premier et second est transmise à la fonction echangerParValeur. Les modifications apportées à var1 et var2 à l'intérieur de cette fonction n'affectent pas les variables originales dans main car elles résident dans des emplacements mémoire distincts. Par conséquent, l'échange n'est pas effectif dans la fonction appelante.

  1. Passage par adresse
#include <stdio.h>

// Déclaration de la fonction d'échange par adresse
void echangerParAdresse(int* ptrVar1, int* ptrVar2);

int main() {
    int premier = 100;
    int second = 200;

    printf("Avant l'échange (main) - premier: %d, second: %d\n", premier, second);

    // Appel de la fonction d'échange par adresse, en passant les adresses
    echangerParAdresse(&premier, &second);

    printf("Après l'échange (main) - premier: %d, second: %d\n", premier, second);

    getchar(); // Pause pour voir le résultat
    return 0;
}

// Implémentation de la fonction d'échange par adresse
void echangerParAdresse(int* ptrVar1, int* ptrVar2) {
    int temporaire;
    // Utilisation de l'opérateur de déréférencement (*) pour accéder aux valeurs aux adresses
    temporaire = *ptrVar1;
    *ptrVar1 = *ptrVar2;
    *ptrVar2 = temporaire;
    printf("Dans echangerParAdresse - *ptrVar1: %d, *ptrVar2: %d\n", *ptrVar1, *ptrVar2);
}

Résultat de l'exécution :

Avant l'échange (main) - premier: 100, second: 200
Dans echangerParAdresse - *ptrVar1: 200, *ptrVar2: 100
Après l'échange (main) - premier: 200, second: 100

Dans le passage par adresse, les adresses mémoire des variables premier et second sont transmises à la fonction echangerParAdresse. Les paramètres ptrVar1 et ptrVar2 sont des pointeurs qui contiennent ces adresses. En utilisant l'opérateur de déréférencement (*), la fonction accède directement aux valeurs stockées aux adresses transmises et les échange. Ainsi, les modifications apportées via les pointeurs affectent les variables originales dans main, réalisant l'échange souhaité.

  1. Tnetative d'échange d'adresses Considérons un scénario où l'on tente d'échanger les pointeurs eux-mêmes dans une fonction.
#include <stdio.h>

// Déclaration de la fonction tentant d'échanger des pointeurs
void echangerPointeurs(int* ptr1, int* ptr2);

int main() {
    int a = 100;
    int b = 200;
    int* ptrA = &a;
    int* ptrB = &b;

    printf("Avant l'échange des pointeurs (main) - ptrA pointe vers: %d (adresse: %p), ptrB pointe vers: %d (adresse: %p)\n", *ptrA, ptrA, *ptrB, ptrB);

    // Appel de la fonction tentant d'échanger les pointeurs
    echangerPointeurs(ptrA, ptrB);

    printf("Après l'échange des pointeurs (main) - ptrA pointe vers: %d (adresse: %p), ptrB pointe vers: %d (adresse: %p)\n", *ptrA, ptrA, *ptrB, ptrB);

    getchar(); // Pause pour voir le résultat
    return 0;
}

// Implémentation de la fonction tentant d'échanger des pointeurs
void echangerPointeurs(int* ptr1, int* ptr2) {
    int* ptrTemporaire;
    ptrTemporaire = ptr1;
    ptr1 = ptr2;
    ptr2 = ptrTemporaire;
    printf("Dans echangerPointeurs - ptr1 pointe vers: %d (adresse: %p), ptr2 pointe vers: %d (adresse: %p)\n", *ptr1, ptr1, *ptr2, ptr2);
}

Résultat de l'exécution :

Avant l'échange des pointeurs (main) - ptrA pointe vers: 100 (adresse: 0x...), ptrB pointe vers: 200 (adresse: 0x...)
Dans echangerPointeurs - ptr1 pointe vers: 200 (adresse: 0x...), ptr2 pointe vers: 100 (adresse: 0x...)
Après l'échange des pointeurs (main) - ptrA pointe vers: 100 (adresse: 0x...), ptrB pointe vers: 200 (adresse: 0x...)

Dans ce cas, les pointeurs ptrA et ptrB dans main contiennent les adresses de a et b respectivement. La fonction echangerPointeurs reçoit des copies de ces pointeurs. L'échange effectué à l'intérieur de echangerPointeurs ne concerne que ces copies locales. Les pointeurs originaux dans main conservent leurs adresses initiales, et donc les variables a et b ne sont pas affectées par cet échange de pointeurs.

Il est possible d'échanger les adresses elles-mêmes, comme démontré dans le code ci-dessous, ce qui montre que les pointeurs ptrA et ptrB dans main peuvent effectivement être modifiés pour pointer vers différentes variables si l'on échange leurs valeurs.

#include <stdio.h>

int main() {
    int a = 100;
    int b = 200;
    int* ptrA = &a;
    int* ptrB = &b;
    int* ptrTemporaire;

    printf("Avant échange des adresses (main) - ptrA: %p, ptrB: %p\n", (void*)ptrA, (void*)ptrB);

    // Échange des valeurs des pointeurs (les adresses qu'ils contiennent)
    ptrTemporaire = ptrA;
    ptrA = ptrB;
    ptrB = ptrTemporaire;

    printf("Après échange des adresses (main) - ptrA: %p, ptrB: %p\n", (void*)ptrA, (void*)ptrB);
    printf("Valeur via ptrA: %d, Valeur via ptrB: %d\n", *ptrA, *ptrB);

    getchar(); // Pause pour voir le résultat
    return 0;
}

Résultat de l'exécution :

Avant échange des adresses (main) - ptrA: 0x..., ptrB: 0x...
Après échange des adresses (main) - ptrA: 0x..., ptrB: 0x...
Valeur via ptrA: 200, Valeur via ptrB: 100

Ce dernier exemple confirme que l'on peut modifier les pointeurs eux-mêmes dans la portée de main. La raison pour laquelle l'échange d'adresses dans la fonction echangerPointeurs du troisième exemple n'a pas modifié les variables originales dans main est que la fonction opérait sur des copies des pointeurs. Pour modifier les pointeurs originaux, il faudrait passer l'adresse des pointeurs eux-mêmes à la fonction.

En résumé :

  1. Le passage par valeur crée des copies des variables. Les modifications dans la fonction n'affectent pas les variables originales.
  2. Le passage par adresse (via des pointeurs) permet à la fonction de modifier directement les variables originales en accédant à leurs emplacements mémoire.
  3. L'échange de pointeurs dans une fonction sans passer leurs adresses ne modifie pas les pointeurs originaux dans la fonction appelante.

Étiquettes: C pointeurs Passage par valeur passage par adresse Gestion Mémoire

Publié le 3 août à 04h15