web-dev-qa-db-fra.com

Que se passe-t-il si je définis un tableau de taille 0 en C / C ++?

Juste curieux, que se passe-t-il si je définis un tableau de longueur nulle int array[0]; Dans le code? GCC ne se plaint pas du tout.

exemple de programme

#include <stdio.h>

int main() {
    int arr[0];
    return 0;
}

Clarification

J'essaie en fait de savoir si les tableaux de longueur nulle initialisés de cette façon, au lieu d'être pointés comme la longueur variable dans les commentaires de Darhazer, sont optimisés ou non.

C’est parce que je dois libérer du code dans la nature, alors j’essaie de comprendre si je dois gérer les cas où le SIZE est défini comme 0, Ce qui arrive dans certains cas. code avec une définition statique int array[SIZE];

En fait, j'ai été surpris que GCC ne se plaint pas, ce qui a conduit à ma question. D'après les réponses que j'ai reçues, l'absence d'avertissement tient en grande partie à la prise en charge de l'ancien code qui n'a pas été mis à jour avec la nouvelle syntaxe [].

Parce que je m'interrogeais principalement sur l'erreur, je marquais la réponse de Lundin comme correcte (celle de Nawaz était la première, mais elle n'était pas aussi complète). t exactement ce que je cherchais.

119
Alex Koay

Un tableau ne peut avoir aucune taille.

ISO 9899: 2011 6.7.6.2:

Si l'expression est une expression constante, sa valeur doit être supérieure à zéro.

Le texte ci-dessus est vrai à la fois pour un tableau simple (paragraphe 1). Pour un VLA (tableau de longueur variable), le comportement est indéfini si la valeur de l'expression est inférieure ou égale à zéro (paragraphe 5). Ceci est un texte normatif dans la norme C. Un compilateur n'est pas autorisé à l'implémenter différemment.

gcc -std=c99 -pedantic donne un avertissement pour le cas non-VLA.

79
Lundin

Normalement, ce n'est pas permis.

Cependant, il est de pratique courante en C d’utiliser des tableaux souples .

C99 6.7.2.1, §16: Dans ce cas particulier, le dernier élément d'une structure comportant plusieurs membres nommés peut avoir un type de tableau incomplet; c'est ce qu'on appelle un membre de groupe flexible.

Manifestation:

struct Array {
  size_t size;
  int content[];
};

L'idée est que vous l'attribueriez alors de la manière suivante:

void foo(size_t x) {
  Array* array = malloc(sizeof(size_t) + x * sizeof(int));

  array->size = x;
  for (size_t i = 0; i != x; ++i) {
    array->content[i] = 0;
  }
}

Vous pouvez également l'utiliser de manière statique (extension gcc):

Array a = { 3, { 1, 2, 3 } };

Ceci est également connu sous le nom de structures à recouvrement (ce terme est antérieur à la publication de la norme C99) ou struct hack (merci à Joe Wreschnig de l'avoir signalé).

Cependant, cette syntaxe n'a été normalisée (et les effets garantis) que récemment en C99. Avant une taille constante était nécessaire.

  • 1 était le moyen portable d'aller, bien que ce fût plutôt étrange
  • 0 indiquait mieux l'intention, mais n'était pas légal en ce qui concerne la norme et était considéré comme une extension par certains compilateurs (y compris gcc)

La pratique du remplissage de queue, cependant, repose sur le fait que le stockage est disponible (attention malloc) donc non adapté pour empiler l'utilisation en général.

74
Matthieu M.

En Standard C et C++, un tableau de taille zéro est non autorisé ..

Si vous utilisez GCC, compilez-le avec -pedantic option. Cela donnera warning, en disant:

zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]

Dans le cas de C++, cela donne un avertissement similaire.

57
Nawaz

C'est totalement illégal, et cela a toujours été le cas, mais de nombreux compilateurs négligent de signaler l'erreur. Je ne sais pas pourquoi tu veux faire ça. L’une des utilisations que je connaisse est de déclencher une erreur de compilation à partir d’un booléen:

char someCondition[ condition ];

Si condition est faux, alors je reçois une erreur de compilation. Parce que les compilateurs le permettent, cependant, j'ai commencé à utiliser:

char someCondition[ 2 * condition - 1 ];

Cela donne une taille de 1 ou -1, et je n'ai jamais trouvé de compilateur qui accepterait une taille de -1.

24
James Kanze

J'ajouterai qu'il y a un page entière de la documentation en ligne de gcc sur cet argument.

Quelques citations:

Les tableaux de longueur nulle sont autorisés dans GNU C.

Dans l’ISO C90, il vous faudrait donner au contenu une longueur de 1

et

Les versions de GCC antérieures à la version 3.0 permettaient l'initialisation statique des tableaux de longueur nulle, comme s'il s'agissait de tableaux flexibles. Outre les cas utiles, cela permettait également les initialisations dans des situations susceptibles de corrompre des données ultérieures.

pour que tu puisses

int arr[0] = { 1 };

et boum :-)

