sizeof () часть структуры C - своего рода - PullRequest
8 голосов
/ 02 февраля 2011

Я хочу получить часть данных внутри некоторых структур 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 , но здесь это кажется невозможным - но это не имеет большого значения для меня) .

Мой вопрос: это правильно и безопасно? Вы видите какую-нибудь потенциальную ловушку или какой-то лучший подход?

Помимо прочего: допускает ли стандарт С (и де-факто компиляторы), что поля структур лежат в памяти в том же порядке, как они определены в источнике? Это, наверное, глупый вопрос, но я бы хотел быть уверен ...

ОБНОВЛЕНИЕ: Некоторые выводы из ответов и мои собственные выводы:

  1. Кажется, нет проблем с моим подходом. В частности, C диктует, что поля структуры никогда не изменят порядок.

  2. Можно также (по предложению aswer) сосчитать из последнего постоянного поля и добавить его размер: (char*)&myst.x2 + sizeof(&myst.x2) - (char*)(&myst). Это будет эквивалентно, за исключением того, что оно будет включать не байты заполнения (если они есть) для последнего поля. Очень маленькое преимущество - и очень маленький недостаток - быть менее простым.

  3. Но принятый ответ с offsetof представляется предпочтительнее, чем мое предложение. Это ясно-выразительный и чистый во время компиляции, он не требует экземпляра структуры. Далее он кажется стандартным, доступным в любом компиляторе. Если один не нуждается в конструкции времени компиляции и имеет доступный экземпляр структуры (как мой сценарий), оба решения по существу эквивалентны.

Ответы [ 5 ]

10 голосов
/ 02 февраля 2011

Вы смотрели на offsetof объект? Возвращает смещение члена от начала структуры. Таким образом, offsetof (st, x2) возвращает смещение x2 от начала структуры. Так что в вашем примере offsetof (st, x2) + sizeof(st.x2) даст вам количество байтов сериализованных компонентов.

Это очень похоже на то, что вы делаете сейчас, вы просто игнорируете отступ после x2 и используете редко используемый фрагмент C.

5 голосов
/ 02 февраля 2011

C гарантирует такое поведение. Это предназначено, чтобы позволить примитивный полиморфизм. Рассмотрим:

struct X {
   int a;
};
struct Y {
   int a;
   int b;
};
void foo(X* x) {
   x->a = 10;
};
Y y;
foo((X*)&y); // Well defined behaviour- y.a = 10.
3 голосов
/ 02 февраля 2011

Компилятор C может вставлять байты заполнения для выравнивания, но не может переупорядочивать структурные переменные.

Более чистым способом может быть просто определение 2-й структуры для целей sizeof (), которая включает начальные переменные структуры, которую вы хотите. Компилятор гарантирует, что 2 стойки, которые имеют одинаковые переменные в одном и том же порядке, будут размещены одинаково.

0 голосов
/ 02 февраля 2011

Я голосую за принцип KISS.Записывайте в файл элемент за элементом, и вы гарантированно не зависите от компилятора.

0 голосов
/ 02 февраля 2011

Вы можете взглянуть на protobuf ;Кажется, он делает то, что вы хотите, портативным способом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...