Pourquoi tout le monde tape-il sur les types C standard?

103

Si vous souhaitez utiliser Qt , vous devez accepter quint8, quint16et ainsi de suite.

Si vous souhaitez utiliser GLib , vous devez accueillir guint8, guint16et ainsi de suite.

Sur Linux, il y en a u32, s16et ainsi de suite.

uC / OS définit SINT32, UINT16et ainsi de suite.

Et si vous devez utiliser une combinaison de ces éléments, vous feriez mieux de vous préparer aux ennuis. Parce que sur votre machine u32sera typedefterminé longet quint32sera typedefterminé intet le compilateur se plaindra .

Pourquoi tout le monde fait-il cela, s'il y en a <stdint.h>? S'agit-il d'une sorte de tradition pour les bibliothèques?

Amomum
la source
8
@Mehrdad dans la programmation de microcontrôleurs, vous pouvez avoir toutes sortes de choses. Sur AVR Mega par exemple (et par conséquent sur le célèbre Arduino), l'int est de 16 bits. Cela peut être une mauvaise surprise. À mon avis, «short non signé» nécessite plus d'effort de frappe. Et cela m'a toujours rendu triste d'utiliser "unsigned char" pour <s> octet </s> octet. Caractère non signé, vraiment?
Amomum
2
@Mehrdad Le fait est que vous ne pouvez pas vraiment être sûr. C'est exactement pourquoi a stdint.hété inventé.
glglgl
1
@glglgl: Voici une autre façon de voir le problème: ne posez-vous pas précisément la mauvaise question? Si vous ciblez plusieurs systèmes, pourquoi coder arbitrairement en dur le nombre de bits dans le code en premier lieu? c'est-à-dire, pourquoi ne pas simplement dire sizeof(int) * CHAR_BIT(par exemple) et utiliser cela? Si votre intest trop petit pour représenter votre plage (par exemple un index de tableau), alors vous ne devriez certainement pas utiliser de inttoute façon, mais quelque chose comme size_t. Pourquoi aurait-il int32plus de sens? Le seul moment où la largeur fixe a du sens est pour la communication entre les systèmes (par exemple, format de fichier / réseau) ...
user541686
1
@Mehrdad Non. Parfois, j'ai des valeurs (comme celles d'un ADC ou autre) que je dois stocker. Je sais qu'ils ont une largeur de 16 bits. La meilleure chose à utiliser est donc uint16_t(ou peut-être sa fastou sa leastvariante). Mon point étant: ces types sont pratiques à utiliser et ont leur raison d'être.
glglgl
1
@Mehrdad: Je suggérerais que - en supposant que cela vaille la peine de produire du code de qualité - vous devriez définir vos propres typedefs fonctionnels signifiant la façon d'interagir avec mon API / le reste de mon code , et les définir sur des bases techniques en termes de Les typedefs "techniques" comme size_tet / ou uint64_t.
PJTraill

Réponses:

80

stdint.hn'existait pas à l'époque où ces bibliothèques étaient en cours de développement. Chaque bibliothèque a donc créé ses propres typedefs.

Edward Karak
la source
4
Et d'accord, je suppose que le manque de stdint.h est une bonne raison, mais pourquoi même aujourd'hui ces typedefs sont sur int, long et ainsi pour et pas sur les types stdint? Cela les rendrait au moins interchangeables
Amomum
25
@Amomum "Pourquoi Glib (qui a été développé sous Linux) n'a pas utilisé les typedefs Linux?" Alors que la "base" de glib est certainement Linux, c'est aussi certainement par conception une bibliothèque portable. La définition de ses propres types garantit la portabilité: il suffit d'adapter un petit en-tête qui correspond aux types de bibliothèques aux types de plates-formes cibles respectifs.
Peter - Réintégrer Monica le
3
@Amomum Pourquoi Glib (qui a été développé sous Linux) ... Non, ce n'était pas le cas. Glib a été créé bien avant le noyau Linux.
andy256
5
@ andy256 "GLib" n'est pas l'abréviation de "glibc". C'est une bibliothèque issue de gtk. Ce n'est pas plus ancien que Linux.
2
@ andy256: glib est 1998, linux est 1991. IOW, GLib a été créé bien après Linux.
MSalters
40

Pour les bibliothèques plus anciennes, cela est nécessaire car l'en-tête en question ( stdint.h) n'existait pas.

