web-dev-qa-db-fra.com

Tableau de zéro longueur

Je travaille sur le refactoring de l'ancien code et j'ai trouvé peu de structs contenant des tableaux de longueur zéro (ci-dessous). Avertissements déprimés par Pragma, bien sûr, mais j'ai échoué à créer par "nouvelles" structures contenant de telles structures (erreur 2233). Array 'Bydata' utilisé comme pointeur, mais pourquoi ne pas utiliser le pointeur à la place? ou une matrice de longueur 1? Et bien sûr, aucun commentaire n'a été ajouté pour me faire profiter du processus ... Toute cause d'utiliser une telle chose? Des conseils pour refactoriser ceux-ci?

struct someData
{
   int nData;
   BYTE byData[0];
}

NB C'est C++, Windows XP, vs 2003

51
bgee

Oui c'est un C-Hack.
[.____] Pour créer un tableau de toute longueur:

struct someData* mallocSomeData(int size)
{
    struct someData*  result = (struct someData*)malloc(sizeof(struct someData) + size * sizeof(BYTE));
    if (result)
    {    result->nData = size;
    }
    return result;
}

Maintenant, vous avez un objet de SOMEDATA avec un tableau d'une longueur spécifiée.

34
Martin York

Malheureusement, plusieurs raisons pour lesquelles vous déclareriez une matrice de longueur zéro à la fin d'une structure. Il vous donne essentiellement la possibilité d'avoir une structure de longueur variable retournée d'une API.

Raymond Chen a fait un excellent article de blog sur le sujet. Je vous suggère de jeter un coup d'œil à ce post car il contient probablement la réponse que vous voulez.

Note à son poste, il oeuvre dans ces tableaux de taille 1 au lieu de 0. C'est le cas car les matrices de longueur zéro sont une entrée plus récente dans les normes. Son message devrait toujours s'appliquer à votre problème.

http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx

[~ # ~] Edit [~ # ~ #]

Remarque: même si la post de Raymond dit que 0 Longueur Des tableaux sont légaux en C99, ils ne sont toujours pas légaux en C99. Au lieu d'un réseau de 0 longueur, vous devez utiliser une matrice de longueur 1

25
JaredPar

Il s'agit d'un ancien piratage C pour permettre à une matrice de taille flexible.

Dans la norme C99, cela n'est pas nécessaire car il prend en charge la syntaxe de l'ARL [].

22
arul

Votre intuition sur "Pourquoi ne pas utiliser un tableau de taille 1" est sur place.

Le code fait le mal "C struct Hack", car les déclarations de matrices de longueur zéro sont une violation de contraintes. Cela signifie qu'un compilateur peut rejeter votre piratage de la batte au moment de la compilation avec un message de diagnostic qui arrête la traduction.

Si nous voulons commenter un piratage, nous devons la faufiler au-delà du compilateur.

La bonne façon de faire le "C struct hack" (ce qui est compatible avec C Dialectes de retour à 1989 ANSI C, et probablement beaucoup plus tôt) est d'utiliser une matrice parfaitement valide de taille 1:

struct someData
{
   int nData;
   unsigned char byData[1];
}

De plus, au lieu de sizeof struct someData, La taille de la pièce avant byData est calculée en utilisant:

offsetof(struct someData, byData);

Pour allouer un struct someData Avec de l'espace pour 42 octets dans byData, nous utiliserions ensuite:

struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42);

Notez que ce calcul offsetof est en fait le calcul correct même dans le cas de la taille de la matrice étant zéro. Vous voyez, sizeof La structure entière peut inclure un remplissage. Par exemple, si nous avons quelque chose comme ça:

struct hack {
  unsigned long ul;
  char c;
  char foo[0]; /* assuming our compiler accepts this nonsense */
};

La taille de struct hack Est probablement éventuellement rembourrée pour l'alignement en raison du membre ul. Si unsigned long Est de quatre octets larges, alors probablement sizeof (struct hack) est 8, alors que offsetof(struct hack, foo) est presque certainement 5. La méthode offsetof est le moyen de Obtenez la taille précise de la partie précédente de la structure juste avant le tableau.

Donc, ce serait le moyen de refroidir le code: faites-la être conforme à la structure classique et hautement portable.

Pourquoi ne pas utiliser un pointeur? Parce qu'un pointeur occupe un espace supplémentaire et doit être initialisé.

Il existe d'autres bonnes raisons de ne pas utiliser un pointeur, à savoir qu'un pointeur nécessite un espace d'adressage pour être significatif. Le piratage de la structure est externalisable: c'est-à-dire qu'il existe des situations dans lesquelles une telle mise en page est conforme au stockage externe, telles que des zones de fichiers, des paquets ou de la mémoire partagée, dans laquelle vous ne voulez pas de point de signification car ils ne sont pas significatifs.

Il y a plusieurs années, j'ai utilisé le piratage de la structure dans une interface de passe de mémoire partagée entre le noyau et l'espace utilisateur. Je ne voulais pas que des indicateurs, car ils n'auraient été significatifs qu'à l'espace d'adressage d'origine du processus générant un message. La partie du noyau du logiciel a visualisé la mémoire à l'aide de sa propre mappage à une adresse différente, et tout était basé sur des calculs de compensation.

9
Kaz

Il convient de souligner IMO la meilleure façon de faire le calcul de la taille, qui est utilisé dans l'article de Raymond Chen lié ci-dessus.

struct foo
{
    size_t count;
    int data[1];
}

size_t foo_size_from_count(size_t count)
{
    return offsetof(foo, data[count]);
}

Le décalage de la première entrée hors tension de la fin de l'allocation souhaitée est également la taille de l'allocation souhaitée. IMO C'est un moyen extrêmement élégant de faire le calcul de la taille. Peu importe le type d'élément de la matrice de taille variable. Le décalage de (ou field_offset ou ufield_offset dans Windows) est toujours écrit de la même manière. Pas de taille d'expression () expressions pour gâcher accidentellement.

1
Mike Ruete