Dans le code du noyau Linux, j'ai trouvé la chose suivante que je ne peux pas comprendre.
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
Le code est ici: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
Quel est le besoin et le but d'un tableau de données sans élément?
c
structure
flexible-array-member
Jeegar Patel
la source
la source
Réponses:
C'est un moyen d'avoir des tailles de données variables, sans avoir à appeler
malloc
(kmalloc
dans ce cas) deux fois. Vous l'utiliseriez comme ceci:Cela n'était pas standard et était considéré comme un hack (comme l'a dit Aniket), mais il a été standardisé en C99 . Le format standard pour cela est maintenant:
Notez que vous ne mentionnez aucune taille pour le
data
champ. Notez également que cette variable spéciale ne peut venir qu'à la fin de la structure.Dans C99, cette question est expliquée au 6.7.2.1.16 (c'est moi qui souligne):
Ou en d'autres termes, si vous avez:
Vous pouvez y accéder
var->data
avec des index au format[0, extra)
. Notez quesizeof(struct something)
ne donnera la taille que pour les autres variables, c'est-à-dire donnedata
une taille de 0.Il peut être intéressant de noter également comment la norme donne en fait des exemples d'
malloc
une telle construction (6.7.2.1.17):Une autre note intéressante de la norme au même endroit est (c'est moi qui souligne):
la source
[0, extra)
?C'est un hack en fait, pour GCC ( C90 ) en fait.
C'est aussi appelé un struct hack .
Alors la prochaine fois, je dirais:
Ce sera équivalent à dire:
Et je peux créer n'importe quel nombre de ces objets struct.
la source
L'idée est de permettre un tableau de taille variable à la fin de la structure. Il y a probablement
bts_action
un paquet de données avec un en-tête de taille fixe (les champstype
etsize
) et undata
membre de taille variable . En le déclarant comme un tableau de longueur 0, il peut être indexé comme n'importe quel autre tableau. Vous alloueriez ensuite unebts_action
structure, disons dedata
taille 1024 octets , comme ceci:Voir aussi: http://c2.com/cgi/wiki?StructHack
la source
malloc
vous fait vous répéter plusieurs fois et si jamais le type deaction
changements, vous devez le corriger plusieurs fois. Comparez les deux suivants pour vous-même et vous saurez:struct some_thing *variable = (struct some_thing *)malloc(10 * sizeof(struct some_thing));
vsstruct some_thing *variable = malloc(10 * sizeof(*variable));
Le second est plus court, plus propre et clairement plus facile à changer.Le code n'est pas valide C ( voir ceci ). Le noyau Linux n'est, pour des raisons évidentes, pas du tout concerné par la portabilité, il utilise donc beaucoup de code non standard.
Ce qu'ils font, c'est une extension non standard de GCC avec une taille de tableau 0. Un programme conforme au standard aurait écrit
u8 data[];
et cela aurait signifié exactement la même chose. Les auteurs du noyau Linux aiment apparemment rendre les choses inutilement compliquées et non standard, si une option pour le faire se révèle.Dans les anciens standards C, terminer une structure par un tableau vide était connu sous le nom de "struct hack". D'autres ont déjà expliqué son objectif dans d'autres réponses. Le piratage de la structure, dans le standard C90, était un comportement non défini et pouvait provoquer des plantages, principalement du fait qu'un compilateur C est libre d'ajouter n'importe quel nombre d'octets de remplissage à la fin de la structure. Ces octets de remplissage peuvent entrer en collision avec les données que vous avez essayé de "pirater" à la fin de la structure.
GCC a dès le début fait une extension non standard pour changer ce comportement indéfini en comportement bien défini. La norme C99 a ensuite adapté ce concept et tout programme C moderne peut donc utiliser cette fonctionnalité sans risque. Il est connu sous le nom de membre de matrice flexible dans C99 / C11.
la source
Une autre utilisation du tableau de longueur nulle est une étiquette nommée à l'intérieur d'une structure pour aider à la vérification du décalage de la structure au moment de la compilation.
Supposons que vous ayez des définitions de structure volumineuses (couvrant plusieurs lignes de cache) que vous souhaitez vous assurer qu'elles sont alignées sur la limite de la ligne de cache à la fois au début et au milieu, là où elle traverse la limite.
Dans le code, vous pouvez les déclarer à l'aide d'extensions GCC comme:
Mais vous voulez toujours vous assurer que cela est appliqué au moment de l'exécution.
Cela fonctionnerait pour une seule structure, mais il serait difficile de couvrir plusieurs structures, chacune ayant un nom de membre différent à aligner. Vous obtiendrez probablement du code comme ci-dessous où vous devez trouver les noms du premier membre de chaque structure:
Au lieu d'aller de cette façon, vous pouvez déclarer un tableau de longueur nulle dans la structure agissant comme une étiquette nommée avec un nom cohérent mais ne consomme aucun espace.
Ensuite, le code d'assertion d'exécution serait beaucoup plus facile à maintenir:
la source