Зачем нужны указатели в c
Перейти к содержимому

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

  • автор:

Что такое указатели в программировании

В программировании есть понятие указателей — особенно часто о них можно услышать в языках вроде C. Указатели считаются сложной темой, и про тех, кто ими пользуются, ходят легенды. Но на самом деле в указателях нет ничего сложного. Сейчас разберём.

Что такое указатель

Когда мы заводим новую переменную, компьютер выделяет для неё место в оперативной памяти:

Что такое указатели в программировании

Количество этих ячеек зависит от типа данных, который хранится в этой переменной: обычно для целого числа выделяют 2 или 4 байта, для дробного — 8 байт, для строки — столько же, сколько и символов и ещё 1 служебный байт и так далее. Но сколько бы байтов ни выделил компьютер для хранения, он выделяет эти байты подряд, друг за другом, и запоминает два момента:

  1. Сколько байтов занимает переменная.
  2. По какому адресу в памяти находится первый байт этой переменной.

Если совсем упростить, то адрес в памяти — это порядковый номер ячейки, где хранится байт. Так вот, в указателях как раз хранятся адреса памяти, где начинаются разные переменные:

Что такое указатели в программировании

Зачем это нужно

Указатели нужны для того, чтобы можно было напрямую работать с оперативной памятью. Например, указатели позволяют экономить выделение памяти: когда в функцию вместо переменной передаётся указатель, компьютер не создаёт её локальную копию, а обращается к ней напрямую.

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

Почему указателями редко пользуются

Если в указатель положить адрес памяти, который выходит за границы, выделенные для этой программы, то мы можем повредить чужие данные. Некоторые языки, например, C++, не всегда перепроверяют то, что делает программист, поэтому там легко сломать не только свою программу, но и весь компьютер.

Дело в том, что компьютеру всё равно, что лежит по адресу, написанному в указателе — данные, переменная или что-то чужое. Всё, что он делает — это идёт по этому адресу и стирает либо записывает туда что-то своё, не задавая лишних вопросов. Это мощная штука, но чтобы ей пользоваться, нужно чётко знать внутреннюю архитектуру памяти и работы компилятора.

Что такое указатели в программировании

В каких языках есть указатели

Самые популярные языки с поддержкой указателей — это всё семейство Си-языков:

А всё потому, что в старые времена оперативной памяти было мало и нужно было использовать её как можно эффективнее. Поэтому иногда было нормально, когда одна и та же область памяти использовалась программистом для хранения разных данных.

Также полноценные указатели есть в некоторых современных языках высокого уровня: Java, Pascal и Go.

Получите ИТ-профессию

В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.

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

Oбъяcнитe, зaчeм нyжны yкaзaтeли? Heт, я пoнял пpинцип paбoты, нo нe пoнял oднoгo — ЗAЧEM? Зaчeм paбoтaть c фyнкциями-члeнaми, пepeмeнными пo ccылкe, ecли я мoгy paбoтaть c ними нaпpямyю?

Отслеживать
51.6k 202 202 золотых знака 65 65 серебряных знаков 249 249 бронзовых знаков
задан 4 янв 2012 в 13:25
3,288 4 4 золотых знака 36 36 серебряных знаков 49 49 бронзовых знаков
Если Вы не поняли «зачем», значит Вы не поняли указатели!
4 янв 2012 в 13:28

Посмотрите, например, man qsort (это очень полезная функция сортировки), подумайте как это можно запрограммировать и многое прояснится.

4 янв 2012 в 13:59

qsort реализуется и без указателей. Например в таких языках как lisp/haskell, где указателей (по крайней мере в явном виде) нет. На плюсах можно без указателей тоже сделать, но только памяти будет больше потребляться.

4 янв 2012 в 14:20
Какой смысл биться за лишние сто байт памяти?
4 янв 2012 в 15:20

На самом деле все намного проще, указатели нужны для того чтобы писать «волшебные» коды, быстрые и непанятные. + для того чтобы те кто не знает С++ думали что С++ это что-то неимоверно сложное 😀 Использовать х надо потому, что в они есть и твой код становится более непонятным. **** PS:Как мне показалось — объяснил на вашем нынешнем уровне **** PPS:не трольте коммент ибо коммент сам по себе троллинг 😀

