Comment obtenir SIZE_MAX en C89

J’essaie d’obtenir SIZE_MAX dans C89.

J’ai pensé au moyen suivant pour trouver SIZE_MAX :

 const size_t SIZE_MAX = -1; 

Depuis la norme (§6.2.1.2 ANSI C) dit:

Lorsqu’un entier signé est converti en un entier non signé de taille égale ou supérieure, si la valeur de l’entier signé n’est pas négative, sa valeur rest inchangée. Sinon: si la taille de l’entier non signé est supérieure, l’entier signé est d’abord promu en entier signé correspondant à l’entier non signé; la valeur est convertie en unsigned en lui ajoutant un plus grand que le plus grand nombre pouvant être représenté dans le type entier non signé 28

Avec note de bas de page 28:

Dans une représentation en complément à deux, il n’y a pas de changement réel dans la configuration de bits, à l’exception du remplissage des bits de poids fort avec des copies du bit de signe si l’entier non signé a une taille supérieure.

Cela semble avoir défini le comportement, mais je ne suis pas tout à fait sûr de bien comprendre le libellé de ce paragraphe.

Notez que cette question concerne explicitement C89, elle ne répond donc pas à ma question car la formulation de la norme est différente.

Si cela ne fonctionne pas, l’inverse est que:

 size_t get_size_max() { static size_t max = 0; if (max == 0) { max -= 1U; } return max; } 

Mais je n’ai rien trouvé à propos du dépassement de nombre entier non signé dans la norme, alors je pique dans le noir ici.

Vous pouvez utiliser:

 #ifndef SIZE_MAX #define SIZE_MAX ((size_t)(-1)) #endif 

Le comportement de la conversion de -1 en type entier non signé est défini au paragraphe C11 6.3.1.3 “Conversions – Entiers signés et non signés”. C89 avait une définition équivalente, numérotée 3.2.1.2. En fait, vous avez cité la définition ISO C90 6.2.1.2 dans votre question (la différence entre ANSI C89 et ISO C90 est que les sections sont numérotées différemment).

Je ne recommanderais pas l’utilisation d’une variable const , car elles ne peuvent pas être utilisées dans des expressions constantes.


Remarque: Ceci ne peut pas être utilisé dans l’arithmétique du préprocesseur C90, qui ne fonctionne que sur les expressions constantes de nombres entiers ne contenant ni mots ni expressions, de sorte que nous ne pouvons utiliser aucune sizeof . Dans ce cas, vous aurez peut-être besoin d’une définition spécifique au système. il n’y a pas de moyen standard pour le préprocesseur de détecter un typedef.

Je recommande d’utiliser la définition de macro telle que décrite dans la réponse de MM .

Dans certains cas, vous aurez peut-être besoin d’une macro similaire, mais sous forme de constante numérique, pour pouvoir l’utiliser dans des directives de préprocesseur telles que #if VALUE > 42#endif . J’ai commenté que dans de tels cas, un programme d’assistance peut être exécuté lors de la compilation pour calculer et imprimer un fichier d’en-tête définissant de telles constantes.

Évidemment, cela ne fonctionnera pas lors de la compilation croisée dans une architecture différente; dans ce cas, le fichier d’en-tête doit être fourni d’une autre manière. (Par exemple, le projet peut avoir un sous-répertoire d’en-têtes pré-générés et une liste d’architectures connues pour chacun, afin que l’utilisateur puisse simplement copier le fichier d’en-tête en place.)

Créer un Makefile et les fonctionnalités associées pour exécuter de tels programmes (et uniquement si l’utilisateur n’a pas copié le fichier d’en-tête à la place) n’est pas difficile.

Tout d’abord, supposons que votre programme se compose de deux fichiers sources, foo.c :

 #include  extern void hello(void); int main(void) { hello(); return EXIT_SUCCESS; } 

et un bar.c :

 #include  #include "size_max.h" #define STRINGIFY_(s) #s #define STRINGIFY(s) STRINGIFY_(s) void hello(void) { fputs("SIZE_MAX = \"" STRINGIFY(SIZE_MAX) "\".\n", stdout); } 

La barre ci-dessus convertit la macro du préprocesseur SIZE_MAX en une chaîne et l’imprime. Si nous avions #define SIZE_MAX (size_t)(-1) , cela SIZE_MAX = "(size_t)(-1)" .

Notez que bar.c inclut le fichier size_max.h, que nous n’avons pas. C’est le fichier d’en-tête que nous avons l’intention de générer à l’aide de notre programme d’assistance, size_max.c :

 #include  #include  int main(void) { printf("#ifndef SIZE_MAX\n"); printf("#define SIZE_MAX %lluU\n", (unsigned long long)(size_t)(-1)); printf("#endif\n"); return EXIT_SUCCESS; } 

