2.2. Разбор массива байт, содержащего структуру данных
2.2.1. Разбор задачи
В индивидуальных вариантах задач на тему разбора массива байт, содержащего структуру данных, предлагается написать программу на языке C или C++ для работы со структурой данных c учетом архитектурных особенностей некоторой платформы. Рассмотрим платформу, информация о которой представлена в табл. 8, с порядком байт от младшего к старшему.
|
Тип |
Размер (байт) |
Знак |
Выравнивание (байт) |
|---|---|---|---|
|
char |
1 |
Да |
1 |
|
unsigned int |
4 |
Нет |
8 |
Структура данных на целевой платформе имеет следующий вид, типы в приведенной структуре данных, возможно, придется адаптировать к используемому в решении задачи компилятору:
struct data {
char field1[9];
unsigned int field2;
char field3[6];
unsigned int field4;
char field5;
char field6;
char field7;
};
Необходимо написать функцию для разбора массива байт, содержащего структуру data. В реализации необходимо учесть особенности платформы и вывести на экран значения полей, как показано в примерах ниже.
Пример 1
Массив байт, содержащий значения структуры data:
unsigned char test_data1[] = {
0xa0, 0x15, 0x01, 0x06, 0x42, 0x89, 0x38, 0x10,
0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xd7, 0x2b, 0x09, 0x8d, 0xd8, 0xbc, 0xf1,
0x86, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xae, 0x9f, 0xab, 0xcd, 0x7b, 0x6c, 0x67,
};
Результат вывода значений полей data на экран:
-96 21 1 6 66 -119 56 16 -69
153868117
-115 -40 -68 -15 -122 -44
3450576814
123
108
103
Пример 2
Массив байт, содержащий значения структуры data:
unsigned char test_data2[] = {
0x67, 0xe7, 0xaf, 0x7e, 0x23, 0x7e, 0x5a, 0xb7,
0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xe4, 0x5b, 0x6e, 0x21, 0x4e, 0x9f, 0x94, 0xcb,
0x4d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x27, 0x3e, 0x61, 0x4c, 0xfc, 0x0b, 0x03,
};
Результат вывода значений полей data на экран:
103 -25 -81 126 35 126 90 -73 -126
560880612
78 -97 -108 -53 77 93
1281441319
-4
11
3
Решение задачи
В этом разделе предположим, что отладка и тестирование программы ведётся на устройстве с архитектурой x86-64 под управлением ОС Windows.
Характеристики типов данных char и unsigned int на целевой платформе, приведённые в таблице табл. 8, совпадают с характеристиками этих типов данных на ОС Windows x86-64, за исключением выравнивания данных для типа unsigned int. В этой связи при решении задачи воспользуемся исходным определением структуры data.
Для копирования значения поля типа char начиная с номера байта offs реализуем функцию copy_char, принимающую на вход указатель to на поле, в которое необходимо поместить значение из массива байт from с номером offs. Функция copy_char записывает в байт по адресу to значение по адресу from + offs, после чего возвращает увеличенный на 1 номер байта.
В функции main объявим структуру data, заполним массив в поле field1 тестовыми значениями из массива байт test_data1 в цикле, после чего в цикле выведем элементы массива в поле field1 на экран:
#include <stdio.h>
int copy_char(char *to, unsigned char *from, int offs) {
to[0] = from[offs];
return offs + 1;
}
int main() {
struct data s;
int offs = 0;
for (int i = 0; i < 9; i++)
offs = copy_char(&s.field1[i], test_data1, offs);
for (int i = 0; i < 9; i++)
printf("%d ", s.field1[i]);
return 0;
}
Для использования функции printf необходимо подключить стандартный заголовочный файл stdio.h. Функция copy_char выполняется в цикле, копируя 9 элементов массива test_data1 в поле field1 структуры data, при копировании значений функция copy_char также увеличивает номер копируемого байта offs. После заполнения массива field1 значения типа char в цикле выводятся на экран, для печати чисел со знаком функцией printf используется формат %d.
Сохраним определение структуры из постановки задачи, массив test_data1 и функции copy_char, main в файл exam.c, скомпилируем полученную программу при помощи компилятора gcc из набора инструментов MinGW [5] и проверим её работу:
PS C:\> gcc -std=c99 -Wall -Wextra -Wpedantic exam.c -o exam.exe
PS C:\> ./exam.exe
-96 21 1 6 66 -119 56 16 -69
Выведенная на экран строка совпадает с ожидаемым результатом вывода поля field1 на экран (см. результат вывода значений полей data на экран для массива test_data1).
На следующем шаге реализуем функцию copy_unsigned_int для копирования из массива байт from начиная с номера байта offs значения поля типа unsigned int, занимающего 4 байта как на целевой платформе (см. табл. 8), так и на ОС Windows x86-64:
int copy_unsigned_int(unsigned int *to, unsigned char *from, int offs) {
unsigned char *to_bytes = (unsigned char*) to;
for (int i = 0; i < 4; i++)
to_bytes[i] = from[offs + i];
return offs + 4;
}
Функция copy_unsigned_int при помощи оператора приведения типов (unsigned char*) преобразует указатель на беззнаковое целое число unsigned int, занимающее 4 байта, в указатель на 1 байт to_bytes, после чего в цикле копирует 4 байта начиная с адреса from + offs в память по адресу to_bytes.
При использовании функции copy_unsigned_int следует учитывать, что данные типа unsigned int на целевой платформе выровнены по 8 байт – это означает, что адрес первого байта значения типа unsigned int должен быть кратным 8. Выравнивание данных типа unsigned int отличается от выравнивания данных этого типа на ОС Windows x86-64.
Для учёта этой особенности целевой платформы реализуем также функцию align_8, которая будет увеличивать номер байта offs до тех пор, пока он не станет кратным 8:
int align_8(int offs) {
while (offs % 8 != 0)
offs++;
return offs;
}
Воспользуемся реализованными функциями align_8 и copy_unsigned_int для копирования значения поля field2 из массива test_data1, а для вывода беззнакового целочисленного значения на экран укажем формат %u в функции printf:
int main() {
struct data s;
int offs = 0;
for (int i = 0; i < 9; i++)
offs = copy_char(&s.field1[i], test_data1, offs);
offs = align_8(offs);
offs = copy_unsigned_int(&s.field2, test_data1, offs);
for (int i = 0; i < 9; i++)
printf("%i ", s.field1[i]);
printf("\n%u\n", s.field2);
return 0;
}
Проверим работу обновлённой программы exam.c:
PS C:\> gcc -std=c99 -Wall -Wextra -Wpedantic exam.c -o exam.exe
PS C:\> ./exam.exe
-96 21 1 6 66 -119 56 16 -69
153868117
Выведенные на экран строки, содержащие значения полей field1 и field2, совпадают с первыми 2 строками из ожидаемого вывода (см. результат вывода значений полей data на экран для массива test_data1).
Воспользуемся реализованными функциями copy_char, align_8 и copy_unsigned_int для разбора всего массива байт, содержащего структуру data. Для проверки корректности работы программы сразу на 2 примерах входных данных, представленных массивами test_data1 и test_data2, переместим код для разбора массива байт в функцию copy_struct:
void copy_struct(struct data *s, unsigned char *data) {
int offs = 0;
for (int i = 0; i < 9; i++)
offs = copy_char(&s->field1[i], data, offs);
offs = align_8(offs);
offs = copy_unsigned_int(&s->field2, data, offs);
for (int i = 0; i < 6; i++)
offs = copy_char(&s->field3[i], data, offs);
offs = align_8(offs);
offs = copy_unsigned_int(&s->field4, data, offs);
offs = copy_char(&s->field5, data, offs);
offs = copy_char(&s->field6, data, offs);
offs = copy_char(&s->field7, data, offs);
}
Для доступа к значениям полей структуры data в функции copy_struct используется оператор -> вместо оператора точки, который использовался ранее – это связано с тем, что на вход функции передаётся указатель на структуру struct data *s, а синтаксис s->field эквивалентен синтаксису (*s).field, где выражение *s получает объект по его адресу, сохранённому в переменной s. Перед каждым вызовом функции copy_unsigned_int используется функция align_8 для учёта особенности целевой платформы в части выравнивания данных типа unsigned int на 8 байт (см. табл. 8).
Код для вывода содержимого полей структуры data на экран отделим от кода для разбора массива байт и поместим в функцию print_struct.
В функции main заполним объявленную структуру data данными из массива test_data1 при помощи функции copy_struct и выведем значения полей на экран функцией print_struct, после чего выполним те же действия с массивом test_data2:
void print_struct(struct data *s) {
for (int i = 0; i < 9; i++)
printf("%d ", s->field1[i]);
printf("\n%u\n", s->field2);
for (int i = 0; i < 6; i++)
printf("%d ", s->field3[i]);
printf("\n%u\n", s->field4);
printf("%d\n", s->field5);
printf("%d\n", s->field6);
printf("%d\n", s->field7);
}
int main() {
struct data s;
copy_struct(&s, test_data1);
print_struct(&s);
copy_struct(&s, test_data2);
print_struct(&s);
return 0;
}
Проверим работу программы на тестовых данных:
PS C:\> gcc -std=c99 -Wall -Wextra -Wpedantic exam.c -o exam.exe
PS C:\> ./exam.exe
-96 21 1 6 66 -119 56 16 -69
153868117
-115 -40 -68 -15 -122 -44
3450576814
123
108
103
103 -25 -81 126 35 126 90 -73 -126
560880612
78 -97 -108 -53 77 93
1281441319
-4
11
3
2.2.2. Упражнения
Задача 1
Написать программу на C/C++ для работы со структурой данных c учетом архитектурных особенностей некоторой платформы. Информация о платформе представлена в таблице табл. 9, порядок байт – от старшего к младшему.
|
Тип |
Размер (байт) |
Знак |
Выравнивание (байт) |
|---|---|---|---|
|
int |
8 |
Да |
4 |
|
char |
1 |
Да |
1 |
|
unsigned long |
8 |
Нет |
1 |
Структура данных на целевой платформе имеет следующий вид, типы в приведенной структуре данных, возможно, придется адаптировать к используемому в решении задачи компилятору:
struct data {
int field1;
char field2[8];
char field3[5];
int field4;
unsigned long field5;
unsigned long field6;
};
Написать функцию для разбора массива байт, содержащего структуру data. В реализации необходимо учесть особенности платформы. Вывести на экран значения полей, как показано в примерах ниже.
Пример 1
Массив байт, содержащий значения структуры data:
unsigned char test_data1[] = {
0x9a, 0xf1, 0x75, 0x3e, 0xf2, 0xc9, 0x4a, 0xe3,
0xb3, 0x60, 0xfa, 0xed, 0xd7, 0x90, 0x76, 0x3d,
0xfc, 0xc3, 0x8a, 0x53, 0xd6, 0x00, 0x00, 0x00,
0x82, 0x5e, 0xc1, 0x7e, 0xeb, 0xff, 0xdd, 0xfb,
0x07, 0xd7, 0x18, 0xdb, 0x9c, 0xe9, 0x8f, 0x49,
0xc1, 0x67, 0x9f, 0xf5, 0x35, 0x80, 0x06, 0x3c,
};
Результат вывода значений полей data на экран:
-7281910209259681053
-77 96 -6 -19 -41 -112 118 61
-4 -61 -118 83 -42
-9052585450098663941
564947609767743305
13936283447434675772
Пример 2
Массив байт, содержащий значения структуры data:
unsigned char test_data2[] = {
0x55, 0x2d, 0x9b, 0x10, 0x70, 0x71, 0x40, 0xa2,
0x5a, 0x31, 0xa5, 0x75, 0x08, 0x6d, 0x47, 0x70,
0x65, 0xb9, 0x9d, 0x9a, 0x0b, 0x00, 0x00, 0x00,
0xe8, 0x24, 0x21, 0x22, 0x64, 0xaf, 0x35, 0xf6,
0xe3, 0x01, 0xc6, 0x30, 0x78, 0x80, 0xdd, 0x62,
0x92, 0x0b, 0xd3, 0x6a, 0x21, 0x9c, 0x1d, 0xc0,
};
Результат вывода значений полей data на экран:
6137732362084106402
90 49 -91 117 8 109 71 112
101 -71 -99 -102 11
-1719212726146877962
16357573233068793186
10523737407065169344
Задача 2
Написать программу на C/C++ для работы со структурой данных c учетом архитектурных особенностей некоторой платформы. Информация о платформе представлена в таблице табл. 10, порядок байт – от младшего к старшему.
|
Тип |
Размер (байт) |
Знак |
Выравнивание (байт) |
|---|---|---|---|
|
short |
2 |
Да |
2 |
|
char |
1 |
Да |
1 |
|
unsigned long long |
8 |
Нет |
1 |
|
unsigned short |
2 |
Нет |
2 |
Структура данных на целевой платформе имеет следующий вид, типы в приведенной структуре данных, возможно, придется адаптировать к используемому в решении задачи компилятору:
struct data {
short field1;
char field2;
unsigned long long field3;
unsigned short field4[3];
};
Написать функцию для разбора массива байт, содержащего структуру data. В реализации необходимо учесть особенности платформы. Вывести на экран значения полей, как показано в примерах ниже.
Пример 1
Массив байт, содержащий значения структуры data:
unsigned char test_data1[] = {
0xb8, 0xb6, 0x73, 0xf3, 0xcb, 0x8f, 0x9c, 0x54,
0x98, 0x64, 0x2d, 0x00, 0x94, 0xc8, 0xd7, 0x15,
0x06, 0x76,
};
Результат вывода значений полей data на экран:
-18760
115
3270906718549167091
51348 5591 30214
Пример 2
Массив байт, содержащий значения структуры data:
unsigned char test_data2[] = {
0x5c, 0x1b, 0x68, 0x14, 0xdc, 0xe0, 0x7e, 0x34,
0x60, 0x81, 0xb5, 0x00, 0x74, 0xa4, 0x25, 0x0e,
0x3b, 0xd7,
};
Результат вывода значений полей data на экран:
7004
104
13078840571443862548
42100 3621 55099
Задача 3
Написать программу на C/C++ для работы со структурой данных c учетом архитектурных особенностей некоторой платформы. Информация о платформе представлена в таблице табл. 11, порядок байт – от старшего к младшему.
|
Тип |
Размер (байт) |
Знак |
Выравнивание (байт) |
|---|---|---|---|
|
float |
4 |
Да |
4 |
|
long |
8 |
Да |
1 |
|
char |
1 |
Да |
1 |
Структура данных на целевой платформе имеет следующий вид, типы в приведенной структуре данных, возможно, придется адаптировать к используемому в решении задачи компилятору:
struct data {
float field1;
float field2[6];
long field3;
char field4[3];
float field5;
float field6;
};
Написать функцию для разбора массива байт, содержащего структуру data. В реализации необходимо учесть особенности платформы. Вывести на экран значения полей, как показано в примерах ниже.
Пример 1
Массив байт, содержащий значения структуры data:
unsigned char test_data1[] = {
0x3e, 0x92, 0x2b, 0x7a, 0xbc, 0x1f, 0x0e, 0x35,
0x3f, 0x63, 0xbd, 0x20, 0x3e, 0x82, 0x92, 0x08,
0xbf, 0x33, 0x3a, 0x49, 0xbf, 0x01, 0x2c, 0x5c,
0x3e, 0x1b, 0x1c, 0x34, 0xb2, 0x38, 0x01, 0x9f,
0xdd, 0x60, 0xab, 0xe3, 0x23, 0xe2, 0x19, 0x00,
0xbd, 0xdc, 0x73, 0xd3, 0x3f, 0x79, 0xcf, 0x2c,
};
Результат вывода значений полей data на экран:
0.28548794984817505
-0.009707977063953876 0.8896045684814453 0.2550203800201416 -0.7001081109046936 -
0.5045831203460693 0.15147477388381958
-5604727950137054237
35 -30 25
-0.10764279216527939
0.9758174419403076
Пример 2
Массив байт, содержащий значения структуры data:
unsigned char test_data2[] = {
0xbf, 0x24, 0xa9, 0xb0, 0x3e, 0x72, 0x7a, 0x8e,
0x3e, 0x7d, 0x2b, 0x40, 0xbe, 0x63, 0x67, 0xe0,
0xbf, 0x0f, 0x9a, 0x1c, 0x3e, 0xee, 0x2c, 0x48,
0xbf, 0x26, 0x22, 0xa7, 0x5b, 0x2f, 0x71, 0xb6,
0x04, 0x2b, 0x0c, 0x94, 0x8a, 0x87, 0xe7, 0x00,
0x3d, 0xce, 0x69, 0xc1, 0x3f, 0x37, 0x89, 0x0c,
};
Результат вывода значений полей data на экран:
-0.643214225769043
0.23679563403129578 0.24723529815673828 -0.2220759391784668 -0.5609452724456787
0.4651815891265869 -0.6489662528038025
6570595407924759700
-118 -121 -25
0.10078764706850052
0.7169349193572998
Задача 4
Написать программу на C/C++ для работы со структурой данных c учетом архитектурных особенностей некоторой платформы. Информация о платформе представлена в таблице табл. 12, порядок байт – от младшего к старшему.
|
Тип |
Размер (байт) |
Знак |
Выравнивание (байт) |
|---|---|---|---|
|
unsigned short |
4 |
Нет |
8 |
|
char |
1 |
Да |
1 |
|
long |
8 |
Да |
2 |
|
unsigned long long |
8 |
Нет |
4 |
Структура данных на целевой платформе имеет следующий вид, типы в приведенной структуре данных, возможно, придется адаптировать к используемому в решении задачи компилятору:
struct data {
unsigned short field1[4];
char field2[4];
char field3[9];
char field4;
char field5;
long field6;
unsigned long long field7;
};
Написать функцию для разбора массива байт, содержащего структуру data. В реализации необходимо учесть особенности платформы. Вывести на экран значения полей, как показано в примерах ниже.
Пример 1
Массив байт, содержащий значения структуры data:
unsigned char test_data1[] = {
0x49, 0xd0, 0x03, 0x13, 0x00, 0x00, 0x00, 0x00,
0x28, 0x36, 0x62, 0x48, 0x00, 0x00, 0x00, 0x00,
0xde, 0xaa, 0xd5, 0x63, 0x00, 0x00, 0x00, 0x00,
0x96, 0x2f, 0xc8, 0x07, 0x82, 0xc3, 0xe2, 0xa0,
0xa8, 0x4d, 0xb1, 0x8f, 0xad, 0xfc, 0xc2, 0xc4,
0xb9, 0xa0, 0x28, 0x00, 0x0f, 0x86, 0x54, 0xaa,
0xe0, 0x61, 0x2a, 0x4a, 0x33, 0x79, 0x5d, 0xbf,
0x91, 0xe7, 0x3a, 0x15,
};
Результат вывода значений полей data на экран:
319017033 1214395944 1674947294 130559894
-126 -61 -30 -96
-88 77 -79 -113 -83 -4 -62 -60 -71
-96
40
5344191525386749455
1529789636612553011
Пример 2
Массив байт, содержащий значения структуры data:
unsigned char test_data2[] = {
0x67, 0x35, 0x68, 0x15, 0x00, 0x00, 0x00, 0x00,
0xe3, 0xe7, 0xc1, 0x5b, 0x00, 0x00, 0x00, 0x00,
0x80, 0x52, 0xc8, 0x38, 0x00, 0x00, 0x00, 0x00,
0x74, 0xed, 0x4c, 0x4c, 0x64, 0x18, 0xa2, 0x0c,
0x2b, 0x34, 0x3b, 0xdb, 0x5f, 0x89, 0x22, 0x78,
0x9d, 0x0c, 0x4a, 0x00, 0xab, 0x91, 0xd4, 0xd0,
0xb8, 0x44, 0x56, 0x26, 0x5d, 0xe0, 0xaa, 0x43,
0x71, 0x16, 0x74, 0xfd,
};
Результат вывода значений полей data на экран:
359150951 1539434467 952652416 1280109940
100 24 -94 12
43 52 59 -37 95 -119 34 120 -99
12
74
2762470982006641067
18263247064616591453