Указатель в языке Си — переменная, хранящая адрес другого объекта в памяти. Сам указатель не содержит значение объекта, только его адрес. Это позволяет значительно оптимизировать работу программ, особенно при передаче больших объемов данных функциям.
Зачем нужны указатели?
Рассмотрим пример: массив из 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)); // Вывод площади с использованием указателей
-> — оператор доступа к членам структуры через указатель.
В этом примере разница в производительности незначительна, но при работе с большими объектами использование указателей значительно улучшает производительность.
Указатели — мощный инструмент в языке Си, позволяющий эффективно работать с памятью и оптимизировать код. Правильное понимание и использование указателей — ключ к написанию эффективных и производительных программ на Си.