Типизированные и нетипизированные указатели
Куда указывают указатели
Видеокурс для начинающих программистов. Вы узнаете, что такое динамические и статические структуры данных. Узнаете, что такое указатели и как с ними работать. Узнаете, как использовать память компьютера в своих программах, и как избежать ошибок, таких как утечки памяти. Кроме того, увидите реальные примеры, как и где применяются эти знания на практике. Подробнее... |
Думаю, вы уже знаете, что указатели могут быть типизированными и нетипизированными.
Типизированный указатель содержит ссылку на область памяти, где хранятся данные определенного типа (byte, real и т.п.).
Нетипизированные указатели содержат ссылку на область памяти, где могут храниться любые данные, поэтому их (нетипизированные указатели) удобно использовать в тех случаях, когда требуется работать с данными, структура и тип которых меняются в ходе выполнения программы (и/или заранее неизвестны).
Сами указатели хранятся в статической области памяти, поэтому их нужно объявлять, как и другие переменные.
При использовании нетипизированных и типизированных указателей надо быть внимательным, поскольку здесь легко можно совершить труднонаходимую ошибку. Например, если у нас в программе явно определены следующие типизированные указатели:
var P1 : ^integer; //Типизированный указатель P2 : ^single; //Типизированный указатель P3 : pointer; //Нетипизированный указатель
то такой код:
P1 := P2;
вызовет ошибку во время компиляции и программа не будет создана. Однако компилятор можно обмануть (сознательно или неосознанно), например, так:
P3 := P2; P1 := P3;
то есть мы таки записали в указатель Р1 значение указателя Р2. Кстати, можете взять этот приём на вооружение - в некоторых редких случаях это может быть необходимо. Однако в большинстве случаев это является ошибкой (хотя компилятор её не заметит и программа будет создана). Эту ошибку показывает следующий пример:
program myprog; var iNum : integer; //Переменная целого типа fNum : single; //Переменная вещественного типа P1 : ^integer; //Типизированный указатель P2 : ^single; //Типизированный указатель P3 : pointer; //Нетипизированный указатель begin iNum := 100; fNum := 100; WriteLn('iNum = ', iNum); //Выведет 100 WriteLn('fNum = ', fNum:0:1); //Выведет 100.0 P1 := @iNum; //Р1 = адрес переменной iNum P2 := @fNum; //Р2 = адрес переменной fNum WriteLn('iNum = ', P1^); //Выведет 100 //Это не вызовет ошибки, однако так делать опасно, //потому что теперь в память по адресу, где должна храниться //целочисленная переменная, записаны данные вещественного типа, //и искажение данных гарантировано P3 := P2; P1 := P3; //Выведет 1120403456 или что-то типа того iNum := P1^; WriteLn('iNum = ', iNum); ReadLn; end.
Здесь, во-первых, мы разрываем связь между указателем Р1 и переменной iNum. Ну это ладно, это не страшно. Давайте посмотрим, что же мы натворили:
- Адрес переменной fNum, который хранился в указателе Р2, мы поместили в нетипизированный указатель Р3. При этом помним, что в переменной fNum у нас хранится вещественное число 100.
- Далее в указатель Р1 мы записали значение указателя Р3. То есть таким образом мы в указатель Р1 записали адрес переменной fNum.
- И теперь указатель Р1 содержит ссылку на место в памяти, где хранится число 100.
- Однако, если мы получим данные, на которые ссылается указатель Р1, и сохраним их в переменную iNum, то это будет не 100, а какое-то непонятное число.
Таким образом мы получили “искажённую картину мира”, то есть наши ожидания не оправдались и мы не смогли преобразовать через указатели вещественное число в целое. А всё потому, что представление целых и вещественных чисел в памяти компьютера совершенно разное. И не зря компилятор ругается, когда мы пытаемся напрямую присвоить значение типизированного указателя одного типа указателю другого типа.
Вот такие (и другие) неприятности поджидают вас в работе с указателями. Если хотите изучить тему указателей более глубоко, то вам сюда.
Подписаться на канал в YouTube
Вступить в группу "Основы программирования" Подписаться на рассылки по программированию |
Директивы компилятора
Как это ни странно, но даже многие опытные программисты не используют директивы компилятора, считая их чем-то ненужным и бесполезным. А между тем, директивы компилятора - это очень классная штука. Если их умело применять в своих программах, то можно существенно сократить время на разработку и уменьшить количество рутинных операций. Подробнее... |