Permutation des pointeurs en C (char, int)

J'ai eu du mal à comprendre le comportement différent lors de l'échange de pointeurs dans C. Si je veux échanger deux pointeurs int, alors je peux faire

void intSwap (int *pa, int *pb){
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

Cependant, si je veux échanger deux pointeurs char, je dois faire quelque chose comme

void charSwap(char** a, char** b){
    char *temp = *a;
    *a = *b;
    *b = temp;
}

Parce que si je le fais

void charSwap(char* a, char* b){
    char temp = *a;
    *a = *b;
    *b = temp;
}

Le compilateur se plaint de l'expression * a = * b car elle ne peut pas changer les valeurs. Si je veux échanger deux strings (c'est-à-dire char* s1= "Hello"; char* s2="Bye"; )Comment le ferait-on?

Pourriez-vous me donner un peu de de l'aide? Je voudrais vraiment apprendre comment cela fonctionne, donc je n'aurai pas besoin de faire l'expérience d'essais et d'erreurs tout le temps jusqu'à ce que j'obtienne la bonne réponse. J'espère que c'est utile pour beaucoup d'autres personnes.

23
demandé sur Manolete 2011-12-06 20:36:21

5 réponses

La première chose que vous devez comprendre est que lorsque vous passez quelque chose à une fonction, que quelque chose est copié dans les arguments de la fonction.

Supposons que vous ayez ce qui suit:

void swap1(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    assert(a == 17);
    assert(b == 42);
    // they're swapped!
}

int x = 42;
int y = 17;
swap1(x, y);
assert(x == 42);
assert(y == 17);
// no, they're not swapped!

Les variables d'origine ne seront pas échangées, car leurs valeurs sont copiées dans les arguments de la fonction. La fonction procède ensuite à l'échange des valeurs de ces arguments, puis retourne. Les valeurs d'origine ne sont pas modifiées, car la fonction échange uniquement ses propres valeurs privées copie.

MAINTENANT, Comment Pouvons-nous contourner cela? La fonction a besoin d'un moyen de se référer aux variables d'origine, pas de copies de leurs valeurs. Comment pouvons-nous nous référer à d'autres variables en C? Utilisation de pointeurs.

Si nous passons des pointeurs à nos variables dans la fonction, la fonction peut échanger les valeurs dans nos variables, au lieu de ses propres copies d'arguments.

void swap2(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    assert(*a == 17);
    assert(*b == 42);
    // they're swapped!
}

int x = 42;
int y = 17;
swap2(&x, &y); // give the function pointers to our variables
assert(x == 17);
assert(y == 42);
// yes, they're swapped!

Remarquez comment à l'intérieur de la fonction, nous n'attribuons pas aux pointeurs, mais à ce qu'ils pointent. Et les pointeurs pointent vers nos variables x et y. La fonction Change directement les valeurs stockées dans nos variables à travers les pointeurs que nous lui donnons. Et c'est exactement ce dont nous avions besoin.

