35 votes

Tableau de taille 0 en fin de structure

Mon professeur de programmation de systèmes sûr, je suis prenant nous a dit aujourd'hui de définir une structure avec un zéro-longueur de tableau à la fin:

struct array{
    size_t size;
    int data[0];
};

typedef struct array array;

C'est un bon struct de définir ou d'initialiser un tableau avec une variable, c'est à dire, quelque chose comme suit:

array *array_new(size_t size){
    array* a = malloc(sizeof(array) + size * sizeof(int));

    if(a){
        a->size = size;
    }

    return a;
}

C'est, en utilisant malloc(), nous avons également allouer de la mémoire pour le tableau de taille zéro. C'est complètement nouveau pour moi, et il me semble bizarre, parce que, selon ma compréhension, les structures ne disposent pas de leurs éléments nécessairement en continu dans des endroits.

Pourquoi le code en array_new allouer de la mémoire à l' data[0]? Pourquoi serait-il légal d'accès, disons

array * a = array_new(3);
a->data[1] = 12;

?

À partir de ce qu'il nous a dit, il semble qu'un tableau défini comme la longueur de zéro à la fin d'une struct est assurée à venir immédiatement après le dernier élément de la structure, mais cela semble étrange, parce que, de nouveau, à partir de ma compréhension, les structures pourrait avoir de remplissage.

J'ai aussi vu partout que c'est juste une caractéristique de la gcc et n'est pas définie par une norme. Est-ce vrai?

37voto

Sourav Ghosh Points 54713

Actuellement, il existe une fonction standard, comme mentionné dans le C11, chapitre §6.7.2.1, appelé flexible membre du groupe.

Citant la norme,

Comme cas particulier, le dernier élément d'une structure avec plus d'un nommé membre peut ont incomplète de type tableau, ce qu'on appelle un flexible de membre du groupe. Dans la plupart des situations, le flexible de membre du groupe est ignoré. En particulier, la taille de la structure est comme si l' flexible membre du groupe ont été omis, à l'exception qu'il peut avoir plus de remplissage à droite de l'omission impliquerait. [...]

La syntaxe doit être

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

où le dernier élément est de type incomplète, (pas de dimensions du tableau, pas même à 0).

Donc, votre code doit mieux ressembler à

struct array{
    size_t size;
    int data[ ];
};

être la norme-conforme.

Maintenant, pour en venir à votre exemple, de 0 tableau de taille, c'était un héritage façon ("struct hack") d'atteindre le même. Avant d' C99, GCC pris en charge cela comme une extension pour émuler flexible membre du groupe de la fonctionnalité.

24voto

Lundin Points 21616

Votre professeur est confus. Ils devraient aller lire ce qui se passe si je définis une taille zéro tableau. C'est un non-standard GCC extension; il n'est pas valide en C et pas quelque chose qu'ils devraient enseigner aux élèves à utiliser (*).

Au lieu de cela, utiliser la norme C flexible membre du groupe. Contrairement à zéro tableau de taille, il sera en fait de travail, de façon portable:

struct array{
    size_t size;
    int data[];
};

Flexible les membres du groupe sont garantis comptent comme le zéro lorsque vous utilisez sizeof sur la struct, vous permettant de faire des choses comme:

malloc(sizeof(array) + sizeof(int[size]));

(*) Dans les années 90, les gens utilisés dangereux exploiter pour ajouter des données après les structures, connu comme le "struct hack". Pour fournir un moyen sûr de prolonger une struct, GCC a mis en œuvre le zéro-tableau de taille caractéristique de la non-extension standard. Elle est devenue obsolète en 1999, lorsque la norme C finalement fourni une meilleure façon de le faire.

7voto

haccks Points 33022

D'autres réponses explique que le zéro de tableaux de longueur sont GCC extension et C permet de varier la longueur du tableau, mais pas de l'une adressée à vos autres questions.

de ma compréhension, les structures ne disposent pas de leurs éléments nécessairement en continu dans des endroits.

Oui. struct type de données n'ont pas leurs éléments nécessairement en continu dans des endroits.

Pourquoi le code en array_new allouer de la mémoire à l' data[0]? Pourquoi serait-il légal d'accès, disons

array * a = array_new(3);
a->data[1] = 12;

?

Vous devriez noter que l'un de la restriction sur le zéro-longueur de la matrice, c'est qu'il doit être le dernier membre d'une structure. Par la présente, le compilateur sait que la structure peut avoir une longueur variable de l'objet et un peu plus de mémoire seront nécessaires lors de l'exécution.
Mais, vous ne devriez pas être confondu avec; "depuis zéro-longueur du tableau est le dernier membre de la structure puis la mémoire allouée pour le zéro-longueur de la matrice doit être ajouté à la fin de la structure et depuis les structures ne disposent pas de leurs éléments nécessairement en continu les emplacements, puis comment pourrait-mémoire allouée y accéder?"

Pas de. Ce n'est pas le cas. L'allocation de mémoire pour la structure des membres qui ne sont pas nécessairement contigus, il peut y avoir un rembourrage entre eux, mais que la mémoire allouée doit être accessible avec la variable data. Et oui, rembourrage aura pas d'effet sur ici. La règle est la suivante: §6.7.2.1/15

Au sein d'une structure de l'objet, de la non-bits membres et les unités dans lesquelles la bit-champs résident ont des adresses qui augmentent dans l'ordre dans lequel elles sont déclarées.


J'ai aussi vu partout que c'est juste une caractéristique de la gcc et n'est pas définie par une norme. Est-ce vrai?

Oui. Comme d'autres réponses déjà mentionné que le zéro de tableaux de longueur ne sont pas pris en charge par le standard C, mais une extension de compilateurs GCC. C99 introduit flexible membre du groupe. Un exemple en C standard (6.7.2.1):

Après la déclaration:

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

la structure struct s dispose d'un flexible de membre du groupe d. D'une manière typique pour l'utilisation c'est:

int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));

et en supposant que l'appel à l' malloc réussit, l'objet pointé par p se comporte, pour la plupart des fins, comme si p a été déclaré que:

struct { int n; double d[m]; } *p;

(il y a des circonstances dans lesquelles cette équivalence est brisée; en particulier, les décalages de membre de l' d pourrait ne pas être le même).

1voto

Neil Points 1235

Une méthode plus standard serait de définir votre tableau avec une taille de données de 1, comme dans:

 struct array{
    size_t size;
    int data[1]; // <--- will work across compilers
};
 

Ensuite, utilisez le décalage du membre de données (pas la taille du tableau) dans le calcul:

 array *array_new(size_t size){
    array* a = malloc(offsetof(array, data) + size * sizeof(int));

    if(a){
        a->size = size;
    }

    return a;
}
 

Ceci utilise efficacement array.data en tant que marqueur pour indiquer où les données supplémentaires pourraient aller (en fonction de la taille).

1voto

jdlugosz Points 96

La façon dont j'ai l'habitude de le faire est sans un mannequin membre à la fin de la structure: la taille de la structure elle-même vous indique l'adresse, juste après elle. Ajoutez 1 à l'tapé pointeur va là:

header * p = malloc (sizeof (header) + buffersize);
char * buffer = (char*)(p+1);

Comme pour les structures en général, vous pouvez savoir que les champs sont placés dans l'ordre. Être capable de correspondre à certains imposées à la structure nécessaire par un format de fichier de l'image binaire, appel du système d'exploitation ou du matériel est un avantage de l'utilisation de C. Vous devez savoir comment le rembourrage pour l'alignement des œuvres, mais ils sont dans l'ordre et dans un bloc contigu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X