4 янв 2012 в 15:35

4 ответа 4

Сортировка: Сброс на вариант по умолчанию

«Если кто-то зажигает звезды, значит, это кому-нибудь нужно»

Вот такой пример. Положим, Вы передаете в функцию какую-то переменную. Там с ее значением что-то делается, и новое значение должно быть занесено в ту переменную, значение которой Вы в функцию передали. Если сделаете что-то типа этого

void func (int a) < a += 10; >. int i = 2; func(i); 

То значение переменной i не изменится, потому что в функцию передается копия этой переменной и с ней производятся операции. А если передать указатель или ссылку на эту переменную, то получите доступ к ней и можете изменять ее значение

void func (int* a) < *a += 10; >. int i = 2; func(&i); 

Если нужно передать в функцию структуру или объект, то передавать его целиком можно, но это может отнять кучу памяти и времени, ведь они могут быть очень большими. В простых примерах это незаметно, но в реальных программах, где это может повторяться тысячи и миллионы раз, тормоза будут огромные. Вместо этого передаете указатель или ссылку и получаете из функции доступ к объекту.

Уже говорили о динамическом выделении памяти. Если еще про это не читали, то прочитаете. Там без указателей В ПРИНЦИПЕ не обойтись.

Это лишь несколько примеров. Чем глубже будете вникать в С++, тем больше об этом узнаете.

И еще. Не думайте, что программисты до Вас были дураками.

Отслеживать
user207618
ответ дан 4 янв 2012 в 18:56
23.9k 2 2 золотых знака 38 38 серебряных знаков 69 69 бронзовых знаков

  1. Для возврата нескольких значений из функции. В качестве аргумента передаётся указатель на переменную, функция записывает туда значение. Такой подход очень распространён в DirectX, OpenGL, Windows API и других библиотеках в стиле C. Для этого можно использовать и ссылки, но не рекомендуется, так как синтаксис передачи и возврата неотличим.
  2. Для хранения адреса динамически выделенной памяти. Она отличается от обычной тем, что программист сам регулирует время жизни объектов, и её больше (а размер стека всего порядка мегабайта). Если адрес будет потерян, то память нельзя будет ни использовать, ни освободить. Возникнет утечка памяти.
  3. C-строка представляет собой указатель на её первый символ.
  4. Для создания различных структур данных: связанных списков, деревья и т. д.
  5. Для передачи аргумента в функцию без копирования (и вызова конструктора для объектов), которое может оказаться долгим для сложных объектов. Правда, здесь лучше использовать константные ссылки.

Таким образом, примений указателей очень много.

Отслеживать

@Xyanight, вспомните, что массивы в Си представляются указателями (даже не в случае параметров). Например: int a[10], *b = a+5; . printf («%d\n»,b[0]); // распечатает значение a[5] Вообще указатели в Си позволяют писать более обобщенный код.

4 янв 2012 в 16:46
В качестве аргумента передаётся указатель на переменную, функция записывает туда значение.
27 июл 2015 в 14:05

А зачем нужны почтовые адреса? Можно же общаться сразу с людьми. Но иногда приходится поручать доставку письма почтальону, которому надо сначала сказать, куда конкретно он должен доставить письмо, так как один и тот же почтальон должен уметь единообразно для адресанта доставлять письма всем адресатам. Как? Лезть в его днк нельзя, нельзя его даже переучивать. Указатель – это такая величина, в которой можно хранить адрес. А ссылка – это нечто, ведущее себя как указатель, но имеющее при этом синтаксис сразу целевой величины, которую мы по этому адресу ищем. То есть поле на конверте с адресом Пети, притворяющееся самим Петей. На это поле на конверте можно наорать вместо Пети, а обидится Петя. По указателю же можно только нанять хулигана, поручив ему наорать на Петю. Совсем же по-другому выглядит, а результат тот же. В одном случае мы голос посрвали, в другом – явно послали посредника. Но по указателю можно послать: почтальона, шпиона, хулигана и киллера, а по ссылке – только наорать на адрес на конверте, спокойно ему что-то сказать или поглазеть прямо на Петин стол, но киллеры даже не поймут, чего от них хотят, а сами мы убивать не умеем.

