ti-enxame.com

Trocando ponteiros em C (char, int)

Eu tenho lutado para entender o comportamento diferente ao trocar ponteiros em C. Se eu quiser trocar dois ponteiros int, posso fazer

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

No entanto, se eu quiser trocar dois ponteiros char, preciso fazer algo como

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

porque se eu fizer

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

o compilador reclama da expressão * a = * b, pois não pode alterar os valores. Se eu quiser trocar dois strings (ou seja, char* s1= "Hello"; char* s2="Bye";) como alguém faria isso?

Poderia me ajudar um pouco? Gostaria realmente de aprender como funciona, para que não precise experimentar tentativa e erro o tempo todo até obter a resposta certa. Espero que seja útil para muitas outras pessoas.

32
Manolete

A primeira coisa que você precisa entender é que, quando você passa algo para uma função, algo é copiado para os argumentos da função.

Suponha que você tenha o seguinte:

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!

As variáveis ​​originais não serão trocadas, porque seus valores são copiados nos argumentos da função. A função então troca os valores desses argumentos e retorna. Os valores originais não são alterados, porque a função apenas troca suas próprias cópias particulares.

Agora, como resolvemos isso? A função precisa de uma maneira de se referir às variáveis ​​originais, não cópias de seus valores. Como podemos nos referir a outras variáveis ​​em C? Usando ponteiros.

Se passarmos ponteiros para nossas variáveis ​​para a função, a função poderá trocar os valores nas variáveis ​​our, em vez de suas próprias cópias de argumentos.

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!

Observe como dentro da função não estamos atribuindo aos ponteiros, mas atribuindo ao que eles apontam. E os ponteiros apontam para nossas variáveis ​​x e y. A função está alterando diretamente os valores armazenados nas variáveis ​​our através dos ponteiros que fornecemos. E é exatamente disso que precisamos.

Agora, o que acontece se tivermos duas variáveis ​​de ponteiro e quisermos trocar os ponteiros eles mesmos (em oposição aos valores para os quais apontam)? Se passarmos ponteiros, os ponteiros serão simplesmente copiados (não os valores que apontam) para os argumentos.

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!

A função swap3 apenas troca suas próprias cópias privadas de nossos ponteiros, para obter seus argumentos. É o mesmo problema que tivemos com swap1. E swap4 está alterando os valores que nossas variáveis ​​apontam, não os ponteiros! Estamos dando à função um meio de se referir às variáveis ​​x e y, mas queremos que elas se refiram a xp e yp.

Como fazemos isso? Nós passamos seus endereços!

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

Dessa forma, alterna nossas variáveis ​​de ponteiro (observe como xp agora aponta para y), mas não os valores para os quais elas apontam. Demos a ele uma maneira de nos referir às nossas variáveis ​​de ponteiro, para que elas possam ser alteradas!

A essa altura, deve ser fácil entender como trocar duas cadeias na forma de char* variáveis. A função de troca precisa receber ponteiros para 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);
101
R. Martinho Fernandes
void intSwap (int *pa, int *pb){
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

Você precisa saber o seguinte -

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

Então, quando você troca assim. Somente o valor será trocado. Então, por um char* apenas o primeiro char será trocado.

Agora, se você entende char * (string) claramente, deve saber disso, basta trocar o ponteiro. Será mais fácil entender se você o considera um array em vez de uma string.

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

Então, aqui você está passando o ponteiro duplo, porque o início de um array é um ponteiro.

5
Rifat

Em C, uma string, como você sabe, é um ponteiro de caractere (char *). Se você deseja trocar duas cadeias, está trocando dois ponteiros de caracteres, ou seja, apenas dois endereços. Para fazer uma troca em uma função, você precisa fornecer os endereços das duas coisas que você está trocando. Portanto, no caso de trocar dois ponteiros, você precisa de um ponteiro para um ponteiro. Bem como trocar um int, você só precisa de um ponteiro para um int.

A razão pela qual seu último trecho de código não funciona é porque você espera que ele troque dois ponteiros de caracteres - ele foi escrito para trocar dois caracteres!

Edit: No seu exemplo acima, você está tentando trocar dois ponteiros int incorretamente, como R. Martinho Fernandes aponta. Isso trocará as duas entradas, se você tivesse:

int a, b;
intSwap(&a, &b);
4
Dan Fego

Você precisa entender a diferença entre passagem por referência e passagem por valor.

Basicamente, C suporta apenas passagem por valor. Portanto, você não pode fazer referência a uma variável diretamente quando passá-la para uma função. Se você deseja alterar a variável para fora de uma função, como a troca faz, é necessário usar passagem por referência. Para implementar a passagem por referência em C, é necessário usar o ponteiro, que pode desreferenciar o valor.

A função:

void intSwap(int* a, int* b)

Ele passa o valor de dois ponteiros para intSwap e, na função, você troca os valores que a/b apontou, mas não o ponteiro em si. Foi por isso que R. Martinho e Dan Fego disseram que trocam dois números inteiros, não ponteiros.

Para caracteres, acho que você quer dizer string, são mais complicados. A sequência em C é implementada como uma matriz de caracteres, referenciada por um caractere *, um ponteiro, como o valor da sequência. E se você deseja passar um caractere * por passagem-por-referência, você precisa usar o ponter de caractere *, para obter o caractere **.

Talvez o código abaixo seja mais claro:

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

A troca de sintaxe (int & a, int & b) é C++, que significa passagem direta por referência. Talvez algum compilador C também implemente.

Espero torná-lo mais claro, não confuso.

1
Googol

Se você tem o luxo de trabalhar em C++, use o seguinte:

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

Concedido, no caso de char*, apenas trocariam os ponteiros, não os dados para os quais apontam, mas na maioria dos casos, tudo bem, certo?

0
TheBuzzSaw