Структура - це зручне сховище для різнорідних даних, які хочеться об'єднати. Наприклад, ви можете створити структуру, яка описує параметри вашого пристрою - мережеві настройки, таймаут сплячого режиму, його ідентифікатор та інше подібне, типу будь-якого рядка привітання і стану світлодіода. Раз всі параметри будуть зберігатися в одному місці - вони завжди будуть на увазі, та й нормальні IDE будуть вам підказувати поля структури при зверненні до них. Ще ми розглянемо зберігання і відновлення структур з архіву, а також їх передачу по мережі.
Як це працює?
Можна було зробити трохи інакше: оголосити тільки тип, а змінну завести пізніше. Для цього ми використовували б ключове слово typedef і написали так:
Звичайно, в обох варіантах ви можете оголосити скільки завгодно примірників структур, або створити масив з них:
Варіант з масивом особливо зручний для сервера в клієнт-серверної топології мережі - на кожному клієнті зберігаються в структурі його власні параметри, а на майстер-пристрої розташовується таблиця параметрів всіх клієнтів у вигляді масиву структур.
В принципі, нічого складного в структурах немає, а з темою серверів і клієнтів ми плавно підійшли до більш цікавої теми:
Зберігання, передача і синхронізація структур
Для багатьох буде подивом те, що дані структури зберігаються в пам'яті у вигляді плоского списку, все поля структури просто йдуть в пам'яті один за одним. Тому стає можливим звертатися з цією структурою як з простим масивом байт! Перевіримо, створимо масив «поверх» цієї структури.
Початкове зміщення отримаємо так:
sizeof і offsetof
Це навіть не функції, а вбудовані макроси мови Сі. Почнемо з більш простою, sizeof.
Компілятор замінює всі записи виду sizeof X на значення довжини Х. Як X може виступати як тип, так і екзмеплярів типу, тобто в нашому випадку можна підставити в sizeof і тип структури (якщо ми його заводили за допомогою typedef), і саму змінну структури так: sizeof params_struct або sizeof params. Вона пройде по всім полям структури, складе їх довжини і віддасть суму, яка і буде довжиною структури.
offsetof - справжній макрос, який приймає два параметри (структуру _s_ і поле _m_ в ній) і віддає положення цього поля в структурі, його зміщення відносно початку структури. Виглядає цей макрос дуже просто:
Як він працює?
Тут потрібно зробити невеличкий відступ. Справа в тому, що я розглядав найпростіший випадок, коли поля упаковані точно слідом один за одним. Є й інші методи упаковки, які називаються «вирівнювання». Наприклад, можна видавати кожному полю «слот», кратний 4 байтам, або 8 байтам. Тоді навіть char займатиме 8 байт, і загальний розмір структури виросте, а все зміщення зрушаться і стануть кратні вирівнюванню. Ця штука корисна при програмуванні для комп'ютера, оскільки через грануляції ОЗУ процесор набагато швидше вміє витягати з пам'яті вирівняні дані, йому потрібно на це менше операцій.
Робота з масивом зі структури
Окей, тепер ми вміємо представляти будь-яку структуру в вигляді масиву байт, і назад. Ви зрозуміли фішку? У нас тепер одна і та ж область пам'яті має ролі «структура» і «масив». Змінюємо щось в структурі - змінюється масив, міняємо масив - змінюється структура.
У цьому - суть процесу! У нас немає окремого масиву, тому що сама структура - це вже масив, і ми просто звертаємося до пам'яті різними методами. І у нас немає ніяких копіюють циклів по полях або по байтам, цей цикл буде вже відразу в функції передачі.
Тепер залишилося лише навчитися зручно з цим всім працювати.
Зберігання та передача структури
Функції передачі даних по мережі зазвичай виглядають приблизно так само. В якості даних передавайте params, а в якості довжини даних - sizeof params.
Прийом і відновлення структури
Все точно так же. Моя функція читання масиву з EEPROM: I2C_burst_read (I2Cx, HW_address, addr, n_data, * data). n_data = sizeof params, * data = params:
Не забувайте, що ви відразу пишете прийняті байти безпосередньо в структуру. При повільній або ненадійною передачі має сенс записати дані в тимчасовий буфер, і після їх перевірки передати їх в структуру через
Реалізувавши ці методи, ми втілимо зручну синхронізацію двох структур, що знаходяться на різних комп'ютерах: клієнт-мікроконтролер може бути хоч на іншому боці земної кулі від сервера, але передати структури буде все так само просто.
Зберігання / відновлення окремих полів
І навіщо ж ми так довго розглядали макрос offsetof? Його дуже зручно використовувати для читання і запису окремих полів структури, наприклад так:
Ну і взагалі, було б непогано зробити зручні макроси-обгортки для цієї мети.