Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование C++ (1 сем, мага) / Лабы / Лабораторная работа №2

.pdf
Скачиваний:
0
Добавлен:
01.09.2026
Размер:
330 Кб
Скачать
Министерство науки и высшего образования Российской Федерации
Федеральное государственное автономное образовательное учреждение высшего
образования
«Национальный исследовательский ядерный университет «МИФИ»
Кафедра экспериментальной ядерной физики и космофизики (№7)
Программирование на С и С++
Выполнил: студент группы M22-105 Васильева А.П.
Москва, 2022
Цель работы: используя механизм указателей, приведение типов памяти и арифметические операции с указателями, получить доступ к определенным байтам заданного числа.
Введение
Указатель это адрес памяти, распределяемой для размещения идентификатора (в
качестве идентификатора может выступать имя переменной, массива, структуры, строкового
литерала). В том случае, если переменная объявлена как указатель, то она содержит адрес
памяти, по которому может находится скалярная величина любого типа. При объявлении
переменной типа указатель, необходимо определить тип объекта данных, адрес которых
будет содержать переменная, и имя указателя с предшествующей звездочкой (или группой
звездочек). Формат объявления указателя:
спецификатор-типа [ модификатор ] * имя-указателя .
Спецификатор-типа задает тип объекта и может быть любого основного типа, типа структуры, смеси (об этом будет сказано ниже). Задавая вместо спецификатора-типа
ключевое слово void, можно своеобразным образом отсрочить спецификацию типа, на
который ссылается указатель. Переменная, объявляемая как указатель на тип void, может
быть использована для ссылки на объект любого типа. Однако для того, чтобы можно было
выполнить арифметические и логические операции над указателями или над объектами, на
которые они указывают, необходимо при выполнении каждой операции явно определить тип
объектов. Такие определения типов может быть выполнено с помощью операции приведения
типов.
В качестве модификаторов при объявлении указателя могут выступать ключевые слова
const, near, far, huge. Ключевое слово const указывает, что указатель не может быть изменен в программе.
Ход работы
Исходное число: AABBCCDD. Требовалось получить: BBCC. Для этого была написана программа, имеющая следующий код:
Переменной block типа unsigned long присваивается значение AABBCCDD, а указатель-переменная ptr инициализируется значением адреса первого байта переменной block. Т.к. это указатель типа void, он не содержит информации о размере объекта в памяти.
Исходное число занимает в памяти 4 байта. С помощью указателей были выделены
второй и третий байты переменной block: CC и BB. При этом выполнялось приведение типа (void*) к типу (char*) и последующий сдвиг указателя на нужное число байт. Результаты были сохранены в переменные типа char – сс и bb. Соответственно получено
cc = 0xcc, bb = 0xbb.
Конечное число должно занимать два байта, поэтому далее работа шла с переменными
типа unsigned short.
Выполнено преобразование bb к типу unsigned short и побитовый сдвиг влево на 8 битов (1 байт). В результате получено число 0xBB00, которое было сохранено в переменную bb00.
Далее cc преобразована к типу unsigned short и результат записан в переменную cc00. При этом в первый байт прилетает мусор, который далее при сложении bb00 + cc00 дает 0xBACC, а не 0xBBCC. Поэтому с помощью операции поразрядного И (&) первый байт был обнулен.
Выполнено сложение значений переменных bb00 и cc00. Результат равен требуемому числу 0xBBCC и записан в переменную bbcc.
Число 0xBBCC было также получено альтернативным способом.
Снова создан указатель типа (void*), но теперь на переменную cc, и выполнено
приведение к типу (unsigned short*). Указатель был разыменован в переменную
result, которая приняла значение 0xBBCC.
Основной сложностью при выполнении работы стало то, что при простом прибавлении
к переменной, содержащей значение 0xBB00, переменной сс в результате получалось
0xBACC. Требуемый результат удалось получить путем явного приведения cc к unsigned short и последующего обнулению старшего байта. Решение с использованием этого
подхода описано выше.
Проблема возникала по следующей причине. При преобразовании char (знакового типа) в unsigned short сначала происходило расширение знака до short, а затем преобразование short в unsigned short. Число интерпретируется как отрицательное,
т.к. выходит за лимиты типа char (0xCC > 0x7f). Двоичная запись
числа 0xCC: 110011002. Старший бит 1 и при преобразовании в short происходит
расширение знака путем добавления единиц. В результате получается:
1111 1111 1100 1100, что в шестнадцатеричной системе счисления соответствует 0xFFCC. Поэтому стоит избегать преобразований между знаковыми и
беззнаковыми типами данных. Наиболее корректным решением проблемы является
использование unsigned char вместо char:
В этом случае преобразование и последующее сложение с 0xBB00 выполняется
корректно:
Заключение
В лабораторной работе освоены механизм указателей, арифметические операции с указателями и приведение типов памяти.
Решена задача по получению нового числа путем выделения определенных байтов исходного и последующим их объединением.
К выводам по работе так же можно отнести:
1. В памяти байты хранятся от младшего к старшему (little endian), т.е. более старший бит имеет больший адрес. У такого порядка хранения байтов имеются следующие достоинства:
Возможность «неявной типизации» целых чисел при чтении меньшего объёма байт (при
условии, что читаемое число помещается в диапазон). Так, если в ячейке памяти
содержится число 0x00000022, то прочитав его как short (два байта) мы получим число
0x0022, прочитав один байт — число 0x22.
В то же время при увеличении размера (количества байт) операнда, значение его первого
байта остаётся прежним: 3210 → 3210’0000.
Упрощается доступ к младшим байтам числа, а большинство арифметических операций
производится от младших байтов к старшим. Соответственно такие операции
упрощаются, что может быть существенно для длинных чисел.
2. Следует избегать преобразований между знаковыми и беззнаковыми типами.