domingo, 24 de abril de 2011

GCC - Atributos (1)

O GCC permite que você especifique atributos de funções, variáveis e estruturas usando a palavra-chave __attribute__, cuja sintaxe é __attribute__((lista de atributos)).

Os atributos podem ser especificados com dois underscores __ antes e depois de cada palavra. O atributo que irei falar aqui é o packed, ou __packed__.

The packed attribute specifies that a variable or structure field should have the smallest possible alignment--one byte for a variable, and one bit for a field, unless you specify a larger value with the aligned attribute.
(GCC 3.3.3 Manual)


Em tradução livre: O atributo packed especifica que uma variável ou campo de uma estrutura deve ter o mínimo de alinhamento possível -- um byte para uma variável, e um bit para um campo, a não ser que você especifique um valor maior que o atributo alinhado.

Isso significa que o GCC não vai adicionar zeros para preencher (para o alinhamento da memória), de modo que as variáveis ou campos fiquem imediatamente ao lado de cada uma. Por exemplo,


struct abc {
    int a;
    char b;
    int c;
};

struct abc    teste = {10, 20, 30};


Compilando o programa com a opção -S (compila o programa, mas não faz a montagem nem o link).

gcc -S -c sempacked.c -o sempacked.s


Olhando o arquivo gerado, sempacked.s:


    .file    "sempacked.c"
.globl test
    .data
    .align 4
    .type    teste, @object
    .size    teste, 12
teste:
    .long    10
    .byte    20
    .zero    3
    .long    30

    .ident    "GCC: (Gentoo 4.4.4-r2 p1.4, pie-0.4.5) 4.4.4"
    .section    .note.GNU-stack,"",@progbits


Veja o código em negrito. É aonde uma variável teste da estrutura é declarada. Atribuindo valores, para o campo "a" (int) como .long 10 seguido de "b" (char) como .byte 20. Para manter os campos alinhados, veja que o GCC adicionou 3 bytes com valor zero (.zero 3) antes do campo "c" (int), que foi declarado com .long 30. Isso faz com que a estrutura tenha tamanho 12 ao invés de 9, como é esperado.



struct abc {
    int a;
    char b;
    int c;
} __attribute__((__packed__));

struct abc    teste = {10, 20, 30};


E ele compilado:


    .file    "packed.c"
.globl teste
    .data
    .type    teste, @object
    .size    teste, 9
teste:
    .long    
10
    .byte    20
    .long    30

    .ident    "GCC: (Gentoo 4.4.4-r2 p1.4, pie-0.4.5) 4.4.4"
    .section    .note.GNU-stack,"",@progbits


Agora ele não gerou o preenchimento de zeros, fazendo com que o tamanho da estrutura abc seja 9. Lembre-se que alinhamento de memória sempre é bom, mesmo que comprometa espaço, então pense duas vezes antes de usar esse atributo. Geralmente, ele é útil quando você quer atribuir a estrutura para um bloco de memória e manipular ele através dos campos da estrutura.

1 comentários:

Mamutti disse...

Interessante. Não sei de nenhuma situação onde eu gostaria de manipular um bloco de memória usando um struct, mas imagino que deve servir pra algo de bem baixo nível, tipo kernel, controlar dispositivos ou coisas assim.