33 0 1MB
Федеральное агентство связи Ордена Трудового Красного Знамени Федеральное государственное бюджетное образовательное учреждение высшего образования «Московский технический университет связи и информатики»
Кафедра «Математической кибернетики и информационных технологий»
Отчет по лабораторной работе по дисциплине «Низкоуровневое программирование» на тему: «Основы работы с указателями в языке программирования С»
Выполнил: Студент группы БВТ1801 Тимофеев И.И. Проверил: Фатхуллин Т.Д.
Москва 2021
Цель работы: Изучить и практически освоить основы работы с указателями в языке программирования C.
2
Задание
Создайте проект для ознакомительного использования указателей. Создайте проект с объявлением и использованием указателей. Создайте проект для побайтового заполнения области памяти. Создайте проект с использованием указателей общего назначения. Создайте проект для побайтового заполнения целочисленного значения с использованием указателей общего назначения. Создайте проект с многоуровневой адресацией. Согласно пунктам выполнения лабораторной работы, сделать необходимые снимки экрана. Изучить полученную информацию и оформить ее в соответствии с требованиями раздела «Содержание отчета».
3
Краткая теория ПЕРЕМЕННЫЕ И ИХ ПРЕДСТАВЛЕНИЕ В ПАМЯТИ Переменная — это поименованная область памяти под определенный тип данных. Как известно, каждая переменная хранится в памяти компьютера и имеет свой адрес, по которому она записана. Память компьютера можно представить в виде таблицы с ячейками (рисунок 1) по одному байту, каждая из которых имеет свой адрес, который записывается цифрами шестнадцатеричной системы.
Рисунок 1 – Пример представления памяти компьютера Для того чтобы получить адрес переменной необходимо перед её именем написать символ “&”. Кроме того, каждая переменная в зависимости от её типа, занимает в памяти различное количество байт. Для того, чтобы узнать размеры различных типов обычно используется функция sizeof(). В качестве примера, напишем программу, которая бы получала адрес переменных a,b и количество байт, отведенных для каждой из этих переменных (рисунок 2).
Рисунок 2 – Получение адреса переменных в памяти 4
На нашем компьютере мы получили два адреса 0x12ff60 и 0x12ff54. Так как это переменные целого типа (int) они занимают в памяти компьютера 4 байта. Как видно из рисунка, переменные указанного типа располагаются в памяти компьютера не подряд, а произвольным образом. Это выглядит примерно так (рисунок 3):
Рисунок 3 – Пример расположения переменных в памяти Для хранения адресов переменных и возможности получения данных непосредственно по адресу в памяти существуют специальные переменные, которые называются указателями. Указатель – переменная, предназначенная для хранения адреса какоголибо объекта (переменной, массива и тд.) в памяти компьютера. Согласно определению, указатель это переменная, следовательно, как и любая переменная, он должен быть как-то объявлен. Делается это в языке С следующим образом: - сначала указывается Базовый тип переменной, которая будет храниться в памяти, по адресу указателя. -далее следует специальный символ «*», называемый оператором косвенной адресации, который сообщает компилятору, что мы собираемся объявить указатель на переменную определенного типа (например, int). После звездочки пишется идентификатор (имя) указателя. Базовый тип указателя (т.е. l-value выражения объявления указателя) может принимать любой тип данных (как простой, определенный стандартом языка, так и составной, определенный стандартом языка или пользователем). После объявления локального указателя до первого присвоения он содержит неопределенное значение. Указатель, не ссылающийся в текущий момент времени на конкретный объект, должен содержать нулевое значение. Нуль (NULL или 0) используется, потому что С, в соответствии со стандартом, гарантирует отсутствие чего-либо по нулевому адресу. Следовательно, если указатель равен нулю, то это значит, во-первых, что он ни на что не ссылается, а во-вторых – что его сейчас нельзя использовать.
5
Выполним листинг (рисунок 4):
Рисунок 4 – Пример обнуления указателя ОПЕРАЦИИ НАД УКАЗАТЕЛЯМИ В языке С определены две операции для работы с указателями: операция разыменования (*) и операция взятия адреса (&). Оператор & - это унарный оператор, возвращающий адрес своего операнда (r-value). Оператор * - это унарный оператор, возвращающий значение переменной, расположенной по указанному адресу. Для лучшего понимания выполним следующий листинг (рисунок 5):
6
Рисунок 5 – Операции для работы с указателями В рамках данной программы мы выполнили операции взятия адреса, с последующим присвоением его указателю и операцию разыменования т.е. получения данных по адресу, на который «указывает указатель». Результат выполнения листинга представлен ниже на рисунке 6:
Рисунок 6 – Пример использования операций взятия адреса и разыменования Модифицируем предыдущий листинг и посмотрим, какое количество памяти занимают переменные разных типов (рисунок 7):
7
Рисунок 7 – Определение размеров переменных Как видно из рисунка 7 переменная «х» типа char занимает в памяти 1 байт. Но при этом, указатель «*р» на переменную х типа char занимает в памяти 8 байт. Этот пример иллюстрирует еще одну особенность указателей: указатель в памяти компьютера, в зависимости от архитектуры установленного процессора (32х-64х) занимает 4-8 байт. Объем занимаемой указателем памяти НЕ зависит от типа данных, на которые он указывает! Адресная арифметика В языке С допустимы только две арифметические операции над указателями: суммирование и вычитание. Все остальные арифметические операции запрещены. А именно: нельзя делить и умножать указатели, 8
суммировать два указателя, выполнять над указателями побитовые операции, суммировать указатель со значениями, имеющими тип float или doubleи т.д. Операции адресной арифметики подчиняются следующим правилам: После выполнения операции увеличения над указателем, данный указатель будет ссылаться на следующий объект своего базового типа. После выполнения операции уменьшения – на предыдущий объект. Применительно к указателям на char, операции адресной арифметики выполняются как обычные арифметические операции, потому что длина объекта char всегда равна 1 (рисунок 8).
Рисунок 8 – Количество байт, занимаемое в памяти переменными различных типов Операции адресной арифметики не ограничены увеличением (инкрементом) и уменьшением (декрементом). Например, к указателям можно добавлять целые числа или вычитать из них целые числа. В качестве примера выполним следующий листинг (рисунок 9):
9
Рисунок 9 – Пример выполнения операций адресной арифметики Кроме суммирования и вычитания указателя и целого, разрешена еще одна операция адресной арифметики: можно вычитать два указателя. Вычитание указателей друг из друга определено только в том случае, если оба указателя указывают на элементы одного и того же массива. Результатом вычитания одного указателя из другого будет количество элементов массива (целое число) между этими указателями. Помимо всего названного ранее, указатели можно сравнивать. Для сравнения указателей используются операции = =, >, ptr2=&ptr1, который в свою очередь будет ссылаться на значение
28