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

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

  • автор:

Для чего же нужны указатели?

Всем доброго времени суток. Никак не могу понять для чего нужны указатели? Объясните простым, человеческим языком, пожалуйста.
Сам я сначала думал чтобы через указатели изменять значения переменных, но зачем это делать через указатели, если можно напрямую данной переменной присвоить новое значение.

  • Вопрос задан более трёх лет назад
  • 9217 просмотров

2 комментария

Оценить 2 комментария

Vitaly @vt4a2h Куратор тега C++

Напишите сначала, чтовы сделали для того, чтобы разобраться с этим вопросом и что конкретно не поняли. Например, читали ли вы книги по C++, статьи, пробовали ли писать программы? Если нет, то почитатйте. Просто так объяснять не имеет смысла, вы поймёте не больше, чем сейчас.

AtomKrieg

«Используют ли он указатели? Это хороший признак. Многие программисты на Си просто не знают, как заставить работать указатели. Я, как правило, не отказываюсь от кандидата из-за отсутствия у него какого-то навыка. Однако я обнаружил, что понимание указателей в Си — это не навык, а способность. В начале первого курса на факультете кибернетики набирается человек 200 вундеркиндов, писавших в четырехлетнем возрасте игрушки для Atari 800 на BASIC’е. Они хорошо проводят время, изучая Паскаль, но в один прекрасный день профессор заводит речь об указателях, и они внезапно перестают понимать. То есть абсолютно. 90% потока переходит на отделение политологии, объясняя это тем, что на кибернетике мало симпатичных студенток. На самом же деле, по неизвестной причине часть человечества просто рождается без того отдела мозга, который понимает указатели. Указатели — это не навык, а способность, требующая особого мышления, и некоторые люди им просто не обладают.»
Джоэль Спольски предлагает вам изучать политологию вместо программирования.

Решения вопроса 1

Nipheris

Станислав Макаров @Nipheris Куратор тега C++

Ну и я тогда тоже попробую.

Посмотрим на вопрос с другой стороны.
Переменная — это контейнер для значения. Что нам вообще нужно для работы с переменной? Что нам нужно, чтобы считать/записать значение из/в нее? Нам нужны тип данных и адрес в памяти.
Тип данных — отдельная история, оставим пока его в стороне. Скажем только, что он определяет допустимые операции со значением и количество памяти, необходимое для хранения значения.
Поговорим об адресе. В языках, где доступна прямая работа с памятью, любая переменная имеет свой адрес в памяти. Также, в чуть более узком смысле, переменная находится в такой области памяти, в которую разрешена запись. Не имеет особого значения, какой структурой данных управляется эта память — стеком или кучей — важно, чтобы на момент использования эта память была доступна.
Корректный адрес в памяти — это и уникальный «ключ» переменной, ее отличительная черта. Работая с переменными, программист на низкоуровневом языке неизбежно работает с адресами.
Другой вопрос — это способ работы с адресом переменной. Когда вы создаете обычную локальную переменную, в работе с ней принимает участие компилятор. Когда вы пишете int a, компилятор (если не вдаваться в детали) размещает у себя в таблице идентификаторов пару: (имя_переменной, адрес_в_памяти). Обычная локальная переменная «а» (еще ее называют «автоматическая переменная») — способ создания переменной средствами компилятора. Компилятор освободит память, занимаемую этой переменной, когда она уйдет из области видимости. Однако, пока вам точно известно, что эта переменная «живет», вы можете совершенно спокойно получить ее адрес с помощью операции & — компилятор отдаст вам его из своей таблицы идентификаторов.

Но «переменные» в широком смысле можно создавать не только средствами компилятора, но и вручную с помощью malloc (Си) или new (С++). Эти динамические переменные живут столько, сколько вам нужно — вы их создаете, вам их и уничтожать. Об этих переменных компилятор ничего не знает, т.к. вы создаете их динамически во время выполнения программы. Для доступа к этим переменным вам также нужен адрес, но у компилятора его не попросишь: поэтому необходимо самому сохранять те адреса, что вернули вам функция malloc или оператор new. Этот адрес вы можете сохранить в ДРУГОЙ переменной, и такая переменная, хранящая адреса — и есть указатель (кроме того, если указатель не бестиповый (void*) то его тип (float*) еще и подсказывает нам тип переменной, на которую он указывает (float)).

Очень важно, что в указатель можно сохранить адрес ЛЮБОЙ переменной — как автоматической, которую вам создал компилятор, так и «ручной» — которую создали ВЫ с помощью malloc/new. И передать, например, этот адрес в функцию. Фактически, в языке Си указатели это и есть способ передачи САМОЙ ПЕРЕМЕННОЙ в функцию, а не ее ЗНАЧЕНИЯ на момент вызова. В C++ есть еще ссылки, но это отдельная история (ссылки — это указатель, «обернутый» в обычный идентификатор), по ним задайте отдельный вопрос.

если можно напрямую данной переменной присвоить новое значение.

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

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

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

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 золотых знака 37 37 серебряных знаков 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

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

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

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

Указатель

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

int i = 0;int *ptr = &i;

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

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

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

// gcc -o pointer pointer.c && ./pointer 
#include
#include // Эта функция не будет работать, так как в Си функция передаётся по значению.
// Внесённые изменения не действительны за пределами функции.
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#!

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

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