8
votes

Membre de tableau flexible dans la structure C

citating de la section C-STD 6.7.2.1,

struct s { int n; double d[]; };


1 commentaires

Ah, c'est un bon exemple qui démontre à nouveau que les tableaux et les pointeurs ne sont pas du tout le même :)


4 Réponses :


13
votes

Le C FAQ répond précisément à cette question. La réponse rapide est que cette structure inclura la matrice double à l'intérieur de la structure plutôt qu'un pointeur sur un tableau en dehors de la structure. Exemple rapide, vous pouvez utiliser votre structure comme dans cet exemple: xxx

et ainsi de suite - la taille de la matrice que vous vous souciez est incluse dans l'allocation, puis vous pouvez l'utiliser. Tout comme n'importe quel tableau. Normalement, un tel type contient la taille dans le cadre de la structure, car l'utilisation du truc + pour passer à travers un tableau de type S sera nécessairement compliqué par cette situation.

à votre question ajoutée 'Comment est-ce que cette construction est plus ou moins puissante que de garder un [pointeur] comme le 2e élément?', ce n'est plus puissant en soi, mais vous n'avez pas t besoin de garder un pointeur autour, de sorte que vous économiseriez au moins que beaucoup d'espace - aussi lorsque vous copiez la structure, vous copieriez également le tableau, plutôt qu'un pointeur sur une matrice - une différence subtile parfois, mais très importante d'autres fois. "Vous-pouvez-la-faire-it-it-it-it-in-plusieurs-façons" est probablement une bonne explication, mais il y a des cas où vous voudriez spécifiquement une conception ou l'autre.


5 commentaires

donc structure s s1 = malloc (...); puis struct S S2 = S1; signifierait que S2 obtient un tableau qui est créé automatiquement et contenu de S1 copié? Est-ce que la même maintien si au lieu de types de pod, struct S a une classe définie par l'utilisateur en tant que 2e élément?


Non, aucun copie magique ne se produirait avec une assignation de structure; Mais si vous utilisez memcpy () avec la taille appropriée, il fonctionnera. Si vous avez un pointeur, vous auriez besoin d'allouer de la mémoire et de copier le tableau séparément.


Je ne suis pas sûr que ce lien vers la FAQ C, Q2.6, répond vraiment à cette question. Si c'est le cas, ce n'est que dans un sens arcanique qui n'aurait aucun sens à quelqu'un qui connaît déjà la réponse. En fait, le lien suggère cela, s'il parle de la même chose, ne doit pas être considéré comme portable.


@Arpan: Votre exemple n'est pas possible comme écrit, car si struct s a un élément de tableau flexible, le type est incomplet et que vous ne pouvez pas déclarer une variable de ce type (vous ne pouvez déclarer que des pointeurs à IT - struct s * ). Vous ne pouvez pas le changer à struct s * s1 = malloc (); structure s * s2; * S2 = * S1; soit, car cela essaille toujours d'accéder à un type incomplet. Aucun de ceux-ci ne compilera.


le struct s {int n; Double D []; }; L'approche présente l'avantage que vous obtenez une bonne localité de cache entre l'INT avec la longueur de la matrice et le début des données de la matrice réelle.



0
votes

J'ai vu cela utilisé sur Windows pour les chaînes étiquetées par leur longueur. Les données de caractères sont stockées directement après la longueur en mémoire, tout en gardant ensemble soigneusement.

typedef struct {
    SIZE_T bytes;
    TCHAR chars[];
} tagged_string;


0 commentaires

3
votes

Vous pouvez l'utiliser pour ajouter des champs d'en-tête à des tableaux alloués dynamiquement, dont celui le plus commun serait sa taille: xxx

Vous pouvez obtenir des résultats similaires en utilisant un int * membre plutôt et attribuant la matrice séparément, mais il serait moins efficace à la fois en termes de mémoire (pointeur supplémentaire, gestion du tas de 2e bloc de mémoire) et d'exécution (indirecte supplémentaire, 2e attribution). < / p>


0 commentaires

5
votes

L'avantage principal est qu'un élément de tableau flexible vous permet d'allouer un bloc de mémoire Single pour le tableau avec les autres données de la structure (avec un pointeur, vous finirez généralement pas de finir. avec deux blocs alloués séparément).

Il est également utile avec des données transmises par des protocoles réseau toutaux, où le flux entrant est défini de la même manière - un entier définissant une longueur, suivi de ces nombreuses unités (typiquement octets / octets) des données. Vous pouvez utiliser (typiquement) un type de jeu de mots pour superposer une structure avec un élément de matrice flexible sur un tampon rempli de telles données et le traverse directement au lieu de l'analyser en morceaux, puis de travailler avec les pièces individuellement. < / p>


3 commentaires

Dans mon expérience, la mise en œuvre d'un protocole réseau (ou d'un format de fichier, qui est essentiellement le même problème) par type-Punning, un tampon d'octets sur un type de structure est généralement un cas de votre problème. La désérialisation du terrain sur le terrain qui finit plutôt d'être beaucoup plus portable.


@CAF: le champ de désérialisation par champ est plus portable, mais le type Punning peut dans certains cas permettre au code d'être plus lisible et plus efficace, surtout s'il peut construire une table des pointeurs sur des objets stockés dans un tampon existant, plutôt que d'avoir à allouer Espace pour une deuxième copie de toutes les informations, puis copiez toutes les informations du tampon de bytes dans l'espace nouvellement attribué. Ce qui aurait fait que les choses qui auraient réellement portable auraient été si c entreprises de structures "explicites", donc le code pourrait dire, par exemple. "J'ai besoin d'un type de données de 64 octets, peut être localisé ...


... sur une limite de 2 octets et inclut [entre autres] un entier 32 bits appelé "Woozle" stocké à la décalage 12 comme quatre octets de l'ordre de Little-Endian. "Avoir un compilateur supportant ce type de chose et gérer Il est efficace dans les cas où il coïncide avec une mise en page naturelle d'un compilateur serait moins cher que d'essayer de reconnaître et d'optimiser toutes les variations différentes du (((uint32_t) PTR [15] << 24) | ((UINT32_T) PTR [14 ] << 16) | ((uint32_t) PTR [13] << 8) | PTR [12]) qui pourrait être remplacé par un couple de charge de 16 bits à partir de l'adresse PTR + 12 et PTR + 14, ou une seule charge 32 bits de PTR + 12.