8
xanatos

Une autre utilisation des tableaux de longueur nulle consiste à créer un objet de longueur variable (pré-C99). Les tableaux de longueur nulle sont différents de tableaux souples qui ont [] sans 0.

Cité de doc gcc :

Les tableaux de longueur nulle sont autorisés dans GNU C. Ils sont très utiles en tant que dernier élément d'une structure qui est vraiment un en-tête pour un objet de longueur variable:

 struct line {
   int length;
   char contents[0];
 };

 struct line *thisline = (struct line *)
   malloc (sizeof (struct line) + this_length);
 thisline->length = this_length;

Dans ISO C99, vous utiliseriez un membre de tableau flexible, dont la syntaxe et la sémantique diffèrent légèrement:

  • Les membres de tableau flexibles sont écrits sous forme de contenu [] sans le 0.
  • Les éléments de groupe flexibles ont un type incomplet, et l'opérateur sizeof peut ne pas être appliqué.

Un exemple concret est un tableau de longueur nulle de struct kdbus_item in kdbus.h (module du noyau Linux).

7
Duke

Les déclarations de tableau de taille zéro dans les structures seraient utiles si elles étaient autorisées et si la sémantique était telle que (1) elles forceraient l'alignement mais n'alloueraient autrement aucun espace, et (2) l'indexation du tableau serait considérée comme un comportement défini dans la cas où le pointeur résultant serait dans le même bloc de mémoire que la structure. Un tel comportement n’a jamais été autorisé par un standard C, mais certains compilateurs plus anciens l’autorisaient avant qu’il devienne standard pour permettre aux compilateurs d’autoriser les déclarations de tableaux incomplets avec des crochets vides.

Le struct hack, utilisé couramment avec un tableau de taille 1, est louche et je ne pense pas qu'il soit nécessaire que les compilateurs s'abstiennent de le casser. Par exemple, je pense que si un compilateur voit int a[1], il serait dans son droit de considérer a[i] comme a[0]. Si quelqu'un essaie de contourner les problèmes d'alignement de la struct hack via quelque chose comme

 typedef struct {
 uint32_t size; 
 uint8_t data [4]; // Utilisez quatre pour éviter que le padding ne gêne la taille de la structure 
} 

un compilateur peut devenir intelligent et supposer que la taille du tableau est vraiment quatre:

; Tel qu'écrit 
 Foo = myStruct-> data [i]; 
; Tel qu'interprété (en supposant du matériel little-endian) 
 Toto = ((* (uint32_t *) maStruct-> data) >> (i << 3)) & 0xFF; 

Une telle optimisation pourrait être raisonnable, surtout si myStruct->data pourrait être chargé dans un registre dans la même opération que myStruct->size. Je ne connais rien dans la norme qui interdirait une telle optimisation, même si, bien sûr, cela briserait tout code susceptible de donner accès à des éléments au-delà du quatrième élément.

6
supercat