Указатель в Си — это переменная, содержащая адрес другой переменной. Сложность указателей заключается в понимании где и для чего они могут пригодиться.
Перед тем, как я начну рассказывать об указателях и зачем они программистам, быстренько вспомним основы:
Указатель
В Си указателем называется переменная, содержащая адрес другой переменной. Его можно использовать с любым типом данных, написав:
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;
}
Читайте также:
- Использование методов расширения в C# для элегантного и плавного кода
- Игра на C# меньше 8 Кб
- 4 golang-сниппета, которые вводят в заблуждение разработчиков C#!
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Book Sadprasid: C Pointer? What is it for?