Обмен указателей в C (char, int) - PullRequest
32 голосов
/ 06 декабря 2011

Я изо всех сил пытался понять различное поведение при обмене указателями в C. Если я хочу поменять местами два указателя int, то я могу сделать

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

Однако, если я хочу поменять местами дваchar указатели Мне нужно сделать что-то вроде

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

, потому что, если я делаю

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

, компилятор жалуется на выражение * a = * b, так как он не может изменить значения.Если я хочу поменять местами два strings (то есть char* s1= "Hello"; char* s2="Bye";), как один из них это сделает?

Не могли бы вы мне помочь?Я хотел бы по-настоящему узнать, как это работает, поэтому мне не нужно будет постоянно испытывать ошибки, пока не получу правильный ответ.Я надеюсь, что это полезно для многих других людей.

Ответы [ 5 ]

100 голосов
/ 06 декабря 2011

Первое, что вам нужно понять, это то, что когда вы передаете что-то в функцию, это что-то копируется в аргументы функции.

Предположим, у вас есть следующее:

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!

Исходные переменные не будут заменены местами, потому что их значения копируются в аргументы функции.Затем функция переходит к обмену значениями этих аргументов, а затем возвращает.Исходные значения не изменяются, потому что функция меняет только свои собственные частные копии.

Теперь, как нам обойти это?Функция нуждается в способе ссылки на исходные переменные, а не на копии их значений.Как мы можем ссылаться на другие переменные в C?Использование указателей.

Если мы передадим указатели на наши переменные в функцию, функция сможет поменять значения в наших переменных вместо своих собственных копий аргументов.

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!

Обратите внимание, что внутри функции мы не назначаем указатели, а назначаем то, на что они указывают.И указатели указывают на наши переменные x и y.Функция напрямую изменяет значения, хранящиеся в наших переменных, через указатели, которые мы передаем.И это именно то, что нам нужно.

Что теперь произойдет, если у нас есть две переменные-указателя и мы хотим поменять сами указатели (в отличие от значений, на которые они указывают)?Если мы передадим указатели, то указатели будут просто скопированы (а не значения, на которые они указывают) в аргументы.

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!

Функция swap3 меняет только свои собственные частные копии наших указателей, которые она получает вего аргументы.Это та же проблема, что и у нас с swap1swap4 меняет значения, на которые указывают наши переменные, а не указатели!Мы даем функции средство для ссылки на переменные x и y, но мы хотим, чтобы они ссылались на xp и yp.

Как мы это делаем?Мы передаем ему их адреса!

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

Таким образом, он меняет наши переменные-указатели (обратите внимание, как xp теперь указывает на y), но не на значения, на которые они указывают.Мы дали ему возможность ссылаться на переменные-указатели, чтобы он мог их изменять!

К настоящему времени должно быть легко понять, как поменять две строки в виде char* переменных.Функция подкачки должна получать указатели на 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);
5 голосов
/ 06 декабря 2011
void intSwap (int *pa, int *pb){
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

Вам нужно знать следующее -

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

Итак, когда вы меняете таким образом.Только значение будет поменяно местами.Таким образом, для char* будет заменен только их первый char.

Теперь, если вы четко понимаете char * (string), то вы должны знать, что вам нужно только заменить указатель.Это будет легче понять, если вы думаете, что это array вместо строки.

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

Итак, здесь вы передаете двойной указатель, потому что начало array само по себе является указателем.

4 голосов
/ 06 декабря 2011

В C строка, как вы знаете, является символьным указателем (char *).Если вы хотите поменять местами две строки, вы поменяете местами два указателя на символы, т.е. только два адреса.Чтобы выполнить любой обмен в функции, вам необходимо дать ей адреса двух вещей, которые вы меняете.Так что в случае замены двух указателей вам нужен указатель на указатель.Как и для замены int, вам просто нужен указатель на int.

Причина, по которой ваш последний фрагмент кода не работает, заключается в том, что вы ожидаете, что он поменяет местами два указателя на символы - он фактически записанпоменяйте местами два символа!

Редактировать: В приведенном выше примере вы пытаетесь некорректно поменять два указателя на int, как указывает Р. Мартиньо Фернандес.Это поменяет два целых, если у вас было:

int a, b;
intSwap(&a, &b);
1 голос
/ 09 января 2012

Если у вас есть возможность работать в C ++, используйте это:

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

Конечно, в случае char*, это будет менять только сами указатели, а не данные, на которые они указывают,но в большинстве случаев это нормально, верно?

1 голос
/ 06 декабря 2011

Необходимо понимать разницу между передачей по ссылке и передачей по значению.

По сути, C поддерживает только передачу по значению. Поэтому вы не можете ссылаться на переменную напрямую, когда передаете ее в функцию. Если вы хотите изменить переменную из функции, которую делает своп, вам нужно использовать передачу по ссылке. Чтобы реализовать передачу по ссылке в C, нужно использовать указатель, который может разыменовываться со значением.

Функция:

void intSwap(int* a, int* b)

Он передает значение двух указателей в intSwap, и в функции вы меняете значения, на которые указывал a / b, но не сам указатель. Вот почему Р. Мартиньо и Дэн Фего сказали, что поменяйте местами два целых числа, а не указатели.

Для символов, я думаю, вы имеете в виду строку, более сложны. Строка в C реализована как массив chars, на который ссылается char *, указатель, как строковое значение. И если вы хотите передать символ * с помощью передачи по ссылке, вам нужно использовать указатель символа *, чтобы получить символ **.

Возможно, код ниже более понятен:

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

Синтаксис swap (int & a, int & b) - C ++, что означает прямую передачу по ссылке. Может быть, какой-то компилятор C тоже реализует.

Надеюсь, я сделаю это более четко, а не смущенно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...