Вопрос выравнивания структуры C ++ - PullRequest
8 голосов
/ 21 сентября 2009

У меня есть предопределенная структура (на самом деле несколько), где переменные охватывают границу 32-битного слова. В Linux (и Windows, использующей GCC) я могу получить свои структуры для упаковки до правильного размера, используя атрибут ((упакованный)). Однако я не могу заставить его работать так же, используя VC ++ и #pragma pack.

При использовании GCC возвращается правильный размер 6 байтов:

struct
{
    unsigned int   a                : 3;
    unsigned int   b                : 1;
    unsigned int   c                : 15;
    unsigned int   troubleMaker     : 16;
    unsigned short padding          : 13;
} __attribute__((packed)) s;

При использовании VC ++ возвращается неправильный размер в 8 байт

#pragma pack(push)
#pragma pack(1)

struct
{
    unsigned int   a                : 3;
    unsigned int   b                : 1;
    unsigned int   c                : 15;
    unsigned int   troubleMaker     : 16;
    unsigned short padding          : 13;
} s;

#pragma pack(pop)

Я могу заставить вещи работать, разделив «TroubleMaker» через границу вручную, но я бы предпочел этого не делать Есть идеи?

Ответы [ 6 ]

17 голосов
/ 21 сентября 2009

Сумасшедшая идея: просто напишите в первую очередь программу, соответствующую C99 или C ++ 03


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

Вы можете написать соответствующую C99 программу, которая будет работать на любой архитектуре или хосте с максимальной скоростью и эффективностью кэширования, используя стандартизированные функции C API для копирования строк и памяти и функции Posix hton и ntoh.

Хорошей практикой является использование следующих функций, для которых существуют опубликованные стандарты:

C99: memcpy(), Posix: htonl(), htons(), ntohl(), ntohs()

Обновление: вот код, который должен работать везде одинаково. Возможно, вам придется получить <stdint.h> из этого проекта , если Microsoft все еще не реализовал его для C99, или просто сделать обычные предположения о размерах int.

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>

struct packed_with_bit_fields {  // ONLY FOR COMPARISON
    unsigned int   a        : 3;
    unsigned int   b        : 1;
    unsigned int   c        : 15;
    unsigned int   troubleMaker : 16;
    unsigned short padding  : 13;
} __attribute__((packed));       // USED ONLY TO COMPARE IMPLEMENTATIONS

struct unpacked { // THIS IS THE EXAMPLE STRUCT
    uint32_t a;
    uint32_t b;
    uint32_t c;
    uint32_t troubleMaker;
}; // NOTE NOT PACKED

struct unpacked su;
struct packed_with_bit_fields sp;
char *bits = "Lorem ipsum dolor";

int main(int ac, char **av) {
  uint32_t x;   // byte order issues ignored in both cases

  // This should work with any environment and compiler
  memcpy(&x, bits, 4);
  su.a = x & 7;
  su.b = x >> 3 & 1;
  su.c = x >> 4 & 0x7fff;
  memcpy(&x, bits + 2, 4);
  su.troubleMaker = x >> 3 & 0xffff;

  // This section works only with gcc
  memcpy(&sp, bits, 6);
  printf( sp.a == su.a
      &&  sp.b == su.b
      &&  sp.c == su.c
      &&  sp.troubleMaker == su.troubleMaker
      ? "conforming and gcc implementations match\n" : "huh?\n");
  return 0;
}
7 голосов
/ 21 сентября 2009

Выравнивание и упорядочение битовых полей общеизвестно зависит от реализации. намного безопаснее объявить нормальное целочисленное поле и манипулировать "битовыми полями" внутри, используя маски и битовые (| & ^) операторы.

2 голосов
/ 21 сентября 2009

Я не верю, что это поведение поддерживается в Visual Studio. В зависимости от макроса пакета я попытался использовать __declspec(align(1)) и получил то же самое поведение. Я думаю, что вы застряли с 12 байтами или немного переупорядочили свою структуру.

1 голос
/ 21 сентября 2009

Если это абсолютно определенно должно быть 6 байтов, тогда определите его как 3 коротких и получите данные сами ... это не замедлит ... компилятор все равно делает это ...

0 голосов
/ 22 сентября 2009

Более короткий пример с только соответствующим кодом


struct unpacked {  // apparently my other example was too long and confusing
    uint32_t a;    // ...here is a much shorter example with only the conforming
    uint32_t b;    // ...code. (The other program had the gcc-specific declaration,
    uint32_t c;    // but only for test code. Still, it was a bit long.)
    uint32_t troubleMaker;
};

struct unpacked su;
char *bits = "Lorem ipsum dolor";

void f(void) {
  uint32_t x;

  memcpy(&x, bits, 4);
  su.a = x & 7;
  su.b = x >> 3 & 1;
  su.c = x >> 4 & 0x7fff;
  memcpy(&x, bits + 2, 4);
  su.troubleMaker = x >> 3 & 0xffff;
  return 0;
}
0 голосов
/ 21 сентября 2009

Я считаю, что VC ++ не поддерживает это, и у меня есть серьезные сомнения, является ли поведение GCC в этом отношении стандартным.

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