Дополнительные байты при объявлении члена структуры как uint32_t

У меня проблема при использовании типа uint32_t из библиотеки stdint.h. Если я запустил следующий код (в Ubuntu linux 11.10 x86_64, g ++ версии 4.6.1):

#include "stdint.h"
#include <iostream>
using std::cout;
typedef struct{
    // api identifier
    uint8_t api_id;

    uint8_t frame_id;
    uint32_t dest_addr_64_h;
    uint32_t dest_addr_64_l;
    uint16_t dest_addr_16;
    uint8_t broadcast_radius;
    uint8_t options;
    // packet fragmentation
    uint16_t order_index;
    uint16_t total_packets;
    uint8_t rf_data[];
} xbee_tx_a;

typedef struct{
    // api identifier
    uint8_t api_id;

    uint8_t frame_id;
    uint16_t dest_addr_64_h;
    uint16_t dest_addr_64_l;
    uint16_t dest_addr_16;
    uint8_t broadcast_radius;
    uint8_t options;
    // packet fragmentation
    uint16_t order_index;
    uint16_t total_packets;
    uint8_t rf_data[];
} xbee_tx_b;


int main(int argc, char**argv){

   xbee_tx_a a;

   cout<<"size of xbee_tx_a "<<sizeof (xbee_tx_a)<<std::endl;
   cout<<"size of xbee_tx_a.api_id "<<sizeof (a.api_id)<<std::endl;
   cout<<"size of xbee_tx_a.frame_id "<<sizeof (a.frame_id)<<std::endl;
   cout<<"size of xbee_tx_a.dest_addr_64_h "<<sizeof (a.dest_addr_64_h)<<std::endl;
   cout<<"size of xbee_tx_a.dest_addr_64_l "<<sizeof (a.dest_addr_64_l)<<std::endl;
   cout<<"size of xbee_tx_a.dest_addr_16 "<<sizeof (a.dest_addr_16)<<std::endl;
   cout<<"size of xbee_tx_a.broadcast_radius "<<sizeof (a.broadcast_radius)<<std::endl;
   cout<<"size of xbee_tx_a.options "<<sizeof (a.options)<<std::endl;
   cout<<"size of xbee_tx_a.order_index "<<sizeof (a.order_index)<<std::endl;
   cout<<"size of xbee_tx_a.total_packets "<<sizeof (a.total_packets)<<std::endl;
   cout<<"size of xbee_tx_a.rf_data "<<sizeof (a.rf_data)<<std::endl;

   cout<<"----------------------------------------------------------\n";

   xbee_tx_b b;
   cout<<"size of xbee_tx_b "<<sizeof (xbee_tx_b)<<std::endl;
   cout<<"size of xbee_tx_b.api_id "<<sizeof (b.api_id)<<std::endl;
   cout<<"size of xbee_tx_b.frame_id "<<sizeof (b.frame_id)<<std::endl;
   cout<<"size of xbee_tx_b.dest_addr_64_h "<<sizeof (b.dest_addr_64_h)<<std::endl;
   cout<<"size of xbee_tx_b.dest_addr_64_l "<<sizeof (b.dest_addr_64_l)<<std::endl;
   cout<<"size of xbee_tx_b.dest_addr_16 "<<sizeof (b.dest_addr_16)<<std::endl;
   cout<<"size of xbee_tx_b.broadcast_radius "<<sizeof (b.broadcast_radius)<<std::endl;
   cout<<"size of xbee_tx_b.options "<<sizeof (b.options)<<std::endl;
   cout<<"size of xbee_tx_b.order_index "<<sizeof (b.order_index)<<std::endl;
   cout<<"size of xbee_tx_b.total_packets "<<sizeof (b.total_packets)<<std::endl;
   cout<<"size of xbee_tx_b.rf_data "<<sizeof (b.rf_data)<<std::endl;
}

тогда я получаю следующий результат:

size of xbee_tx_a 20
size of xbee_tx_a.api_id 1
size of xbee_tx_a.frame_id 1
size of xbee_tx_a.dest_addr_64_h 4
size of xbee_tx_a.dest_addr_64_l 4
size of xbee_tx_a.dest_addr_16 2
size of xbee_tx_a.broadcast_radius 1
size of xbee_tx_a.options 1
size of xbee_tx_a.order_index 2
size of xbee_tx_a.total_packets 2
size of xbee_tx_a.rf_data 0
----------------------------------------------------------
size of xbee_tx_b 14
size of xbee_tx_b.api_id 1
size of xbee_tx_b.frame_id 1
size of xbee_tx_b.dest_addr_64_h 2
size of xbee_tx_b.dest_addr_64_l 2
size of xbee_tx_b.dest_addr_16 2
size of xbee_tx_b.broadcast_radius 1
size of xbee_tx_b.options 1
size of xbee_tx_b.order_index 2
size of xbee_tx_b.total_packets 2
size of xbee_tx_b.rf_data 0

Я распечатываю общий размер структуры и размер каждого члена структуры.

В случае xbee_tx_b размеры членов складываются в размер структуры (14)