Отслеживать
ответ дан 8 ноя 2022 в 13:12
Тарас Атавин Тарас Атавин
204 1 1 серебряный знак 9 9 бронзовых знаков

Чтобы выделять большое количество памяти. Советую прочитать про стек и кучу (heap). На стеке при большом объеме данных будет происходить переполнение.

Отслеживать
ответ дан 4 янв 2012 в 14:03
221 1 1 серебряный знак 10 10 бронзовых знаков

Вот именно! Автору вопроса нужно смотреть в сторону размерных и ссылочных типов данных, как они устроены, какие отличия, где хранятся, зачем их разделили и тд. К чему тут тролинг.

4 янв 2012 в 15:56
Нeт, я пoнял пpинцип paбoты, нo нe пoнял oднoгo — ЗAЧEM Малеха противоречивый бред? Не находите?
4 янв 2012 в 16:00
Согласен — автор не разобрался в данной теме!
4 янв 2012 в 16:13
@AlexWindHope ну значит плохо объясняли
4 янв 2012 в 16:14

public и private предполагает что код может быть использовн кем-то еще, кто может залезть своими руками куда не надо и все поломать.

Указатели (C++)

Указатель — это переменная, в которой хранится адрес памяти объекта. Указатели широко используются как в C, так и в C++ для трех основных целей:

  • для выделения новых объектов в куче,
  • передача функций другим функциям
  • для итерации элементов в массивах или других структурах данных.

В программировании в стиле C необработанные указатели используются для всех этих сценариев. Однако необработанные указатели являются источником многих серьезных ошибок программирования. Поэтому их использование настоятельно не рекомендуется, за исключением случаев, когда они обеспечивают значительное преимущество производительности, и нет неоднозначности в отношении того, какой указатель является владельцем указателя, который отвечает за удаление объекта. Современный C++ предоставляет интеллектуальные указатели для выделения объектов, итераторов для обхода структур данных и лямбда-выражений для передачи функций. Используя эти средства языка и библиотеки вместо необработанных указателей, вы сделаете программу более безопасной, проще отладить и упростить понимание и обслуживание. Дополнительные сведения см . в смарт-указателях, итераторах и лямбда-выражениях .

В этом разделе

  • Необработанные указатели
  • Константные и переменные указатели
  • новые и удаленные операторы
  • Интеллектуальные указатели
  • Практическое руководство. Создание и использование экземпляров unique_ptr
  • Практическое руководство. Создание и использование экземпляров shared_ptr
  • Практическое руководство. Создание и использование экземпляров weak_ptr
  • Практическое руководство. Создание и использование экземпляров CComPtr и CComQIPtr

См. также

Обратная связь

Были ли сведения на этой странице полезными?

Обратная связь

Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see: https://aka.ms/ContentUserFeedback.

Отправить и просмотреть отзыв по

Указатели

Указатели представляют собой объекты, значением которых служат адреса других объектов (переменных, констант, указателей) или функций. Как и ссылки, указатели применяются для косвенного доступа к объекту. Однако в отличие от ссылок указатели обладают большими возможностями.

Определение указателя

Для определения указателя надо указать тип объекта, на который указывает указатель, и символ звездочки *:

тип_данных* название_указателя;

Сначала идет тип данных, на который указывает указатель, и символ звездочки *. Затем имя указателя.

Например, определим указатель на объект типа int:

int* p;

Такой указатель может хранить только адрес переменной типа int , но пока данный указатель не ссылается ни на какой объект и хранит случайное значение. Мы его даже можем попробовать вывести на консоль:

#include int main()

Например, в моем случае консоль вывела «0x8» — некоторый адрес в шестнадцатеричном формате (обычно для представления адресов в памяти применяется шестнадцатеричная форма). Но также можно инициализировать указатель некоторым значением:

int* p<>;

Поскольку конкрентное значение не указано, указатель в качестве значения получает число 0. Это значение представляет специальный адрес, который не указывает не на что. Также можно явным образом инициализировать нулем, например, используя специальную константу nullptr :

