Que signifie “représentable” dans C11?

Selon le projet de version C11 WG14, N1570 :

L’en-tête clare plusieurs fonctions utiles pour la classification et le mappage de caractères. Dans tous les cas, l’argument est un int dont la valeur doit pouvoir être représentée sous la forme d’un caractère unsigned char ou doit être égale à la valeur de la macro EOF . Si l’argument a une autre valeur, le comportement est indéfini.

Est-ce un comportement indéfini?

 #include  #include  #include  int main(void) { char c = CHAR_MIN; /* let assume that char is signed and CHAR_MIN < 0 */ return isspace(c) ? EXIT_FAILURE : EXIT_SUCCESS; } 

La norme permet-elle de passer char à isspace() ( char à int )? En d’autres termes, char après conversion en int représentable en tant que unsigned char ?


Voici comment wiktionary définit “representable” :

Capable d’être représenté.

Est-ce que char est capable d’être représenté en tant que unsigned char ? Oui §6.2.6.1 / 4:

Les valeurs stockées dans des objects autres que des CHAR_BIT bits de tout autre type d’object sont composées de n × bits CHAR_BIT , où n est la taille en octets d’un object de ce type. La valeur peut être copiée dans un object de type unsigned char [ n ] (par exemple, par memcpy ); l’ensemble d’octets résultant est appelé la représentation d’object de la valeur.

sizeof(char) == 1 conséquent, sa représentation d’object est unsigned char[1] c’est-à-dire que char est capable d’être représenté comme un caractère unsigned char . Où est-ce que je me trompe?

Exemple concret, je peux représenter [-2, -1, 0, 1] comme [0, 1, 2, 3] . Si je ne peux pas alors pourquoi?


En relation: Selon §6.3.1.3, isspace((unsigned char)c) est portable si INT_MAX >= UCHAR_MAX sinon il est défini par la mise en oeuvre.

En supposant que char soit signé, il s’agirait alors d’ un comportement indéfini , sinon, il est bien défini puisque CHAR_MIN aurait la valeur 0 . Il est plus facile de voir l’intention et la signification de:

dont la valeur doit pouvoir être représentée sous la forme d’un caractère non signé ou doit être égale à la valeur de la macro EOF

si nous lisons la section 7.4 Traitement des caractères de la justification de la norme internationale Standard – Langages de programmation — C, qui dit (l’ accent est mis sur l’avenir ):

Étant donné que ces fonctions sont souvent principalement utilisées comme macros, leur domaine est limité aux petits entiers positifs pouvant être représentés dans un caractère non signé, plus la valeur de EOF . EOF est traditionnellement -1, mais peut être n’importe quel entier négatif, et donc différent de tout code de caractère valide. Ces macros peuvent donc être efficacement implémentées en utilisant l’argument comme index dans un petit tableau d’atsortingbuts.

Les valeurs valides sont donc:

  1. Entiers positifs pouvant tenir dans un caractère non signé
  2. EOF qui est un nombre négatif défini par l’implémentation

Même s’il s’agit là d’une logique C99 puisque le libellé auquel vous faites référence ne change pas de C99 à C11 et que la logique rest donc valable.

Nous pouvons également trouver pourquoi l’interface utilise int en tant qu’argument plutôt que char , à partir de la section 7.1.4 Utilisation des fonctions de bibliothèque , on lit:

Tous les prototypes de bibliothèque sont spécifiés en termes de types «élargis». Un argument précédemment déclaré comme char est maintenant écrit comme int. Cela garantit que la plupart des fonctions de bibliothèque peuvent être appelées avec ou sans prototype , ce qui permet de conserver une compatibilité ascendante avec le code antérieur à C89. Notez cependant que, comme les fonctions printf et scanf utilisent des listes d’arguments de longueur variable, elles doivent être appelées dans le cadre d’un prototype.

Que signifie représentable dans un type?

Reformulé, un type est une convention pour la signification des motifs de bits sous-jacents. Une valeur est donc représentable dans un type, si ce type affecte un motif binary à cette signification.

Une conversion (qui peut nécessiter une conversion) est un mappage d’une valeur (représentée par un type spécifique) vers une valeur (éventuellement différente) représentée dans le type cible.


Sous l’hypothèse donnée (que le caractère est signé), CHAR_MIN est certainement négatif, et le texte que vous avez cité ne laisse aucune place à l’interprétation:
Oui, il s’agit d’un comportement non défini, car unsigned char ne peut représenter aucun nombre négatif.

Si cette hypothèse ne se CHAR_MIN pas, votre programme serait bien défini, car CHAR_MIN aurait pour valeur 0 , une valeur valide pour le caractère unsigned char .

Ainsi, nous avons un cas où il est défini par implémentation si le programme est indéfini ou bien défini.


En passant, rien ne garantit que sizeof(int)>1 ou INT_MAX >= CHAR_MAX ; int pourrait donc ne pas être en mesure de représenter toutes les valeurs possibles pour le caractère unsigned char .

Comme les conversions sont définies comme préservant les valeurs, un caractère signé peut toujours être converti en int .
Mais si c’était négatif, cela ne change pas l’impossibilité de représenter une valeur négative en tant que caractère unsigned char . (La conversion est définie, car la conversion de tout type intégral en tout type entier unsigned est toujours définie, bien que les conversions réducsortingces nécessitent un transt.)

La citation révélasortingce (pour moi) est § 6.3.1.3 / 1:

si la valeur peut être représentée par le nouveau type, il est inchangé.

Autrement dit, si la valeur doit être modifiée, elle ne peut pas être représentée par le nouveau type.

Par conséquent, un type unsigned ne peut pas représenter une valeur négative.

Pour répondre à la question dans le titre: “représentable” se réfère à “peut être représenté” à partir du § 6.3.1.3 et sans rapport avec la “représentation d’object” à partir du § 6.2.1.

Cela semble banal en rétrospective. J’aurais peut-être été dérouté par l’habitude de traiter b'\xFF' , 0xff , 255 , -1 comme le même octet en Python:

 >>> (255).to_bytes(1, 'big') b'\xff' >>> int.from_bytes(b'\xFF', 'big') 255 >>> 255 == 0xff True >>> (-1).to_bytes(1, 'big', signed=True) b'\xff' 

et l’incrédulité selon laquelle le passage d’un caractère à une fonction de classification de caractère , par exemple, isspace(CHAR_MIN) constitue un comportement indéfini .