Практическое использование битовых полей нулевой длины

Я не совсем уверен в C, но C++ допускает неназванные битовые поля длиной 0. Например:

struct X
{
    int : 0;
};
  • Вопрос первый: Какие практические применения этого может вы думать?
  • вопрос второй: Какие реальные практические применения (если таковые имеются) вы знаете?

редактировать пример после ответа ice-crime

Edit: OK, благодаря текущим ответам я теперь знаю теоретическую цель. Но вопросы о практическом использовании, так что они все еще держат:)

5 ответов


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

реальным примером является следующий перенос ядра xnu из архитектуры Motorola 68000 (m68k) в архитектуру i386. Рядом была рабочая версия ядра m68k. Когда они портировали его на i386, они обнаружили, что требования к выравниванию i386 отличаются от требований m68k таким образом, что машина m68k и машина i386 не согласовали макет следующей структуры BOOTP для конкретного поставщика. Чтобы сделать макет структуры i386 согласующимся с m68k, они добавили неназванное битовое поле нулевой длины, чтобы заставить NV1 структура/nv_U Союз 16-разрядное выровненный.

вот соответствующие части из исходного кода Mac OS X 10.6.5 xnu:

/* from xnu/bsd/netinet/bootp.h */
/*
 * Bootstrap Protocol (BOOTP).  RFC 951.
 */
/*
 * HISTORY
 *
 * 14 May 1992 ? at NeXT
 *  Added correct padding to struct nextvend.  This is
 *  needed for the i386 due to alignment differences wrt
 *  the m68k.  Also adjusted the size of the array fields
 *  because the NeXT vendor area was overflowing the bootp
 *  packet.
 */
/* . . . */
struct nextvend {
  u_char nv_magic[4]; /* Magic number for vendor specificity */
  u_char nv_version;  /* NeXT protocol version */
  /*
   * Round the beginning
   * of the union to a 16
   * bit boundary due to
   * struct/union alignment
   * on the m68k.
   */
  unsigned short  :0;
  union {
    u_char NV0[58];
    struct {
      u_char NV1_opcode;  /* opcode - Version 1 */
      u_char NV1_xid; /* transcation id */
      u_char NV1_text[NVMAXTEXT]; /* text */
      u_char NV1_null;  /* null terminator */
    } NV1;
  } nv_U;
};

стандарт (9.6/2) допускает только 0 битовых полей длины как особый случай :

как частный случай, неназванный бит-поле с шириной нуля указывает выравнивание следующего битовое поле в единице распределения граница. только при объявлении Безымянное бит-поле может constant-выражение равно значению к нулю.

единственное применение описано в этой цитате, хотя я никогда не сталкивался с этим в практический код.


для записи я просто попробовал следующий код под VS 2010:

struct X {
    int i : 3, j : 5;
};

struct Y {
    int i : 3, : 0, j : 5; // nice syntax huh ?
};

int main()
{
    std::cout << sizeof(X) << " - " << sizeof(Y) << std::endl;
}

выход на моей машине так и есть : 4 - 8.


struct X { int : 0; };

неопределенное поведение в C.

посмотреть (выделено мной):

(C99, 6.7.2.1p2) " наличие struct-declaration-list в спецификаторе структуры или объединения объявляет новый тип в единице перевода. Struct-declaration-list-это последовательность объявлений для членов структуры или объединения. если struct-declaration-list не содержит именованных членов, поведение не определено"

(C11 имеет та же формулировка.)

вы можете использовать неименованное битовое поле с 0 ширина, но не если в структуре нет другого именованного элемента.

например:

struct W { int a:1; int :0; };  // OK
struct X { int :0; };           // Undefined Behavior

кстати для второго объявления,gcc выдает диагностику (не требуется стандартом C) с -pedantic.

С другой стороны:

 struct X { int :0; };

определяется в GNU C. Он используется, например, ядром Linux (include/linux/bug.h) в силу ошибки компиляции с помощью следующий макрос, если условие истинно:

#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))

Это из MSDN и не помечено как Microsoft Specific, поэтому я думаю, что это общий стандарт C++:

неназванное битовое поле шириной 0 вынуждает выравнивание следующего битового поля к границе следующего типа, где type-тип элемента.


стандарт C11 теперь позволяет включать битовые поля нулевой длины. Вот пример из проекта Комитета C (N1570), который, я считаю, иллюстрирует практическое использование.

3.14 памяти
...
4. Пример структуры, объявленной как

struct {
  char a;
  int b:5, c:11, :0, d:8;
  struct { int ee:8; } e;
}

содержит четыре отдельных места памяти: member a, и бит-поля d и e.ee - отдельные ячейки памяти, и может быть изменены одновременно не мешая друг другу. Битовые поля b и c вместе составляют четвертое место памяти. Битовые поля b и c невозможно одновременно изменить, но b и a, например, может быть.

таким образом, включая битное поле нулевой длины между битными полями c и d позволяет одновременную модификацию b и d Как хорошо.