int* p;

Хотя никто не запрещает не инициализировать указатели. Однако в общем случае рекомендуется все таки инициализировать, либо каким-то конкретным значением, либо нулем, как выше. Так, к примеру, нулевое значение в будущем позволит определить, что указатель не указывает ни на какой объект.

Cтоит отметить что положение звездочки не влияет на определение указателя: ее можно помещать ближе к типу данных, либо к имени переменной — оба определения будут равноценны:

int* p1<>; int *p2<>;

Также стоит отметить, что размер значения указателя (хранимый адрес) не зависит от типа указателя. Он зависит от конкретной платформы. На 32-разрядных платформах размер адресов равен 4 байтам, а на 64-разрядных — 8 байтам. Например:

#include int main() < int *pint<>; double *pdouble<>; std::cout

В данном случае определены два указателя на разные типы — int и double. Переменные этих типов имеют разные размеры — 4 и 8 байт соответственно. Но размеры значений указателей будут одинаковы. В моем случае на 64-разрядной платформе размер обоих указателей равен 8 байтам.

Получение адреса и оператор &

С помощью операция & можно получить адрес некоторого объекта, например, адрес переменной. Затем этот адрес можно присвоить указателю::

int number ; int *pnumber ; // указатель pnumber хранит адрес переменной number

Выражение &number возвращает адрес переменной number . Поэтому переменная pnumber будет хранить адрес переменной number . Что важно, переменная number имеет тип int, и указатель, который указывает на ее адрес, тоже имеет тип int. То есть должно быть соответствие по типу. Однако также можно использовать ключевое слово auto :

int number ; auto *pnumber ; // указатель pnumber хранит адрес переменной number

Если мы попробуем вывести адрес переменной на консоль, то увидим, что он представляет шестнадцатиричное значение:

#include int main() < int number ; int *pnumber ; // указатель pnumber хранит адрес переменной number std::cout

Консольный вывод программы в моем случае:

number addr: 0x1543bffc74

В каждом отдельном случае адрес может отличаться и при разных запусках программы может меняться. К примеру, в моем случае машинный адрес переменной number — 0x1543bffc74 . То есть в памяти компьютера есть адрес 0x1543bffc74, по которому располагается переменная number. Так как переменная x представляет тип int , то на большинстве архитектур она будет занимать следующие 4 байта (на конкретных архитектурах размер памяти для типа int может отличаться). Таким образом, переменная типа int последовательно займет ячейки памяти с адресами 0x1543bffc74, 0x1543bffc75, 0x1543bffc76, 0x1543bffc77.

Указатели в C++

И указатель pnumber будет ссылаться на адрес, по которому располагается переменная number, то есть на адрес 0x1543bffc74.

Итак, указатель pnumber хранит адрес переменной number, а где хранится сам указатель pnumber? Чтобы узнать это, мы также можем применить к переменной pnumber операцию &:

#include int main() < int number ; int *pnumber ; // указатель pnumber хранит адрес переменной number std::cout

Консольный вывод программы в моем случае:

number addr: 0xe1f99ff7cc pnumber addr: 0xe1f99ff7c0

Здесь мы видим, что переменная number располагается по адресу 0xe1f99ff7cc , а указатель, который хранит этот адрес, — по адресу 0xe1f99ff7c0 . Из вывода видно, что обе переменные хранятся совсем рядом в памяти

Получение значения по адресу

Но так как указатель хранит адрес, то мы можем по этому адресу получить хранящееся там значение, то есть значение переменной number. Для этого применяется операция * или операция разыменования («indirection operator» / «dereference operator»). Результатом этой операции всегда является объект, на который указывает указатель. Применим данную операцию и получим значение переменной number:

#include int main() < int number ; int *pnumber ; std::cout Address = 0x44305ffd4c Value = 25

Значение, которое получено в результате операции разыменования, можно присвоить другой переменной:

int n1 ; int *pn1 ; // указатель pn1 хранит адрес переменной n1 int n2 < *pn1>; // n2 получает значение, которое хранится по адресу в pn1 std::cout int x = 10; int *px = &x; *px = 45; std::cout

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *