Указатель в Си  —  это переменная, содержащая адрес другой переменной. Сложность указателей заключается в понимании где и для чего они могут пригодиться.

Перед тем, как я начну рассказывать об указателях и зачем они программистам, быстренько вспомним основы:

Указатель

В Си указателем называется переменная, содержащая адрес другой переменной. Его можно использовать с любым типом данных, написав:

int i = 0;

int *ptr = &i;

Оператор «&» (амперсанд) определяет адрес переменной, а оператор «*» разыменования позволяет получить значение по адресу, указанным указателем. В примере выше адрес i присваивается к указателю ptr и получается, что ptr указывает на i.

Для чего нужен указатель в Си

Функции в Си принимают аргументы, передавая или копируя значения в стек функции. Такой метод иногда называется передачей по значению. Поскольку функции в Си и переменные, переданные им, в действительности не связываются, любые внесённые изменения в эти переменные не будут сохраняться за пределами действия функции. Это может вызвать сложности, потому что в некоторых функциях необходимо изменять текущие переменные. Здесь-то нам и пригодится указатель. С его помощью можно получить доступ к памяти, находящейся за пределами стекового кадра. Однако важно отметить, что с помощью указателя можно получить доступ лишь к переменным, расположенным ниже текущего кадра.

// gcc -o pointer pointer.c && ./pointer
 #include <stdio.h>
 #include <stdlib.h>

// Эта функция не будет работать, так как в Си функция передаётся по значению.
// Внесённые изменения не действительны за пределами функции.
void increment(int i) {
   i = i + 1;
}

// Передайте указатель на i, а не на само значение, тогда всё заработает.
void incrementWorks ( int* i) {
    *i = *i + 1;
}

int main() {
    int i = 765;

printf("Original value: %d\n", i);
    increment(i);
    printf("After the increment function is called: %d\n", i);

printf("Original value: %d\n", i);
    incrementWorks (&i);
    printf("After the increment function is called %d\n", i);

return (EXIT_SUCCESS);
}

В примере выше простая функция с задачей  —  увеличить на единицу число, проходящее через параметры. Функция написана следующим образом:

// Эта функция не будет работать, так как в языке Си функция передаётся по значению.
// Внесённые изменения не действительны за пределами функции.
void increment(int i) {
   i = i + 1;
}

Однако при запуске кода никаких изменений с переменной не происходит. Связанно это с тем, что в функцию increment (увеличения) копируется только значение переменной, и остальная часть программы не видит изменений, внесённых в эту переменную. Другими словами, переменная i находится внутри функции increment(), и несмотря на одинаковые названия, это не одна и та же переменная, что int i, расположенная за пределами этой функции.

Для того, чтобы решить эту проблему, нужно вместо самой переменной передать в функцию increment указатель этой переменной. Таким образом мы предоставим текущей функции доступ к переменной i, которая не попадает в область действия при выполнении этой функции. Выглядит она вот так:

// Передайте указатель на i, а не на само значение, тогда всё заработает
void increment(int *i) {
   *i = *i + 1;
}

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Book Sadprasid: C Pointer? What is it for?