Указатель - это переменная, содержащая адрес переменной. Указатели широко применяются в Си - отчасти потому, что в некоторых случаях без них просто не обойтись, а отчасти потому, что программы с ними обычно короче и эффективнее. Указатели и массивы тесно связаны друг с другом: в данной главе мы рассмотрим эту зависимость и покажем, как ею пользоваться. Наряду с goto указатели когда-то были объявлены лучшим средством для написания малопонятных программ. Так оно и есть, если ими пользоваться бездумно. Ведь очень легко получить указатель, указывающий на что-нибудь совсем нежелательное. При соблюдении же определенной дисциплины с помощью указателей можно достичь ясности и простоты. Мы попытаемся убедить вас в этом.
Изменения, внесенные стандартом ANSI, связаны в основном с формулированием точных правил, как работать с указателями. Стандарт узаконил накопленный положительный опыт программистов и удачные нововведения разработчиков компиляторов. Кроме того, взамен char* в качестве типа обобщенного указателя предлагается тип void* (указатель на void).
Начнем с того, что рассмотрим упрощенную схему организации памяти. Память типичной машины подставляет собой массив последовательно пронумерованных или проадресованных ячеек, с которыми можно работать по отдельности или связными кусками. Применительно к любой машине верны следующие утверждения: один байт может хранить значение типа char, двухбайтовые ячейки могут рассматриваться как целое типа short, а четырехбайтовые - как целые типа long.
Адресная арифметика – это способ вычисления адреса какого-либо объекта при помощи арифметических операций над указателями, а также использование указателей в операциях. Адресную арифметику также называют арифметикой над указателями.
Арифметика с указателями дает программисту возможность работать с разными типами одинаковым способом. В то же время мощная арифметика с указателями может стать причиной ошибок. Из-за сложностей использования указателей многие современные языки программирования высокого уровня (например, Java или C#) не разрешают прямой доступ к памяти с использованием адресов. В то же время указатели в С – достоинство этого языка, и не зря ОС пишут в основном на С.
Язык С разрешает операцию сравнения указателей одинакового типа, при этом, по сути, сравниваются адреса в памяти.
Сравнение указателей в общем случае некорректно! Это связано с тем, что одним и тем же физическим адресам памяти могут соответствовать различные пары значений «сегмент‑смещение» (при страничной (сегментной) организации памяти разные страницы (сегменты) виртуальной памяти могут загружать в один и тот же физический блок оперативной памяти).Но при определенных условиях указатели все же можно сравнивать. Если p и q указывают на элементы одного и того же массива, то такие отношения, как <, >= и т.д., работают надлежащим образом. Например, p < q истинно, если p указывает на более ранний элемент массива, чем q. Отношения == и != тоже работают.Также любой указатель можно осмысленным образом сравнить на равенство или неравенство с NULL.Но ни за что нельзя ручаться, если вы используете сравнения при работе с указателями, указывающими на разные массивы. Если вам повезет, то на всех машинах вы получите очевидную бессмыслицу. Если же нет, то ваша программа будет правильно работать на одной машине и давать непонятные результаты на другой.