Подтвердить что ты не робот

Перемещение указателей в C (char, int)

Я пытаюсь понять различное поведение при переключении указателей на 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";), как это сделать?

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

4b9b3361

Ответ 1

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

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

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 заменяет только свои личные копии наших указателей, которые она получает в своих аргументах. Это та же проблема, что и у swap1. И swap4 меняет значения, на которые указывают наши переменные, а не указатели! Мы предоставляем функции как ссылку на переменные 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);

Ответ 2

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 является указателем.

Ответ 3

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

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

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

int a, b;
intSwap(&a, &b);

Ответ 4

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

В основном, C поддерживает только пропускную способность. Таким образом, вы не можете напрямую ссылаться на переменную, передавая ее функции. Если вы хотите изменить переменную из функции, которую выполняет своп, вам нужно использовать pass-by-reference. Чтобы реализовать pass-by-reference в C, нужно использовать указатель, который может разыменовываться.

Функция:

void intSwap(int* a, int* b)

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

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

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

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

Синтаксический обмен (int & a, int & b) - это С++, что означает прямое обращение к ссылке. Возможно, некоторые компиляторы C тоже.

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

Ответ 5

Если у вас есть роскошь работы на С++, используйте это:

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

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