No código do kernel do Linux, encontrei a seguinte coisa que não consigo entender.
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
O código está aqui: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
Qual é a necessidade e o objetivo de uma matriz de dados com zero elementos?
c
structure
flexible-array-member
Jeegar Patel
fonte
fonte
Respostas:
Essa é uma maneira de ter tamanhos variáveis de dados, sem precisar chamar
malloc
(kmalloc
nesse caso) duas vezes. Você usaria assim:Isso costumava não ser padrão e era considerado um hack (como disse Aniket), mas era padronizado no C99 . O formato padrão para ele agora é:
Observe que você não menciona nenhum tamanho para o
data
campo. Observe também que essa variável especial pode vir apenas no final da estrutura.Em C99, esse assunto é explicado em 6.7.2.1.16 (grifo meu):
Ou, em outras palavras, se você tiver:
Você pode acessar
var->data
com índices em[0, extra)
. Observe quesizeof(struct something)
apenas o tamanho é responsável pelas demais variáveis, ou seja, odata
tamanho é 0.Também pode ser interessante observar como o padrão realmente fornece exemplos de
malloc
tais construções (6.7.2.1.17):Outra observação interessante do padrão no mesmo local é (ênfase minha):
fonte
[0, extra)
?Na verdade, esse é um truque, para o GCC ( C90 ).
Também é chamado de struct hack .
Então, da próxima vez, eu diria:
Será equivalente a dizer:
E eu posso criar qualquer número desses objetos struct.
fonte
A idéia é permitir uma matriz de tamanho variável no final da estrutura. Presumivelmente,
bts_action
há alguns pacotes de dados com um cabeçalho de tamanho fixo (os campostype
esize
) e umdata
membro de tamanho variável . Ao declará-lo como uma matriz de comprimento 0, pode ser indexado como qualquer outra matriz. Vocêbts_action
alocaria uma estrutura, comdata
tamanho de 1024 bytes , da seguinte forma:Veja também: http://c2.com/cgi/wiki?StructHack
fonte
malloc
faz com que você se repita várias vezes e, se é queaction
muda o tipo de alteração, precisa corrigi-lo várias vezes. Compare os dois a seguir e você saberá:struct some_thing *variable = (struct some_thing *)malloc(10 * sizeof(struct some_thing));
vs.struct some_thing *variable = malloc(10 * sizeof(*variable));
O segundo é mais curto, mais limpo e claramente mais fácil de mudar.O código não é válido C ( veja isso ). O kernel do Linux, por razões óbvias, não se preocupa nem um pouco com a portabilidade, por isso usa bastante código não-padrão.
O que eles estão fazendo é uma extensão não-padrão do GCC com tamanho de matriz 0. Um programa compatível com o padrão teria sido escrito
u8 data[];
e significaria o mesmo. Os autores do kernel Linux aparentemente adoram tornar as coisas desnecessariamente complicadas e não padronizadas, se uma opção para isso se revelar.Nos padrões C mais antigos, o final de uma estrutura com uma matriz vazia era conhecido como "o truque da estrutura". Outros já explicaram seu objetivo em outras respostas. O truque de estrutura, no padrão C90, era um comportamento indefinido e poderia causar falhas, principalmente porque um compilador C é livre para adicionar qualquer número de bytes de preenchimento no final da estrutura. Esses bytes de preenchimento podem colidir com os dados que você tentou "invadir" no final da estrutura.
No início, o GCC fez uma extensão não padrão para alterar esse comportamento de indefinido para bem definido. O padrão C99 adaptou esse conceito e qualquer programa C moderno pode, portanto, usar esse recurso sem risco. É conhecido como membro flexível da matriz em C99 / C11.
fonte
Outro uso da matriz de comprimento zero é como um rótulo nomeado dentro de uma estrutura para auxiliar na verificação do deslocamento da estrutura do tempo de compilação.
Suponha que você tenha algumas definições grandes de estrutura (que abrangem várias linhas de cache) que deseja garantir que elas estejam alinhadas ao cache da fronteira da linha, tanto no começo quanto no meio em que ela cruza a fronteira.
No código, você pode declará-las usando extensões do GCC, como:
Mas você ainda deseja garantir que isso seja imposto em tempo de execução.
Isso funcionaria para uma única estrutura, mas seria difícil cobrir muitas estruturas, cada uma com um nome de membro diferente a ser alinhado. Você provavelmente obteria código como abaixo, onde você precisa encontrar os nomes do primeiro membro de cada estrutura:
Em vez de seguir esse caminho, você pode declarar uma matriz de comprimento zero na estrutura, atuando como um rótulo nomeado com um nome consistente, mas não consome nenhum espaço.
Então, o código de asserção de tempo de execução seria muito mais fácil de manter:
fonte