En C, TOUS les arguments de fonctions sont passés par valeur, même si ce sont des arguments de type pointeur.
Ainsi, quand tu écris:
void swap(int *x,int *y) // passage des parametres par adresse
en fait ton commentaire est légèrement erroné, car la fonction swap(...) ne fait que recevoir des VALEURS qui se trouvent être de type int* (pointeurs d'entier).
Que fait ta fonction swap()? Elle s'alloue LOCALEMENT -- j'insiste sur ce terme! -- deux variables x et y de type int*, lesquelles variables reçoivent des valeurs fournies par l'appelant.
Pour simplifier, disons que dans main() tu as:
int A; // déclare et alloue A de type int
int B; // déclare et alloue B de type int
int* pA; // déclare et alloue pA de type int*
int* pB; // déclare et alloue pB de type int*
pA = &A; // charge dans pA l'adr de A
pB = &B; // charge dans pB l'adr de B
[je garderai ces notations par la suite]
Lorsque tu fais swap(pA, pB), il se passe implicitement et localement, compte tenu du prototype de la fonction:
int* x; // déclare/alloue localement x de type int*
int* y; // déclare/alloue localement y de type int*
x = pA; // charge dans x la VALEUR de pA
// (en l'occurrence l'adr de A = &A)
y = pB; // charge dans y la VALEUR de pB
// (en l'occurrence l'adr de B = &B)
A partir de là, toutes les opérations que tu vas faire DIRECTEMENT sur x ou sur y, du genre:
<<
...
x=y;
y=temp;
...
>>
se bornent à permuter, non pas des "adresses" au sens où tu l'entends, mais simplement des valeurs temporaires de variables locales.
A l'issue de ce bout de code, compte tenu du contexte, x contient effectivement l'adresse de B et y l'adr de A (puisque x et y ont échangé leurs valeurs, initialisées sur pA et pB), MAIS aucune des variables A, B, pA, pB n'ont changé de valeur!
Donc, il ne faut pas s'étonner si, revenant dans le main(), tout est comme avant.
Quand tu écris que tout se passe bien dans le swap, c'est d'ailleurs un trompe-l'oeil, vu ton code de test:
<<
printf("adresse de x: %x ,adresse de y: %x\n",x,y);
printf("contenu de x: %d ,contenu de y: %d\n",*x,*y);
>>
Ce test est inexact car:
- l'ADRESSE d'une variable X n'est pas "X" mais "&X"
- le CONTENU d'une variable X n'est pas "*X" mais "X" (!)
- l'écriture "*X" représente le contenu (évalué selon le typage du pointeur) situé à l'adresse contenue dans X
Ta question initiale était:
<< De retour dans le main, "a" et "b" ne sont pas permutés! Mais pourquoi alors que j'ai transmis a et b par leurs adresses >>
La réponse est que tu n'as pas TRANSMIS a et b par leurs adresses, tu as chargé dans x et y locales des valeurs qui se trouvent être les adresses de a et b ...mais sans jamais manipuler le contenu de ces adresses.
La seule manière de le faire était évidemment de passer par *x et *y. Je ne comprends pas ta "contrainte" t'imposant d'<<échanger par adresse dans swap() et non par contenu de pointeur>>. Au demeurant, cette formule n'est pas claire du tout. Le mécanisme des pointeurs te permet de modifier dans une fonction la valeur d'une variable qui lui est externe mais dont elle connaît l'adresse, pourquoi voudrais-tu te priver de cette fonctionnalité intrinsèque du C?
Le C++ a introduit le passage d'argument par référence, càd réellement "par adresse" au sens où tu l'entends. C'est essentiellement une amélioration syntaxique, puisque ça permet de prototyper swap(int& x, int& b) et d'appeler swap(A,B) pour manipuler -- dans swap! -- directement les variables-arguments (et non des recopies de leur valeur).
Mais, dans tous les cas de figure, il est peut-être utile de rappeler qu'étant donné une variable X de type qcq, l'adresse de X (&X) n'est pas accessible EN ECRITURE. Tu peux faire en sorte qu'un pointeur pX pointe sur &Y au lieu de pointer sur &X, tu peux modifier la valeur de X indirectement via un pointeur (opérateur *), mais aucun code ne te permet de "réadresser" X. Donc, si ton objectif était d'échanger les adresses de A et de B... c'est voué à l'échec.
Pour aller + loin, il ne serait pas superflu que tu nous précises exactement le cahier des charges de ta fonction swap(). Au fond, quelles valeurs doit-elle échanger:
1 - celles de A et B seulement ?
2 - celles de pA et pB seulement ?
3 - celles de A et B ET de pA et pB ?
Dans les cas 2 et 3, il sera nécessaire de fournir à swap les adresses de pA et pB, ce qui force à prototyper:
swap(int** X, int** Y)
et à appeler la fonction avec un swap(&pA, &pB)
Dans le cas 3, le code pourrait être du genre:
void swap(int** X, int** Y)
// Contexte extérieur:
// int A ; int B ; int* pA=&A ; int* pB=&B;
// Objectifs:
// échanger les val de A et B ET les val de pA et pB
// Situation initiale implicite:
// X = &pA | *X = pA - **X = *pA = A
// Y = &pB | *Y = pB - **Y = *pB = B
{
int tmp1; // var tempo pour swapper des INT
int* tmp2; // var tempo pour swapper des INT*
// échanger les valeurs de A et B
tmp1 = **X; // sauvegarder la valeur de A
**X = **Y; // charger dans A la valeur de B
**Y = tmp1; // charger dans B la valeur sauvergardée
// A ce stade, les var externes A et B
// ont leurs valeurs permutées, mais on
// a encore: pA=&A et pB=&B
// et, bien sûr, X=&pA et Y=&pB
// échanger les valeurs de pA et pB
tmp2 = *X; // sauvegarder la valeur de pA(=&A)
*X = *Y; // charger dans pA la valeur de pB(=&B)
*Y = tmp2; // charger dans pB la valeur sauvergardée
// A ce stade, d'une part les var externes A et B
// ont leurs valeurs permutées, d'autre part
// pA=&B et pB=&A (v. commentaire infra)
}
Observons pour en finir que cette double permutation a pour conséquence que:
- pA pointe sur B qui a pris la valeur initiale de A
- pB pointe sur A qui a pris la valeur initiale de B
Donc, si on note a la valeur init. de A et b la valeur init. de B avant l'appel de swap, on a en sortie:
A = b , B = a
pA = &B , pB = &A
*pA = a , *pB = b (!)
Ce n'était probablement pas l'objectif poursuivi, mais je crois que l'exercice avait valeur pédagogique... Non?
Message édité par ACut le 09-08-2003 à 00:22:10