Cependant, il existe toujours un problème: ces types ( uint64_tet d'autres) sont une fonctionnalité facultative de la norme. Ainsi, une implémentation conforme pourrait ne pas être livrée avec eux - et obliger ainsi les bibliothèques à les inclure encore de nos jours.

Ven
la source
14
Les uintN_ttypes sont facultatifs, mais les types uint_leastN_tet uint_fastN_tne le sont pas.
Kusalananda
7
@Kusalananda: Malheureusement, ceux-ci sont d'une utilité limitée.
Courses de légèreté en orbite le
16
Bien sûr, la raison pour laquelle ils sont facultatifs est que vous n'êtes pas assuré qu'il existe des types entiers avec exactement ce nombre de bits. C prend toujours en charge les architectures avec des tailles entières plutôt impaires.
celtschk
5
@LightnessRacesinOrbit: En quoi sont-ils d'une utilité limitée? Je dois admettre qu'en dehors des interfaces matérielles, je ne vois pas pourquoi vous auriez besoin d'un nombre exact de bits, plutôt que d'un minimum, pour vous assurer que toutes vos valeurs correspondent.
celtschk
23
@Amomum Les typedefs sont obligatoires si l'implémentation a des types répondant aux exigences: "Cependant, si une implémentation fournit des types entiers avec des largeurs de 8, 16, 32 ou 64 bits, pas de bits de remplissage, et (pour les types signés) qui ont une représentation complémentaire à deux, il définira les noms de typedef correspondants. " (citation de N1570, 7.20.1.1 "Types d'entiers de largeur exacte") Donc, si la bibliothèque standard ne les a pas, une bibliothèque tierce ne pourrait pas non plus, semble-t-il.
Eric M Schmidt
13

stdint.h est normalisé depuis 1999. Il est plus probable que de nombreuses applications définissent des types (en fait des alias) pour conserver une indépendance partielle par rapport à l'architecture de la machine sous-jacente.

Ils donnent aux développeurs l'assurance que les types utilisés dans leur application correspondent aux hypothèses spécifiques de leur projet sur le comportement qui peut ne pas correspondre à la norme de langage ou à l'implémentation du compilateur.

La pratique est reflétée dans le modèle de conception de Façade orienté objet et est beaucoup abusée par les développeurs qui écrivent invariablement des classes wrapper pour toutes les bibliothèques importées.

Lorsque les conformes étaient beaucoup moins standard et que les architectures de machine pouvaient varier de 16 bits, 18 bits à 36 bits de longueur de mot, c'était beaucoup plus une considération. La pratique est beaucoup moins pertinente maintenant dans un monde convergeant vers les systèmes embarqués ARM 32 bits. Cela reste une préoccupation pour les microcontrôleurs bas de gamme avec des cartes mémoire impaires .

Pekka
la source
1
Certes, il stdint.hest normalisé depuis 1999, mais depuis combien de temps est-il disponible dans la pratique? Les gens traînent les pieds pour mettre en œuvre et adopter de nouvelles normes, et pendant cette longue période de transition, les anciennes méthodes sont toujours indispensables.
Siyuan Ren
1
Un inconvénient stdint.hest que même sur les plates-formes où, par exemple, longet int32_tont la même taille et la même représentation, il n'est pas nécessaire que le cast d'un int32_t*to long*produise un pointeur qui peut accéder de manière fiable à un fichier int32_t. Je ne peux pas croire que les auteurs du Standard aient pensé qu'il était évident que les types compatibles avec la mise en page devraient être compatibles avec les alias, mais comme ils n'ont pas pris la peine de le dire, les auteurs de gcc et IIRC clang pensent que le langage serait amélioré en ignorant même l'aliasing. dans les cas où c'est évident.
supercat du
2
@supercat - cela vaut probablement la peine d'être soumis comme un erratum au comité C ... parce que c'est gratuitement stupide , pour le dire légèrement
LThode
@LThode: Pour que le comité C reconnaisse cela comme une erreur, il faudrait qu'il déclare officiellement le comportement de clang et gcc comme obtus. Pensez-vous que cela va arriver? Le mieux que l'on puisse espérer (et à mon humble avis la manière logique de procéder) serait de définir des moyens pour que les programmes spécifient des "modes d'aliasing". Si un programme dit spécifie qu'il peut accepter des règles d'alias très strictes, alors un compilateur pourrait l'utiliser pour permettre des optimisations au-delà de ce qui est actuellement possible. Si un programme spécifie qu'il nécessite des règles un peu plus conviviales pour les programmeurs que les présentes ...
supercat
... mais permettrait encore de nombreuses optimisations utiles, alors un compilateur pourrait générer du code beaucoup plus efficace que ce qui serait possible avec -fno-strict-alias, mais qui fonctionnerait toujours. Même s'il n'y avait pas de base de code existante, aucun ensemble de règles ne pourrait trouver l'équilibre optimal entre l'optimisation et la sémantique pour toutes les applications, car différentes applications ont des besoins différents. Ajoutez la base de code existante et la nécessité de différents modes devrait être claire.
supercat du
3

Vous avez donc le pouvoir de typedef char to int.

Une "horreur du codage" a mentionné qu'un en-tête de société avait un point où un programmeur voulait une valeur booléenne, et un char était le type natif logique pour le travail, et ainsi de suite typedef bool char. Puis, plus tard, quelqu'un a trouvé qu'un entier était le choix le plus logique et a écrit typedef bool int. Le résultat, bien avant Unicode, était virtuellement typedef char int.

Beaucoup de compatibilité avant-gardiste et prospective, je pense.

Christos Hayward
la source