Указатели в С++ для начинающих Поверхностное знакомство

Сайт не является учебником по программированию. Это только небольшой авторский сборник информации в помощь начинающим программистам.

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

  • Указатель — это вид переменной, способной хранить адрес памяти

Чтобы компилятор умел работать с переменными, функциями, объектами классов и т. п. — компилятору нужно постоянно обращаться к памяти компьютера. Почти все сущности в языке С++ имеют свой собственный адрес памяти. Так почему бы не иметь возможности запоминать адреса сущностей в отдельную переменную? Создатели языка С создали такой вид переменной, которая способна хранить адреса. Язык C++ является надмножеством языка C, и многое из C в C++ работает. К этим рабочим возможностям можно отнести указатели.

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

И еще есть разные причины, например, только использую указатель или ссылку можно наслаждаться добротами полиморфизма.

  • Указатели в С++ трудно понимаемы оттого, что они имеют двойственную природу

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

Чтобы было понятнее, я буду называть указатель, который хранит адрес, указателем, а указатель, который используется для работы с объектом, косвенной сущностью указателя. Эти два определения перепутать, надеюсь, сложно.

Итак, чтобы объявить указатель, нужно использовать звёздочку:

Объявление указателя мало отличается от объявления переменной, добавляется только звёздочка слева от имени переменной. Научиться читать или понимать правильно — задача достаточно мозгоёмкая. Местоположение звёздочки, близко к имени переменной или близко к типу, не играет никакого значения, но в зависимости от стиля написания кода, может вводить в заблуждение:

  • Каждая переменная, которая должна быть указателем, должна быть объявлена названным именем с указанием на то, что она указатель, т. е. слева должна быть звёздочка

Так как указатель предназначен для хранения адресов, то использовать указатель можно для прямой работы с памятью. К тому же вывод значения указателя на экран с помощью cout будет выводить на экран именно адрес, а не значение объекта, который на этом адресе успел прописаться:

Чтобы работать посредством указателя с какой-то сущностью — нужно обязательно связать указатель с этой сущностью. Делается это присваиванием в указатель адреса сущности:

В показанном сейчас примере используется инициализация указателя значением. Если объявлять какое-то новое имя переменной и тут же в неё присваивать значение — это будет процесс инициализации. Вот звёздочка для имени указателя используется именно при объявлении нового имени, в дальнейшем звёздочка используется только для косвенной сущности указателя. Использование звёздочки для обращения к косвенной сущности указателя называется разыменованием указателя. Сейчас вернёмся к последнему примеру, слегка его дополнив кодом:

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

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

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

  • Изменение значения переменной, прописавшейся на адресе, с которым связан указатель, влечёт с собой изменение значения косвенной сущности указателя
  • Изменение состояния косвенной сущности указателя влечёт с собой изменение состояния переменной, прописавшейся на адресе, связанным с указателем

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

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

Для взятия адреса используется знак амперсанда: &

Чтобы присваивать в указатель значение, нужно присваивать в указатель адреса, будь это хоть адрес указателя, хоть адрес функции.

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

Иногда имеет смысл обнулять указатель, наподобие обнуления обычных переменных, для этого в компиляторах используется ключевое слово NULL, дибо nullptr. nullptr — это ключевое слово появилось в компиляторах свежее 2010г. Обозначают эти ключевые слова указатель вникуда. nullptr отличается от NULL тем, что NULL указывает на нулевой адрес, который на некоторых компьютерах не обязательно обозначает ничто и может иметь иное этому назначение, поэтому если ваш компилятор поддерживает nullptr, используйте это ключевое слово.

Подводим итоги:

  • Указатель — это вид переменной для хранения адреса памяти
  • Указатель объявляется с помощью звёздочки
  • Указатель владеет способностью работать со своей косвенной сущностью
  • Чтобы работать с косвенной сущностью указателя, нужно использовать звёздочку, исключение — объявление самого указателя
  • У многих сущностей языка С++ можно взять адрес памяти, для этого нередко используется знак амперсанда: &
  • В указатель можно присваивать адреса
  • У указателя двойственная природа: сам указатель и его косвенная сущность

Лучше всего, чтобы указатель на что-то указывал всегда. Нет ничего хорошего в неинициализированных переменных. Пусть указатель указывает вникуда, тогда есть возможность проверки указателя, если же указатель указывает непонятно куда, то можно об этом легко забыть, а потом компилятор даст непонятно что. Поэтому инициализируйте указатели сразу.