Maintenant, que se passe-t-il si nous avons deux variables de pointeur et que nous voulons échanger les pointeurs eux-mêmes (par opposition aux valeurs qu'ils pointent)? Si nous passons des pointeurs, les pointeurs seront simplement copiés (pas les valeurs vers lesquelles ils pointent) vers les arguments.

void swap3(int* a, int* b) {
    int* temp = a;
    a = b;
    b = temp;
    assert(*a == 17);
    assert(*b == 42);
    // they're swapped!
}
void swap4(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    assert(*a == 17);
    assert(*b == 42);
    // they're swapped!
}

int x = 42;
int y = 17;
int* xp = &x;
int* yp = &y;
swap3(xp, yp);
assert(xp == &x);
assert(yp == &y);
assert(x == 42);
assert(y == 17);
// Didn't swap anything!
swap4(xp, yp);
assert(xp == &x);
assert(yp == &y);
assert(x == 17);
assert(y == 42);
// Swapped the stored values instead!

La fonction swap3 échange seulement ses propres copies privées de nos pointeurs qu'il obtient dans ses arguments. C'est le même problème que nous avons eu avec swap1. Et swap4 change les valeurs vers lesquelles pointent nos variables, pas les pointeurs! Nous donnons à la fonction un moyen de se référer aux variables x et y, mais nous voulons qu'ils se réfèrent à xp et yp.

Comment on fait ça? Nous lui transmettons leurs adresses!

void swap5(int** a, int** b) {
    int* temp = *a;
    *a = *b;
    *b = temp;
    assert(**a == 17);
    assert(**b == 42);
    // they're swapped!
}


int x = 42;
int y = 17;
int* xp = &x;
int* yp = &y;
swap5(&xp, &yp);
assert(xp == &y);
assert(yp == &x);
assert(x == 42);
assert(y == 17);
// swapped only the pointers variables

De cette façon, il échange nos variables de pointeur (remarquez comment xp pointe maintenant vers y) mais pas le les valeurs qu'ils pointent. Nous lui avons donné un moyen de se référer à nos variables de pointeur, afin qu'il puisse les changer!

Maintenant, il devrait être facile de comprendre comment échanger deux chaînes Sous la forme de variables char*. La fonction d'échange doit recevoir des pointeurs sur char*.

void swapStrings(char** a, char** b){
    char *temp = *a;
    *a = *b;
    *b = temp;
    assert(strcmp(*a, "world") == 0);
    assert(strcmp(*b, "Hello") == 0);
}

char* x = "Hello";
char* y = "world";
swapStrings(&x, &y);
assert(strcmp(x, "world") == 0);
assert(strcmp(y, "Hello") == 0);
84
répondu R. Martinho Fernandes 2012-01-08 20:12:28
void intSwap (int *pa, int *pb){
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

Vous devez savoir ce qui suit-

int a = 5; // an integer, contains value
int *p; // an integer pointer, contains address
p = &a; // &a means address of a
a = *p; // *p means value stored in that address, here 5

void charSwap(char* a, char* b){
    char temp = *a;
    *a = *b;
    *b = temp;
}

Donc, quand vous échangez comme ça. Seule la valeur sera échangée. Donc, pour un char*, seul leur premier {[5] } sera échangé.

Maintenant, si vous comprenez clairement char* (string), alors vous devriez le savoir, il vous suffit d'échanger le pointeur. Ce sera plus facile à comprendre si vous le pensez comme un array au lieu de string.

void stringSwap(char** a, char** b){
    char *temp = *a;
    *a = *b;
    *b = temp;
}

Donc, ici vous passez un double pointeur car le démarrage d'un array lui-même est un pointeur.

5
répondu Rifat 2011-12-06 20:24:06

En C, une chaîne, comme vous le savez, est un pointeur de caractère (char *). Si vous voulez échanger deux chaînes, vous échangez deux pointeurs de caractères, c'est-à-dire seulement deux adresses. Afin de faire un échange dans une fonction, vous devez lui donner les adresses des deux choses que vous échangez. Donc, dans le cas de l'échange de deux pointeurs, vous avez besoin d'un pointeur sur un pointeur. Tout comme pour échanger un int, vous avez juste besoin d'un pointeur sur un int.

La raison pour laquelle votre dernier extrait de code ne fonctionne pas est que vous vous y attendez swap deux pointeurs de caractères - il est en fait écrit pour échanger deux caractères!

Edit: dans votre exemple ci-dessus, vous essayez d'échanger incorrectement deux pointeurs int, comme le souligne R. Martinho Fernandes. Cela va échanger les deux ints, si vous aviez:

int a, b;
intSwap(&a, &b);
4
répondu Dan Fego 2011-12-06 16:51:19

, Vous devez comprendre la différence entre les passer par référence et par valeur.

Fondamentalement, C ne supporte que la valeur pass-by-value. Vous ne pouvez donc pas référencer une variable directement lorsque vous la transmettez à une fonction. Si vous voulez changer la variable sur une fonction, ce que le swap fait, vous devez utiliser pass-by-reference. Pour implémenter pass-by-reference en C, besoin d'utiliser un pointeur, qui peut déréférencer la valeur.

La fonction:

void intSwap(int* a, int* b)

Il passe la valeur de deux pointeurs à intSwap, et dans la fonction, vous échangez les valeurs vers lesquelles a / b a pointé, mais pas le pointeur lui-même. C'est pourquoi R. Martinho & Dan Fego ont dit qu'il échangeait deux entiers, pas des pointeurs.

Pour les caractères, je pense que vous voulez dire string, sont plus compliqués. La chaîne en C est implémentée comme un tableau de caractères, référencé par un char*, un pointeur, comme valeur de chaîne. Et si vous voulez passer un char * par pass-by-reference, vous devez utiliser le pont de char*, donc vous obtenez char**.

Peut-être le code ci-dessous plus clairement:

typedef char* str;
void strSwap(str* a, str* b);

La syntaxe de swap(int& a, int& b) est C++, ce qui signifie par référence directement. Peut-être que certains compilateurs C implémentent aussi.

J'espère que je le fais plus clairement, pas comfuse.

1
répondu Googol 2011-12-06 16:58:04

Si vous avez le luxe de travailler en C++, utilisez ceci:

template<typename T>
void swapPrimitives(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

Certes, dans le cas de char*, Il ne ferait qu'échanger les pointeurs eux-mêmes, pas les données vers lesquelles ils pointent, mais dans la plupart des cas, c'est OK, Non?

1
répondu TheBuzzSaw 2012-01-08 20:24:34