В случае xbee_tx_a размеры членов в сумме составляют 18 байтов ... но размер структуры составляет 20 байтов!

Единственная разница между xbee_tx_a и xbee_tx_b заключается в типе членов dest_addr_64_X. Это uint32_t в xbee_tx_a и uint16_t в xbee_tx_b. Почему размер структуры больше суммы размеров ее членов, когда я использую uint32_t? Откуда берутся эти 2 дополнительных байта?

Спасибо!


person Juan Camilo Gamboa    schedule 01.11.2011    source источник
comment
Чудеса заполнения структуры.   -  person Alok Save    schedule 01.11.2011
comment
Заполнение и выравнивание поиска. Кроме того, с каких это пор мы определяем типы наших структур?   -  person Kerrek SB    schedule 01.11.2011
comment
возможный дубликат Почему sizeof для структуры не равен сумме sizeof каждого члена?   -  person Matt Ball    schedule 01.11.2011


Ответы (4)


Структуры дополняются целым числом, кратным 4 байтам 1, чтобы они были выровнены по словам. http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding

Смотрите также:


1 Как прокомментировал @Mooing Duck, это не всегда правда:

Это не всегда кратное 4 байтам, оно варьируется (немного) в зависимости от членов. С другой стороны, в 99% случаев он кратен 4 байтам.

person Matt Ball    schedule 01.11.2011
comment
Это не всегда кратное 4 байтам, оно варьируется (немного) в зависимости от членов. С другой стороны, в 99% случаев он кратен 4 байтам. - person Mooing Duck; 01.11.2011
comment
@MooingDuck Насчет 99% я не знаю. Большинство структур, в которых я работаю, содержат double, поэтому они дополняются до кратного 8. И, конечно, если структура содержит только char и char[], она, вероятно, вообще не будет дополнена. - person James Kanze; 01.11.2011
comment
@JamesKanze: CTRL-F в моем проекте на работе: double: 5 (0%), float: 20 (1%), long: 170 (10%), int: 817 (50%), short: 140 (9%) ), char: 495 (30%), включая комментарии и литералы. Результаты будут отличаться. С другой стороны, на 64-битной версии указателей 8, так что вы, вероятно, правы. Вероятно, это ближе к 20%, чем к 99%. - person Mooing Duck; 01.11.2011
comment
Разве общий принцип не состоит в том, что структуры дополняются выравниванием самого большого члена? Это применяется рекурсивно, и выравнивание примитивного типа обычно равно его размеру или, возможно, 8 для long double. - person Kerrek SB; 01.11.2011
comment
@KerrekSB короткий ответ: нет. Помимо других факторов, заполнение структуры может зависеть от порядка содержимого. stackoverflow.com/questions/119123/ - person Matt Ball; 01.11.2011
comment
@MattBall: Впрочем, это не противоречит тому, что я имел в виду. Внутри элементы структуры, конечно же, выровнены в соответствии с их выравниванием. Но общий размер структуры должен быть дополнен, чтобы быть кратным собственному выравниванию структуры, не? - person Kerrek SB; 01.11.2011
comment
@MooingDuck Это во многом зависит от приложения. В прошлом я много работал с телефонными системами, и обычно они не содержали ни одного double. Однако в других приложениях, в том числе в том, над которым я сейчас работаю, double встречается чаще, чем string или int. - person James Kanze; 02.11.2011

Это из-за выравнивания. На вашей платформе uint32_t должен быть выровнен по 4 байта. Чтобы добиться этого, dest_addr_64_h должен иметь два байта заполнения прямо перед ним, потому что позиция сразу после двух членов uint8_t кратна 2, но не 4.

Вы можете использовать макрос offsetof (), чтобы точно определить, где размещаются члены в структуре, чтобы убедиться, что это правда.

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

person bames53    schedule 01.11.2011
comment
Или вы можете разработать код так, чтобы он не имел значения. Нет особого смысла в упакованной структуре, если только вы не глубоко разбираетесь в ОС. - person James Kanze; 01.11.2011

Типы данных имеют разные требования к согласованию в зависимости от платформы. Дополнительные байты используются для выравнивания одного из элементов вашей структуры по определенному размеру и / или положению. ЕСЛИ вам нужен более точный контроль, вы можете указать это выравнивание с помощью __attribute__ или #pragma pack

person Dark Falcon    schedule 01.11.2011

Вам необходимо объявить компилятору упаковку структуры

Я считаю, что это сработает для GCC

struct test
    {
            unsigned char  field1;
            unsigned short field2;
            unsigned long  field3;
    } __attribute__((__packed__));

В MS это было бы что-то с использованием прагмы, упакованной

http://www.cplusplus.com/forum/general/14659/

#pragma pack(push, 1) // exact fit - no padding
struct MyStruct
{
  char b; 
  int a; 
  int array[2];
};
#pragma pack(pop) //back to whatever the previous packing mode was 
person Michael    schedule 01.11.2011
comment
Да, но будьте осторожны при доступе к элементам структуры через указатели: stackoverflow.com/questions/1756811/does-gccs-attribute-packed/ - person Ambroz Bizjak; 01.11.2011