Я хочу получить часть данных внутри некоторых структур C, чтобы частично сериализовать / десериализовать их, записать байты из памяти на диск и наоборот.
Структуры заранее неизвестны, они динамически создаются с помощью моего собственного генератора кода C (а также кода, который его сериализует). Сериализуемые поля будут размещены в начале структуры.
Допустим, у нас есть структура с 4 полями, и первые два должны быть сериализованы:
typedef struct {
int8_t x1;
int32_t x2; /* 1 + 4 = 5 bytes (if packed) */
int8_t y1;
int32_t y2; /* 1 + 4 +1 + 4 = 10 bytes (if packed) */
} st;
Я планирую захватить указатель на переменную структуры и записать / прочитать n
байтов, которые охватывают эти два первых поля (x1, x2
). Я не думаю, что мне нужно беспокоиться о выравнивании / упаковке, потому что я не намерен сериализовать, чтобы пережить разные компиляции (ожидается, что только уникальный исполняемый файл будет читать / записывать данные). И, поскольку я нацеливаюсь на широкий спектр архитектур компиляторов, я не хочу делать предположения о пакетах выравнивания или специфических приемах компилятора.
Тогда мне нужно посчитать байты. И я не могу просто сделать sizeof(st.x1)+sizeof(st.x2)
из-за отступов. Итак, я планирую вычесть указатели от начала структуры до первого «непостоянного» поля:
st myst;
int partsize = (char*)&myst.y1 - (char*)(&myst);
printf("partial size=%d (total size=%d)\n",partsize,sizeof(myst));
Кажется, это работает. И его можно поместить в макрос.
(Для справки: я также пытался написать другой макрос, который не требует экземпляра структуры, что-то вроде this , но здесь это кажется невозможным - но это не имеет большого значения для меня) .
Мой вопрос: это правильно и безопасно? Вы видите какую-нибудь потенциальную ловушку или какой-то лучший подход?
Помимо прочего: допускает ли стандарт С (и де-факто компиляторы), что поля структур лежат в памяти в том же порядке, как они определены в источнике? Это, наверное, глупый вопрос, но я бы хотел быть уверен ...
ОБНОВЛЕНИЕ: Некоторые выводы из ответов и мои собственные выводы:
Кажется, нет проблем с моим подходом. В частности, C диктует, что поля структуры никогда не изменят порядок.
Можно также (по предложению aswer) сосчитать из последнего постоянного поля и добавить его размер: (char*)&myst.x2 + sizeof(&myst.x2) - (char*)(&myst)
. Это будет эквивалентно, за исключением того, что оно будет включать не байты заполнения (если они есть) для последнего поля. Очень маленькое преимущество - и очень маленький недостаток - быть менее простым.
Но принятый ответ с offsetof
представляется предпочтительнее, чем мое предложение. Это ясно-выразительный и чистый во время компиляции, он не требует экземпляра структуры. Далее он кажется стандартным, доступным в любом компиляторе.
Если один не нуждается в конструкции времени компиляции и имеет доступный экземпляр структуры (как мой сценарий), оба решения по существу эквивалентны.