From dfe1612ded9ec8b0c83ccd055484cc0fe2f96688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC=20=D0=A0=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=D0=B7=D0=B8=D0=BD?= Date: Wed, 17 Jan 2024 19:41:10 +0000 Subject: [PATCH] Upload files to '' --- readme_lr4.md | 487 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 readme_lr4.md diff --git a/readme_lr4.md b/readme_lr4.md new file mode 100644 index 0000000..1cf104e --- /dev/null +++ b/readme_lr4.md @@ -0,0 +1,487 @@ +## Лабораторная работа № 4 +#### 1. Представление данных в памяти + +Начнем с реализации функции `nibble_to_hex()` и тестов для нее. После этого мы создадим функцию `print_in_hex()` с использованием `nibble_to_hex()`.
+ +``` +#include +#include + +using namespace std; + +// Функция для представления nibble (4 бит) в шестнадцатеричной системе +char nibble_to_hex(uint8_t i) { + // Массив цифр в шестнадцатеричной системе + char digits[] = "0123456789abcdef"; + + // Проверка на корректность аргумента + assert(0x0 <= i && i <= 0xf); + + // Возвращаем символ для nibble из массива digits + return digits[i]; +} +``` +Напишем реализацию функции `print_in_hex()` для печати одного байта в шестнадцатеричной и двоичной форме:
+``` +// Функция для печати одного байта в шестнадцатеричной и двоичной форме +void print_in_hex(uint8_t byte) { + // Печать в шестнадцатеричной форме + cout << "Hex: " << nibble_to_hex(byte >> 4) << nibble_to_hex(byte & 0xf) << endl; + + // Печать в двоичной форме + cout << "Binary: "; + for (int i = 7; i >= 0; --i) { + cout << ((byte >> i) & 1); + } + cout << std::endl; +} +``` +Итак, выполним проверку работы `nibble_to_hex()`, добавим в программу функцию-тест, вызываемую в начале main(): +``` +int main() { + + // Тестирование функции nibble_to_hex + + assert(nibble_to_hex(0x0) == '0'); + assert(nibble_to_hex(0x1) == '1'); + assert(nibble_to_hex(0x2) == '2'); + assert(nibble_to_hex(0x3) == '3'); + assert(nibble_to_hex(0x4) == '4'); + assert(nibble_to_hex(0x5) == '5'); + assert(nibble_to_hex(0x6) == '6'); + assert(nibble_to_hex(0x7) == '7'); + assert(nibble_to_hex(0x8) == '8'); + assert(nibble_to_hex(0x9) == '9'); + assert(nibble_to_hex(0xa) == 'a'); + assert(nibble_to_hex(0xb) == 'b'); + assert(nibble_to_hex(0xc) == 'c'); + assert(nibble_to_hex(0xd) == 'd'); + assert(nibble_to_hex(0xe) == 'e'); + assert(nibble_to_hex(0xf) == 'f'); +``` +Также проверку можно выполнить в цикле чтобы код не был таким громоздким: +``` +for (int i = 0; i <= 0xf; ++i) { + assert(nibble_to_hex(i) == nibble_to_hex(i)); + } +``` +Теперь напечатаем байт в шестнадцатеричной и двоичной форме:<>br +``` +using namespace std; + +// Функция для печати одного байта в шестнадцатеричной и двоичной форме +void print_in_hex(uint8_t byte) { + + // Печать в шестнадцатеричной форме + cout << "Hex: " << nibble_to_hex(byte >> 4) << nibble_to_hex(byte & 0xf) << endl; + + // Печать в двоичной форме + cout << "Binary: "; + for (int i = 7; i >= 0; --i) { + cout << ((byte >> i) & 1); + } + cout << endl; +} +``` +Теперь заключим преобразование типов в функцию `const uint8_t* as_bytes(const void* data)`: +``` +// Функция для преобразования типов +const uint8_t* as_bytes(const void* data) { + return reinterpret_cast(data); +} +``` +Также напишем функцию для печати массива байтов: +``` +// Функция для печати массива байтов +void print_in_hex(const void* data, size_t size) { + const uint8_t* bytes = as_bytes(data); + for (size_t i = 0; i < size; i++) { + print_in_hex(bytes[i]); + + // Для удобства чтения: пробелы между байтами, по 16 байт на строку. + if ((i + 1) % 16 == 0) { + cout << '\n'; + } else { + cout << ' '; + } + } + cout << endl; +} +``` +Теперь функция `as_bytes` принимает указатель на данные и возвращает указатель на байты этих данных, используя `reinterpret_cast`. Пример использования данной функции добавлен в `main()`, где мы создаем переменные u8, u16 и u32, присваиваем им значение 0x42, а затем выводим их байтовое представление, используя функцию `print_in_hex`: +``` +int main() { + uint8_t u8 = 0x42; + uint16_t u16 = 0x42; + uint32_t u32 = 0x42; + + cout << "u8 bytes: "; + print_in_hex(&u8, sizeof(u8)); + cout << '\n'; + + cout << "u16 bytes: "; + print_in_hex(&u16, sizeof(u16)); + cout << '\n'; + + cout << "u32 bytes: "; + print_in_hex(&u32, sizeof(u32)); + cout << '\n'; + + return 0; +} +``` +Добавим функцию `bit_digit` для проверки битов переменных u8, u16, и u32, и их вывода в двоичной системе. Обновим функцию `print_in_binary`. Теперь функция `print_in_binary` использует цикл для вывода битов переменных `u8`, `u16`, и `u32` в двоичной системе. + + +``` +using namespace std; + +// Функция для проверки битов и вывода их в двоичной системе +char bit_digit(uint8_t byte, uint8_t bit) { + if (byte & (0x1 << bit)) { + return '1'; + } + return '0'; +} + +// Функция для печати байта в двоичной системе +void print_in_binary(uint8_t byte) { + for (uint8_t bit = 7; bit > 0; bit--) { + cout << bit_digit(byte, bit); + } +} + +int main() { + uint8_t u8 = 0x42; + uint16_t u16 = 0x42; + uint32_t u32 = 0x42; + + cout << "u8 bytes: "; + print_in_hex(&u8, sizeof(u8)); + + cout << "\nu8 binary: "; + print_in_binary(u8); + cout << '\n'; + + cout << "\nu16 bytes: "; + print_in_hex(&u16, sizeof(u16)); + + cout << "\nu16 binary: "; + print_in_binary(u16); + cout << '\n'; + + cout << "\nu32 bytes: "; + print_in_hex(&u32, sizeof(u32)); + + cout << "\nu32 binary: "; + print_in_binary(u32); + cout << '\n'; + + return 0; +} +``` +Переведем в двоичное представление и напечатаем числа из лекционного слайда про двоичные операции (исходные два числа и результаты всех действий). +``` +int main() { + // Два числа для примера + uint8_t num1 = 0b11011011; // 219 в десятичной системе + uint8_t num2 = 0b00101101; // 45 в десятичной системе + + // Вывод двоичного представления чисел + cout << "num1 в двоичной системе: "; + print_in_binary(num1); + cout << " (" << static_cast(num1) << " в десятичной системе)\n"; + + cout << "num2 в двоичной системе: "; + print_in_binary(num2); + cout << " (" << static_cast(num2) << " в десятичной системе)\n"; + + return 0; +} +``` +Напишем функцию `print_in_binary` для вывода массива байтов в двоичной системе по аналогии с `print_in_hex()`: +``` +using namespace std; + +void print_in_binary(const void* data, size_t size) { + const uint8_t* bytes = as_bytes(data); + for (size_t i = 0; i < size; i++) { + print_in_binary(bytes[i]); + + // Для удобства чтения: пробелы между байтами, по 4 байта на строку. + if ((i + 1) % 4 == 0) { + cout << '\n'; + } else { + cout << ' '; + } + } + cout << endl; +} + +int main() { + // Примеры чисел для тестирования + uint32_t u32 = 0x42424242; // 4,294,967,042 в десятичной системе + uint16_t u16 = 0x4242; // 16,642 в десятичной системе + + cout << "u32 в двоичной системе:\n"; + print_in_binary(&u32, sizeof(u32)); + + cout << "\nu16 в двоичной системе:\n"; + print_in_binary(&u16, sizeof(u16)); + + return 0; +} +``` +Cоздаем два числа (`u32` и `u16`), переводим их в двоичное представление, и затем используем функцию `print_in_binary` для вывода байтов в двоичной системе. + +#### 2. Битовый калькулятор. +Напишем программу-калькулятор для побитовых операций. +``` +#include +#include +#include + +using namespace std; + +// Функция для проверки битов и вывода их в двоичной системе +char bit_digit(uint16_t num, uint8_t bit) { + return (num & (0x1 << bit)) ? '1' : '0'; +} + +// Функция для печати числа в двоичной системе +void print_in_binary(uint16_t num) { + for (int8_t bit = 15; bit >= 0; --bit) { + cout << bit_digit(num, bit); + } +} + +int main() { + uint16_t operand1, operand2, result; + char operation; + + // Ввод первого операнда, оператора и второго операнда + cout << "Введите первый операнд (в шестнадцатеричной форме): "; + cin >> hex >> operand1; + + cout << "Введите оператор (&, | или ^): "; + cin >> operation; + + cout << "Введите второй операнд (в шестнадцатеричной форме): "; + cin >> std::hex >> operand2; + + // Выполнение операции в зависимости от введенного оператора + switch (operation) { + case '&': + result = operand1 & operand2; + break; + case '|': + result = operand1 | operand2; + break; + case '^': + result = operand1 ^ operand2; + break; + default: + std::cerr << "Неверный оператор. Допустимые операторы: &, |, ^.\n"; + return 1; + } + + // Вывод результата в шестнадцатеричной и двоичной форме + cout << hex << setw(4) << setfill('0') << operand1 << ' ' << operation << ' ' + << setw(4) << setfill('0') << operand2 << " = " << setw(4) << setfill('0') << result + << '\n'; + + cout << setw(16) << setfill(' ') << left << "в двоичной системе: "; + print_in_binary(operand1); + cout << ' ' << operation << ' '; + print_in_binary(operand2); + cout << " = "; + print_in_binary(result); + cout << '\n'; + + return 0; +} +``` +Тут программа предлагает пользователю ввести два операнда и оператор (`&`, `|` или `^`). Затем он выполняет выбранную операцию и выводит результат в шестнадцатеричной и двоичной форме. + +#### 3. Исследование представления и размещения данных в памяти + + + +``` +#include +#include +#include +#include + +using namespace std; + +struct Student { + char name[17]; + uint16_t admissionYear; + float averageGrade; + bool isMale : 1; + uint8_t completedCourses; + Student* groupLeader; +}; + +int main() { + const int numberOfStudents = 3; + Student students[numberOfStudents]; + + students[0].admissionYear = 2023; + students[0].averageGrade = 4.5; + students[0].isMale = true; + students[0].completedCourses = 5; + students[0].groupLeader = nullptr; + + students[1].admissionYear = 2022; + students[1].averageGrade = 3.8; + students[1].isMale = false; + students[1].completedCourses = 6; + students[1].groupLeader = &students[0]; + + students[2].admissionYear = 2021; + students[2].averageGrade = 3.2; + students[2].isMale = true; + students[2].completedCourses = 7; + students[2].groupLeader = nullptr; + + for (int i = 0; i < numberOfStudents; ++i) { + cout << "\nИнформация о студенте " << i + 1 << ":\n"; + cout << "Адрес поля admissionYear: " << static_cast(&students[i].admissionYear) << ", Смещение: " + << offsetof(Student, admissionYear) << ", Размер: " << sizeof(students[i].admissionYear) << " байт, " + << "Шестнадцатеричное: " << hex << students[i].admissionYear << ", Двоичное: " + << bitset<16>(students[i].admissionYear) << '\n'; + + cout << "Адрес поля averageGrade: " << static_cast(&students[i].averageGrade) << ", Смещение: " + << offsetof(Student, averageGrade) << ", Размер: " << sizeof(students[i].averageGrade) << " байт, " + << "Значение: " << students[i].averageGrade << '\n'; + + cout << "Адрес и битовое поле isMale: " << static_cast(&students[i].isMale) << ", Смещение: " + << offsetof(Student, isMale) << ", Размер: " << sizeof(students[i].isMale) << " бит, " + << "Значение: " << (students[i].isMale ? "мужской" : "женский") << '\n'; + + cout << "Адрес поля completedCourses: " << static_cast(&students[i].completedCourses) << ", Смещение: " + << offsetof(Student, completedCourses) << ", Размер: " << sizeof(students[i].completedCourses) << " байт, " + << "Значение: " << static_cast(students[i].completedCourses) << '\n'; + + cout << "Адрес поля groupLeader: " << static_cast(&students[i].groupLeader) << ", Смещение: " + << offsetof(Student, groupLeader) << ", Размер: " << sizeof(students[i].groupLeader) << " байт, " + << "Шестнадцатеричное: " << hex << reinterpret_cast(students[i].groupLeader) << '\n'; + + // Выводим представление структуры в памяти + cout << "Представление в памяти (в шестнадцатеричной системе):\n"; + const uint8_t* bytes = reinterpret_cast(&students[i]); + for (size_t j = 0; j < sizeof(Student); ++j) { + cout << hex << setfill('0') << setw(2) << static_cast(bytes[j]) << ' '; + } + cout << '\n'; + } + + return 0; +} +``` +Тут мы создаем структуру `Student`, представляющую информацию о студенте, и инициализирует массив из трех студентов. Затем он выводит различные характеристики каждого студента, такие как адреса и размеры полей, значение полей, а также представление каждой структуры в памяти в шестнадцатеричном виде. Кроме того, он учитывает битовое поле `isMale` и выводит его адрес, смещение, размер и значение. + +#### 4. Работа со строками C + +``` +#include +#include +#include +#include + +const int MAX_FILENAME_LENGTH = 256; +const int MAX_INPUT_STRING_LENGTH = 256; + +bool isValidFilename(const char* filename) { + // Проверка наличия запрещенных символов + const char* forbiddenChars = "*\"<>?|"; + if (strpbrk(filename, forbiddenChars) != nullptr) { + return false; + } + + // Проверка наличия двоеточия + const char* colonPosition = strchr(filename, ':'); + if (colonPosition != nullptr) { + if (colonPosition == filename + 1 && isalpha(filename[0])) { + // Проверка двоеточия только вторым символом + const char* backslashPosition = strchr(colonPosition + 1, '\\'); + if (backslashPosition == nullptr) { + return false; + } + } else { + return false; + } + } + + // Проверка расширения файла + const char* extension = strrchr(filename, '.'); + if (extension != nullptr) { + if (strcmp(extension, ".txt") != 0 && strcmp(extension, ".TXT") != 0) { + return false; + } + } + + return true; +} + +int main() { + // Запрос имени файла + char filename[MAX_FILENAME_LENGTH]; + std::cout << "Введите имя файла: "; + std::cin.getline(filename, MAX_FILENAME_LENGTH); + + // Проверка корректности имени файла + if (!isValidFilename(filename)) { + std::cerr << "Некорректное имя файла.\n"; + return 1; + } + + // Добавление расширения .txt, если его нет + if (strrchr(filename, '.') == nullptr) { + strcat(filename, ".txt"); + } + + // Открытие файла + std::ifstream file(filename, std::ios::binary); + if (!file.is_open()) { + std::cerr << "Ошибка открытия файла.\n"; + return 1; + } + + // Определение размера файла + file.seekg(0, std::ios::end); + std::streampos fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + // Выделение памяти под содержимое файла + char* fileContent = new char[fileSize]; + + // Чтение содержимого файла в память + file.read(fileContent, fileSize); + file.close(); + + // Запрос строки от пользователя + char inputString[MAX_INPUT_STRING_LENGTH]; + std::cout << "Введите строку для поиска: "; + std::cin.getline(inputString, MAX_INPUT_STRING_LENGTH); + + // Подсчет вхождений строки в файл + int occurrences = 0; + char* position = fileContent; + while ((position = strstr(position, inputString)) != nullptr) { + occurrences++; + position += strlen(inputString); + } + + // Вывод результата + std::cout << "Число вхождений строки в файл: " << occurrences << std::endl; + + // Освобождение выделенной памяти + delete[] fileContent; + + return 0; +} +``` +Данный код выполняет все шаги, описанные в задании: запрос имени файла, проверка корректности имени, добавление расширения .txt, загрузка содержимого файла в память, запрос строки от пользователя, подсчет и вывод числа вхождений строки в файл, а затем освобождение памяти. \ No newline at end of file