Указатели в C: простое объяснение для новичков

Указатель в языке Си — переменная, хранящая адрес другого объекта в памяти. Сам указатель не содержит значение объекта, только его адрес. Это позволяет значительно оптимизировать работу программ, особенно при передаче больших объемов данных функциям.

Зачем нужны указатели?

Рассмотрим пример: массив из 200 элементов. При передаче такого массива в функцию без использования указателей вся информация массива копируется, что требует значительных вычислительных ресурсов. Использование указателей решает эту проблему: в функцию передаётся не весь массив, а только его адрес в памяти. Функция получает доступ к элементам массива по этому адресу, что значительно экономит ресурсы. Это особенно актуально для больших массивов и сложных объектов. Поэтому указатели широко используются при передаче объектов в функции и структуры.

Даже в таких распространенных функциях, как scanf, при получении значения и его записи в переменную используется указатель (через амперсанд &). Мы не передаём переменную целиком, а только её адрес.

Создание и использование указателей

Создадим переменную x со значением 0:

int x = 0;

Выведем значение и адрес переменной x с помощью printf:

printf("%d\n", x); // Вывод значения
printf("%p\n", &x); // Вывод адреса

&x — оператор взятия адреса. %p — спецификатор формата для вывода адреса.

Теперь создадим указатель:

int *ptr; // Объявление указателя на целое число
ptr = &x; // Присвоение адресу переменной x указателю ptr

* перед именем указателя указывает, что это указатель. Значение указателю присваивается с помощью оператора &.

Выведем адрес, хранящийся в указателе:

printf("%p\n", ptr); // Вывод адреса, хранящегося в указателе

Результат будет совпадать с адресом переменной x.

Выведем значение переменной x через указатель:

printf("%d\n", *ptr); // Вывод значения по адресу, хранящемуся в указателе

*ptr — оператор разыменования указателя, возвращающий значение по адресу, хранящемуся в указателе. Этот код аналогичен printf("%d\n", x);, но выполняется немного медленнее.

Изменим значение переменной через указатель:

*ptr = 10;
printf("%d\n", x); // Вывод значения переменной x после изменения через указатель

Значение переменной x изменилось на 10.

Указатели и структуры

Создадим структуру abstract с полями width и height:

struct abstract {
    int width;
    int height;
};

Создадим объект squared типа abstract и вычислим его площадь с помощью функции calc:

struct abstract squared = {5, 7};

int calc(struct abstract obj) {
    return obj.width * obj.height;
}

printf("%d\n", calc(squared)); // Вывод площади без использования указателей

Теперь реализуем функцию calc с использованием указателей:

int calc_ptr(struct abstract *obj) {
    return obj->width * obj->height;
}

printf("%d\n", calc_ptr(&squared)); // Вывод площади с использованием указателей

-> — оператор доступа к членам структуры через указатель.

В этом примере разница в производительности незначительна, но при работе с большими объектами использование указателей значительно улучшает производительность.

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

Что будем искать? Например,программа