Статья полностью изменена из первоначального варианта 26.04.2017г.
Все комментарии на сайте проверяются, поэтому ваш комментарий может появиться не сразу. Для вставки кода в комментарий используйте теги: [php]ВАШ_КОД[/php]

Один комментарий: Указатели в С++ для начинающих Поверхностное знакомство

  • Sergio говорит:

    Помогите найти косяк… Похоже я запутался с указателями. 😳
    Интересуют вопросы в тексте программы, а также самый главный: как сделать так, чтобы закомментированный кусок заработал?

    Извиняюсь за корявые двойные кавычки…

    Автор сайта отвечает:
    вы, похоже, время теряете на то, чтобы каждый раз код руками в комментариях написать. Можно ведь скопировать его из блокнота, только подправлять комментарии нужно будет. (исходники ведь не с воздуха, они на диске хранятся, открыть блокнотом можно) И никаких проблем с самим кодом не будет.
    А иначе и вы и я терем много времени. Чтобы расшифровывать такой вид кода время ведь тоже нужно.

    Как ни странно, но код в комментах никогда руками не набирал, копировал из Borland 5.02 C++.
    [& quot]; — тег html, двойная кавычка.

    Автор сайта отвечает:
    я его расшифровал, знания html небольшие, но есть) За это не парьтесь.
     
    Не ленитесь открывать блокнотом. Оттуда не так криво копирует как из сред разработок.
     
    и такие вот вопросы, как этот, лучше в форумы задавать. Там ответят точно быстрее.
    Разбираться в чужом коде иногда проблема. Стиль написания совсем не мой.

    Почему отображается только первая цифра? Потому что char *mQ[5]; обозначает массив из 5 символов. Т.е. каждый элемент массива вмещает не больше чем 1 символ, массив одиночных символов не есть массив слов.

    Ответ на остальное не знаю когда напишу.

    И правда, не хватает форума на вашем сайте 😀

    Автор сайта отвечает:
    числа это совсем не левые, а коды символов, которые принадлежат элементам массива. Вы же объявили тип int и присваиваете к нему тип char. символу "1" соответствует числовой код 49 и так далее.
     
    Пример прост.

    то же самое происходит у вас.

    Спасибо, с этим вроде разобрался…

    Автор сайта отвечает:
    И в строке 41 попробуйте
    char *B[5] = {0,};

    Пробовал, от «вылетов» и прочих глюков помогло, спасибо.
    Но вот по части if-ов у меня явный косяк, не работает — не выводятся элементы массива указателей… 😕

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

    и еще в глаза бросилось, что присваивание в конце не к разыменованному массиву, а к указателю идет. (это так, просто в глаза бросилось, возможно ошибка, возможно так и задумано, не знаю)

    Это намного упрощенная версия моей программы, поэтому все условия и не писал 😮

    Автор сайта отвечает:
    Так как вы достаточно активно изучаете мои материалы, то держите подсказку. Недавно в одном из комментариев я просил именно вас проверить в вашем проекте что-то похожее (вы там про все способы описания массивов слов спрашивали), но похоже, что вы решили, что вам это не нужно и зря.

    Это относится к строке 42
    ===================

    Это не решение, а подсказка.
     
    и еще вместо приведения типов (int = char) используйте функции работы со строками. Вы же хотели строку к цифре привести, так и используйте готовую для этого функцию. atoi (http://ru.wikipedia.org/wiki/Atoi)
    Пример

    Это вам для решения тоже нужно знать.

    Огроменное спасибо за atoi, если бы раньше знал про её существование, то и не задавал бы глупых вопросов… 😉

    P.S. Хотелось бы в будущем на вашем ресурсе увидеть статью (а может и серию статей): Преобразование типов переменных: int в char, char в int и др.. Новичкам, как мне, это будет крайне полезная информация.
     
    Выложу кусок кода, который работает как изначально и задумывалось, может кому-нибудь пригодится…

     
    admin, честное слово, на этот раз копировал из блокнота))

    Автор сайта отвечает:
    Потом исправлю, только хочу пояснить, что решение какой-то задачи без условия самой задачи новичкам полезным в принципе быть не может.

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

Ваш e-mail не будет опубликован.

Поиск

 
     
Яндекс.Метрика

НАГРАДИ АВТОРА САЙТА
WEBMONEY
R375024497470
U251140483387
Z301246203264
E149319127674

Подождите, идет подготовка к зависанию компьютера...

Выражаю свою признательность

  • Максиму очень признателен за указание на мои ошибки и неточности.
  • Sergio ===> за оказание помощи в исправлении моих ошибок
  • Gen ===> за правильное стремление помочь другим новичкам и выявления моих ошибок