chux a noté dans un commentaire que le suffixe u (pour un type entier non signé suffisamment grand) pourrait être nécessaire. Si ce n’est pas ce dont vous avez besoin, je suis sûr que vous pouvez modifier le générateur de macros en fonction de vos besoins.

MM a noté dans un commentaire que %z n’est pas pris en charge par ANSI C / ISO C90. Le programme ci-dessus crée donc d’abord la constante en utilisant (size_t)(-1) , puis en la unsigned long long format unsigned long long .

Maintenant, les Makefiles peuvent être écrits de manière agnostique, mais je suis trop paresseux pour le faire ici. Je vais donc utiliser les valeurs qui fonctionnent avec les outils GNU. Pour que cela fonctionne sur d’autres systèmes, il vous suffit de modifier les valeurs de

  • CC , pour refléter le compilateur que vous utilisez

  • CFLAGS , pour refléter vos options de compilateur préférées

  • LD , pour refléter votre éditeur de liens, sauf si identique à CC

  • LDFLAGS , si vous avez besoin de drapeaux d’éditeur de liens (peut-être -lm ?)

  • RM , pour refléter la commande pour supprimer les fichiers inutiles

  • Les noms de fichiers, si votre système de construction nécessite une extension de nom de fichier géniale pour les exécutables

Quoi qu’il en soit, voici le Makefile :

 CC := gcc CFLAGS := -Wall -O2 LD := $(CC) LDFLAGS := $(CFLAGS) RM := rm -f # Programs to be built PROGS := example # Relative path to use for executing the header generator helper program HEADERGEN := ./headergen # Rules that do not correspond to actual files .PHONY: all clean headergen # Default rule is to build all binaries all: $(PROGS) # Clean rule removes build files and binaries clean: -$(RM) $(PROGS) $(HELPROG) *.o size_max.h # Rule to "rebuild" size_max.h size_max.h: size_max.c -@$(RM) $(HEADERGEN) size_max.h @$(CC) $(CFLAGS) $^ -o $(HEADERGEN) $(HEADERGEN) > size_max.h @$(RM) $(HEADERGEN) # Rule to build object files from .c source files %.o: %.c size_max.h $(CC) $(CFLAGS) -c $< # Example binary requires foo.o and bar.o: example: foo.o bar.o size_max.h $(LD) $(LDFLAGS) foo.o bar.o -o $@ 

Notez que l'indentation doit utiliser des tabulations et non des espaces. Ainsi, si vous copiez-collez ce qui précède, exécutez par exemple sed -e 's|^ *|\t|' -i Makefile sed -e 's|^ *|\t|' -i Makefile pour le réparer.

Avant de compresser ou de compresser l’arborescence source, exécutez la commande make clean pour supprimer tous les fichiers générés.

Notez le supplément size_max.h dans les prérequirejs de la recette. Il indique à make de s’assurer que size_max.h existe avant de pouvoir terminer la recette.

L'inconvénient de cette approche est que vous ne pouvez pas utiliser $^ dans les recettes de liens pour faire référence à tous les noms de fichiers prérequirejs. $< fait référence au premier nom de fichier prérequirejs. Si vous utilisez GNU make ou un make compatible, vous pouvez utiliser $(filter-out %.h, %^) (pour répertorier tous les prérequirejs sauf les fichiers d’en-tête).

Si tous vos fichiers binarys sont générés à partir d’une source unique portant le même nom, vous pouvez remplacer les deux dernières recettes par

 # All programs are built from same name source files: $(PROGS): %: %.c size_max.h $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@ 

Sur mon système, en cours d'exécution

 make clean all && ./example 

les sorties

 rm -f example *.o size_max.h ./headergen > size_max.h gcc -Wall -O2 -c foo.c gcc -Wall -O2 -c bar.c gcc -Wall -O2 foo.o bar.o -o example SIZE_MAX = "18446744073709551615U". 

et courir

 make CC="gcc-5" CFLAGS="-Wall -std=c99 -pedantic -m32" clean all && ./example 

les sorties

 rm -f example *.o size_max.h ./headergen > size_max.h gcc-5 -Wall -std=c99 -pedantic -m32 -c foo.c gcc-5 -Wall -std=c99 -pedantic -m32 -c bar.c gcc-5 -Wall -std=c99 -pedantic -m32 foo.o bar.o -o example SIZE_MAX = "4294967295U". 

Notez que make ne détecte pas si vous modifiez les options du compilateur, si vous modifiez le Makefile ou si vous utilisez différentes options CFLAGS= ou CC= lors de l’exécution de make. Vous devez donc spécifier la cible clean abord, afin de vous assurer de bien démarrer avec les nouveaux parameters en vigueur.

Lors de l'édition et des constructions normales, lorsque vous ne modifiez pas les compilateurs ou les options du compilateur, il n'est pas nécessaire de make clean générations entre elles.