Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.2. Разбор массива байт, содержащего структуру данных

2.2.1. Разбор задачи

В индивидуальных вариантах задач на тему разбора массива байт, содержащего структуру данных, предлагается написать программу на языке C или C++ для работы со структурой данных c учетом архитектурных особенностей некоторой платформы. Рассмотрим платформу, информация о которой представлена в табл. 8, с порядком байт от младшего к старшему.

Таблица 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, порядок байт – от старшего к младшему.

Таблица 9. Платформа задачи 1 на тему разбора байт

Тип

Размер (байт)

Знак

Выравнивание (байт)

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, порядок байт – от младшего к старшему.

Таблица 10. Платформа задачи 2 на тему разбора байт

Тип

Размер (байт)

Знак

Выравнивание (байт)

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, порядок байт – от старшего к младшему.

Таблица 11. Платформа задачи 3 на тему разбора байт

Тип

Размер (байт)

Знак

Выравнивание (байт)

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, порядок байт – от младшего к старшему.

Таблица 12. Платформа задачи 4 на тему разбора байт

Тип

Размер (байт)

Знак

Выравнивание (байт)

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