Conversions implicites de -1 vers unsigned

Comment sont réalisées ces conversions ?

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Voici un tout petit code pour introduire la question :

#include <stdio.h>
#include <stdlib.h>

int main()
{
	unsigned char a0 = -1;
	unsigned short a1 = -1;
	unsigned long a2 = -1;
	unsigned long long a3 = -1;

	printf(	"a0 : %u\n"
		"a1 : %u\n"
		"a2 : %lu\n"
		"a3 : %llu\n", a0, a1, a2, a3);

	return 0;
}

Ce code me renvoie le nombre max que peuvent contenir les types des variables a0, a1, etc. Ces conversions là ne sont pas expliquées dans le cours de zeste de savoir… On y explique les conversions d’un entier vers un flottant ou d’autres. En l’occurrence, je me demande quel mécanisme permet cette conversion… Qu’aurait affiché mon code si j’avais mis -2, ou -3 ?

Avant l’exécution j’imaginais ceci : en faisant les choses dans l’ordre le compilateur va : écrire -1 sur un int car nous avons une constante entière, ce qui donne FFFFFFFF si un int est codé sur 4 octets. Puis le compilateur va écrire ce nombre dans un unsigned long long (par exemple) ce qui sera un nombre positif mais certainement pas la valeur maximale du type unsigned long long. En y réfléchissant davantage, je me dis qu’avec mon raisonnement, la syntaxe : long long b = -1; ne fonctionnerait pas, car cela reviendrait à stocker un nombre positif dans le long long alors qu’on demande clairement un nombre négatif. De plus cela reviendrait à ce que les syntaxes long long b = -1; et : long long b = 0xffffffff; reviennent exactement au même (ce qui n’est pas le cas). Mais alors cela indique quelque chose de significatif : le tutoriel du site me semble légèrement erroné. La valeur d’une constante entière n’est pas seulement conditionnée par ce qu’elle vaudrait "en soi" mais aussi par ce qui l’entoure. En l’occurrence, pour -1 et 0xffffffff, le compilateur comprend qu’on veut un nombre négatif dans un cas et un positif dans l’autre, pourtant ces deux expressions entières semblent avoir exactement la même valeur.
Pour en rajouter encore, la syntaxe : long long a = 5000000000; (5 milliards) ne fonctionnerait pas si la constante entière 5 milliards était d’abord affectée à un int car le nombre est trop grand pour un int. Cela est en contradiction totale avec le cours qui indique que la constante entière serait affectée à un int par défaut avant que la valeur soit affectée au long long.

D’où ma question : que se passe-t-il "vraiment" lors d’une conversion ? Manifestement le cours commet quelques approximations et j’ai du mal à me mettre les idées au clair seul en faisant des tests. Je ne trouve pas vraiment de réponses ailleurs sur le net…

EDIT : Mon interrogation ne se situe pas au niveau de la représentation des nombres négatifs en mémoire. Je suis bien au fait de la notion de complément à deux. Et je connais la représentation des entiers signés (j’ai eu plusieurs réponses qui cernaient mal ma question, je précise cela pour lever les ambiguïtés)

Pour rajouter encore un exemple à mon soucis, voici un deuxième bout de code :

#include <stdio.h>
#include <stdlib.h>

int main()
{
	unsigned long long a1 = -1;
	unsigned long long a2 = -1UL;
	unsigned long long a3 = -1ULL;

	printf(	"a1 : %llu\n"
		"a2 : %llu\n"
		"a3 : %llu\n", a1, a2, a3);

	return 0;
}

L’exécution me donne :

a1 : 18446744073709551615
a2 : 4294967295
a3 : 18446744073709551615

Le but de mon post est de comprendre l’origine de ce genre résultat, qui-plus-est en contradiction avec des éléments du cours de C de ce site il me semble

EDIT 2 : [en partie faux] après analyse de la réponse de Aabu, on peut anticiper sur le résultat de mon deuxième code, par un raisonnement que je suppose et qui pourrait contenir un bourde, je le soumets à votre validation et je reste attentif à vos futures remarques :

  • Premier cas (a1) : -1 est une constante entière qui est donc (cf. cours) de type int. Pour convertir dans le type unsigned long long, il faut donc ajouter 1, puis ajouter ou retrancher la valeur max du type unsigned long long. On tombe sur la valeur max du type unsigned long long. CQFD.
  • Deuxième cas : -1 est encore une constante entière et de type int par défaut. La présence du suffixe UL va réaliser une conversion vers le type unsigned long. Par le même raisonnement que précédemment, on tombe cette fois sur 4 milliards et quelques. Puis on doit affecter cette valeur à a2 qui est un unsigned long long et toujours en vertu de la méthode avancée par Aabu, cette valeur est affectée inchangée à a2.
  • Troisième cas : -1 est une constante entière de type int. A cause du suffixe ULL, il y a conversion vers le type unsigned long long, ce qui donne (comme pour le premier cas) : 18446744073709551615. Cette valeur est affectée sans changement à a3 car elle est dans la plage de valeurs acceptées.

EDIT 3 : la réponse de Ache donne un bon aperçu de la complexité du type des constantes que l’on retrouve dans la norme (lien : partie 6.4.4.1 Integer constants de la norme C11 (ou C99)). Le type des constantes entières y est détaillé, on comprend immédiatement pourquoi entre autres, si un int fait 4 octets, la constante 0xFFFFFFFF est un unsigned int, et la constante (de même valeur mathématiquement) 4294967295 est un long.

EDIT 4 : en regroupant toutes les réponses on arrive à l’ensemble règles suivantes (pardonnez l’emploi éventuel de mots mal choisis je ne suis pas encore confirmé) :

  • en ce qui concerne un éventuel signe, celui est traité ensuite
  • on traite d’abord la partie avec les nombres et le suffixe. Un nombre est soit en octal (commence par un 0) soit en hexa (commence par un 0x) soit en décimal (commence par entre 1 et 9). Suivant que l’on soit en décimal, en octal ou en hexa, et suivant le suffixe, le compilateur va déterminer le type de la constante suivant le tableau donné dans le norme (section 6.4.4.1 - Integer constants). On choisit le premier type possible ayant offrant assez de place pour le contenir (on raisonne uniquement en termes de capacité : on peut mettre 4 milliards dans un long, bien que mathématiquement on ne soit pas dans la "bonne" plage de valeurs offerte par un long signé : dans ce cas on aura un nombre négatif dans le long en question).
  • On applique l’éventuel signe en faisant un bête complément à deux.
  • Notre constante a maintenant un type, il faut maintenant l’affecter à une variable (par exemple lors de la syntaxe long a = -0x253L, on affecte la valeur à a). Pour l’affectation, on est dans le cas d’une conversion d’un type vers un autre : soit la valeur représentée dans le type source peut être accueillie dans le type cible et la conversion se passe tranquillement, soit ce n’est pas le cas deux cas de figure sont possibles :
  • si le type cible est non signé, on ajoute ou retranche 1+[la valeur max représentable du type cible] autant de fois qu’il faut pour tomber dans la plage de valeurs représentables par le type cible, et c’est cette valeur qui sera affectée
  • si le type cible est signé : ce cas n’est pas intéressant car il y a bien peu de chances qu’un programmeur utilise un tel comportement sans être en fait en train de faire une erreur.

En espérant avoir été clair, voici deux exemples pour illustrer : long a = -0xFFFFFFFFL; ici a vaut 1 unsigned long long a = -1UL; ici a vaut 4294967295

+0 -0

Salut,

Ton premier raisonnement me semble bon, à ceci près qu’il n’y a en fait pas de conversion. La différence entre un nombre signé et un nombre non-signé tient en fait à la façon dont le compilateur va considérer le tout premier bit du nombre :

  • si le nombre est signé (donc il peut être négatif ou positif), alors le premier bit servira à indiquer si le négatif (0 s’il est positif, 1 s’il est négatif).
  • si le nombre est non-signé (donc il ne peut être que positif), alors le premier bit participera à coder la valeur du nombre.

Lorsque tu écris unsigned char c = -1, le compilateur donne en binaire la même valeur que si cela avait été un simple char : 11111111. Cependant, comme c’est un nombre non-signé, sa valeur est donc considéré comme le nombre maximal positif possible, soit ici 255. Si le nombre avait été signé, en revanche, le premier bit codant le signe, il aurait donc considéré le nombre comme étant négatif, et donc le résultat aurait été -1.

Si tu souhaites un peu de doc sur le fonctionnement des nombres signés en binaire, tu peux regarder cette page

J’espère que c’est plus clair :)

+0 -0

La réponse définitive sera dans la norme (enfin son draft, mais c’est sensiblement la même chose). J’ai pris celle indiquée par le cours.

On y trouve le passage suivant à propos des conversions (le gras est de moi) :

6.3.1.3 Signed and unsigned integers

When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

Que je traduis par :

Autrement, si le nouveau type est non-signé, la valeur est convertie en ajoutant ou soustrayant de manière répétée un plus la valeur maximum qui peut être représentée par le nouveau type, jusqu’à ce que la valeur soit dans l’intervalle du nouveau type.

La norme précise dans une note que "valeur" est à prendre au sens mathématique.

Si on applique ça, on se retrouve bien avec le nombre maximum dans le cas de l’OP :

  • on part de -1 ;
  • on ajoute 1, on se retrouve à zéro ;
  • on ajoute le max et on se retrouve donc au maximum du nombre.

On peut tester que ça colle avec une autre valeur :

void main() {
    unsigned int i = -2;
    printf("%u", i);
}

Chez moi, ça affiche 4294967294 (mes unsigned int sont sur 32 bits, je ne sais pas si c’est normatif).

+0 -0

Salut,

Ton premier raisonnement me semble bon, à ceci près qu’il n’y a en fait pas de conversion. La différence entre un nombre signé et un nombre non-signé tient en fait à la façon dont le compilateur va considérer le tout premier bit du nombre :

  • si le nombre est signé (donc il peut être négatif ou positif), alors le premier bit servira à indiquer si le négatif (0 s’il est positif, 1 s’il est négatif).
  • si le nombre est non-signé (donc il ne peut être que positif), alors le premier bit participera à coder la valeur du nombre.

Deuchnord

Merci de ta réponse ! Cependant, je crois que cela ne répond pas vraiment à ma question. Pour reprendre le cours : par défaut une constante entière sera de type int et sera signée sauf si elle est suivie du suffixe U. Donc dans ton exemple, si tu remplaces le char par un long long, la suite de 1 ne devrait naïvement pas être assez grande pour passer le premier bit du long long à 1 et donc le -1 ne devrait pas être transcrit correctement. C’est le tout premier point d’interrogation que je soulève, il y en a d’autres que je trouve moins "évidents"…

Pour comprendre pourquoi -1 devient le plus grand nombre possible quand on le considère non signé, il faut savoir comment sont mémorisés les nombres négatifs.

Commençons par voir le cas non signé. Le plus petit nombre c’est 0, tous ses bits sont à 0. Le plus grand est celui qui à tous ses bits à 1. Si on considère unsigned char on peut y stocker un nombre de 0 à 255. Que se passe-t-il si on ajoute 1 au nombre max:

unsigned char x = 255;
x = x + 1;
printf( "%d\n" , x );
x = x - 1;
printf( "%d\n" , x );

Ça affiche 0, et en faisant l’opération inverse on revient à 255. Les nombres sont cycliques, quand on veut dépasser le nombre max ça recommence.

Pour les nombres signés, on doit en utilisant le même nombres de bits avoir des nombres positifs et des nombres négatifs. On a alors forcément plus la même valeur max que pour l’équivalent non signé. La norme impose que la transformation signée/non signée (c’est que tu veux faire) doit se faire:

  • sans rien changer aux bits mémorisés
  • les valeurs qui sont valides pour les deux sont inchangées. Par exemple: 100 est valide dans unsigned char et dans signed char, est stocké en binaire 0b01100100.

Pour savoir comment sont gérées les nombres négatifs tout en respectant cette règle, la plupart des processeurs utilisent un principe appelée complément à 2. Reprenons l’exemple précédent en utilisant un nombre signé.

signed char x = 255;
x = x + 1;
printf( "%d\n" , x );
x = x - 1;
printf( "%d\n" , x );

On voit que 255 est désormais affiché comme étant -1. Qu’y a-t-il de changé? En fait absolument rien n’a changé! -1 est bien le nombre juste avant zéro, comme 255 est lui aussi le nombre juste avant zéro en non signé. Et donc -1 et 255 sont bien la même chose dans un char suivant si en veux une vision signée ou pas.

Alors comment est codé -2? C’est le nombre avant -1, il est donc codé comme le nombre 254. Si on regarde le code binaire des nombres négatifs, on se rends compte que ce sont tous ceux qui ont leur premier bit à 1. Le premier bit à 1 signale donc un nombre négatif dans une variable signée, et il signale les très grands nombres dans une variable non signée (ceux de 128 à 255 pour le unsigned char).

Si on utilise des stockage plus grands (short int long …), c’est pareil sauf que le maximum est un plus grand nombre.

Pour comprendre pourquoi -1 devient le plus grand nombre possible quand on le considère non signé, il faut savoir comment sont mémorisés les nombres négatifs.

Commençons par voir le cas non signé. Le plus petit nombre c’est 0, tous ses bits sont à 0. Le plus grand est celui qui à tous ses bits à 1. Si on considère unsigned char on peut y stocker un nombre de 0 à 255. Que se passe-t-il si on ajoute 1 au nombre max:

unsigned char x = 255;
x = x + 1;
printf( "%d\n" , x );
x = x - 1;
printf( "%d\n" , x );

Ça affiche 0, et en faisant l’opération inverse on revient à 255. Les nombres sont cycliques, quand on veut dépasser le nombre max ça recommence.

Pour les nombres signés, on doit en utilisant le même nombres de bits avoir des nombres positifs et des nombres négatifs. On a alors forcément plus la même valeur max que pour l’équivalent non signé. La norme impose que la transformation signée/non signée (c’est que tu veux faire) doit se faire:

  • sans rien changer aux bits mémorisés
  • les valeurs qui sont valides pour les deux sont inchangées. Par exemple: 100 est valide dans unsigned char et dans signed char, est stocké en binaire 0b01100100.

Pour savoir comment sont gérées les nombres négatifs tout en respectant cette règle, la plupart des processeurs utilisent un principe appelée complément à 2. Reprenons l’exemple précédent en utilisant un nombre signé.

signed char x = 255;
x = x + 1;
printf( "%d\n" , x );
x = x - 1;
printf( "%d\n" , x );

On voit que 255 est désormais affiché comme étant -1. Qu’y a-t-il de changé? En fait absolument rien n’a changé! -1 est bien le nombre juste avant zéro, comme 255 est lui aussi le nombre juste avant zéro en non signé. Et donc -1 et 255 sont bien la même chose dans un char suivant si en veux une vision signée ou pas.

Alors comment est codé -2? C’est le nombre avant -1, il est donc codé comme le nombre 254. Si on regarde le code binaire des nombres négatifs, on se rends compte que ce sont tous ceux qui ont leur premier bit à 1. Le premier bit à 1 signale donc un nombre négatif dans une variable signée, et il signale les très grands nombres dans une variable non signée (ceux de 128 à 255 pour le unsigned char).

Si on utilise des stockage plus grands (short int long …), c’est pareil sauf que le maximum est un plus grand nombre.

dalfab

Merci pour la réponse complète, cependant il me semble que cela ne répond pas du tout à ma question… Après la réponse pourra sans doute être utile à des gens qui ne sont pas à l’aise avec la représentation des nombres en mémoire, laissez donc la visible. J’ai essayé de toutes mes forces de produire un sujet compréhensible mais force est de constater que tu es la deuxième personne à mal cerner où se situe mon interrogation. Je vais voir s’il faut que je modifie mon post de base

D’où ma question : que se passe-t-il "vraiment" lors d’une conversion ? Manifestement le cours commet quelques approximations et j’ai du mal à me mettre les idées au clair seul en faisant des tests. Je ne trouve pas vraiment de réponses ailleurs sur le net…

AScriabine

La question n’est pas seulement "que se passe-t-il ?", mais également "quand cela se passe-t-il ?".

unsigned long long a1 = -1;
unsigned long long a2 = -1UL;
unsigned long long a3 = -1ULL;

De quel type est -1 ? C’est un peu abstrait comme question, parce que c’est une constante dans le code, pas vraiment un contenu en mémoire. Cependant, il faut savoir comment faire des opérations avec. Autrement dit, c’est purement une convention, et l’endroit où on écrit la convention commune, c’est dans la norme. Que dit la norme ? 6.4.4.1 Integer constants dit qu’un entier sans suffixe est du premier type de la liste qui peut le représenter (il y a la liste dans la norme). Dans notre cas, c’est int. Et cet int vaut 1. Le - ne fait pas partie du nombre. C’est la syntaxe qui le dit. Le - est donc un opérateur unaire, qui va être appliqué au nombre, qui va être promu à int (qu’il a déjà), donc on va avoir un int qui vaut le négatif de 1, -1, qui est représentable. Pour l’affecter à a1, on va faire une promotion à des entiers de même classe, la classe de long long, et comme on a un long long signé à convertir en non signé, on va ajouter la valeur maximum+1 d’un unsigned long long, et donc obtenir le (MAX_ULL+1) - 1. D’où a1 = MAX_ULL

à la deuxième ligne, on a une constante différente: -1UL. 1UL est un unsigned long, qui n’est donc pas promu, donc on va rajouter MAX_UL+1 à -1 pour obtenir une valeur représentable par un unsigned long, et obtenir MAX_UL, qu’on va ensuite convertir en unsigned long long (qui peut représenter cette valeur), et a2 = MAX_UL.

La 3eme se passe comme la première, sauf qu’on fait la promotion avant de considérer l’opération d’affectation.

Et maintenant les questions bonus:

En l’occurrence, je me demande quel mécanisme permet cette conversion…

Le compilateur. C’est lui qui traite le texte du fichier source pour ensuite générer un code machine se comportant comme spécifié dans la norme.

Qu’aurait affiché mon code si j’avais mis -2, ou -3 ?

et bien, à après la conversion vers un type non signé, on aurait eu (max du type non signé + 1 -2/-3) à la place de (max du type non signé + 1 -1). Puis on aurait gardé cette valeur en passant sur des types plus grands.

Pour en rajouter encore, la syntaxe : long long a = 5000000000; (5 milliards) ne fonctionnerait pas si la constante entière 5 milliards était d’abord affectée à un int car le nombre est trop grand pour un int. Cela est en contradiction totale avec le cours qui indique que la constante entière serait affectée à un int par défaut avant que la valeur soit affectée au long long.

Je n’ai pas la référence dans le cours qui dit ça mais ce qui est sûr c’est que le type d’une expression constante n’est pas évident ! Pas du tout. Elle dépend, de la valeur, de la représentation (hexadécimal, décimal ou octal, bientôt peut être même binaire) de la présence éventuelle d’un suffixe de la taille maximum de chaque type et à partir de ça, tu peux te servir d’un tableau qui grâce à ces informations te permet de connaître le type de l’expression, c’est à dire qu'il n’y a pas de règle simple énoncée !

Ainsi, c’est hyper contre intuitif mais 0x65535 n’a pas forcément le même type que 65535. Si un int sur la machine est de 2octets alors 65536 sera de type long int car ce n’est pas représentable dans un int. Alors que 0x65535 sera un unsigned int.

int main() {
    printf("%zd %zd", sizeof(0xFFFFFFFF), sizeof(4294967295));
}

Bref, je comprends que l’auteur du cours est largement simplifié la règle.1

Bien-sûr, si le type n’est représentable dans aucun type alors … c’est spécifié ! Il peut être représenté dans un type étendu donc signe d’une règle encore très obscure.

Bref, je te conseille de lire la partie 6.4.4.1 Integer constants de la norme C11 (ou C99).
Tu comprendras la complexité de ta question.

Pour la conversion des types, la réponse de @Aabu est très complète alors je n’ai rien à ajouter à ce niveau là. Le type d’une expression constante n’était pas ta question de base, je l’ai bien compris mais puisque c’était pas claire ben j’explique. :P

PS: En C11 on peut afficher le type d’une expression avec les Generics ! :D

#include <stdio.h>

#define TYPEOF(x) _Generic((x), \
    short: "short", \
    int: "int", \
    unsigned: "unsigned", \
    long: "long", \
    long long: "long long")


int main() {
    printf("%s / %s", TYPEOF(0xFFFFFFFF), TYPEOF(4294967295));
}

Ça peut certainement t’aider. Ici, ça affichera unsigned puis long sur une machine où int fait 4octets.


  1. Grosso modo, la règle est en représentation décimale. Sans suffixe, l’expression est du plus petit type signé dans lequel ça rentre (généralement int). Si on a U comme suffixe elle est du plus petit type non signé dans lequel ça rentre. Si on met un L en plus, le plus petit type est au moins long (avec U ou sans U) si on met LL alors le plus petit type est long long (de manière à ce que ULL impose le type unsigned long long int).
+0 -0

D’où ma question : que se passe-t-il "vraiment" lors d’une conversion ? Manifestement le cours commet quelques approximations et j’ai du mal à me mettre les idées au clair seul en faisant des tests. Je ne trouve pas vraiment de réponses ailleurs sur le net…

AScriabine

La question n’est pas seulement "que se passe-t-il ?", mais également "quand cela se passe-t-il ?".

unsigned long long a1 = -1;
unsigned long long a2 = -1UL;
unsigned long long a3 = -1ULL;

De quel type est -1 ? C’est un peu abstrait comme question, parce que c’est une constante dans le code, pas vraiment un contenu en mémoire. Cependant, il faut savoir comment faire des opérations avec. Autrement dit, c’est purement une convention, et l’endroit où on écrit la convention commune, c’est dans la norme. Que dit la norme ? 6.4.4.1 Integer constants dit qu’un entier sans suffixe est du premier type de la liste qui peut le représenter (il y a la liste dans la norme). Dans notre cas, c’est int. Et cet int vaut 1. Le - ne fait pas partie du nombre. C’est la syntaxe qui le dit. Le - est donc un opérateur unaire, qui va être appliqué au nombre, qui va être promu à int (qu’il a déjà), donc on va avoir un int qui vaut le négatif de 1, -1, qui est représentable. Pour l’affecter à a1, on va faire une promotion à des entiers de même classe, la classe de long long, et comme on a un long long signé à convertir en non signé, on va ajouter la valeur maximum+1 d’un unsigned long long, et donc obtenir le (MAX_ULL+1) - 1. D’où a1 = MAX_ULL

à la deuxième ligne, on a une constante différente: -1UL. 1UL est un unsigned long, qui n’est donc pas promu, donc on va rajouter MAX_UL+1 à -1 pour obtenir une valeur représentable par un unsigned long, et obtenir MAX_UL, qu’on va ensuite convertir en unsigned long long (qui peut représenter cette valeur), et a2 = MAX_UL.

La 3eme se passe comme la première, sauf qu’on fait la promotion avant de considérer l’opération d’affectation.

Et maintenant les questions bonus:

En l’occurrence, je me demande quel mécanisme permet cette conversion…

Le compilateur. C’est lui qui traite le texte du fichier source pour ensuite générer un code machine se comportant comme spécifié dans la norme.

Qu’aurait affiché mon code si j’avais mis -2, ou -3 ?

et bien, à après la conversion vers un type non signé, on aurait eu (max du type non signé + 1 -2/-3) à la place de (max du type non signé + 1 -1). Puis on aurait gardé cette valeur en passant sur des types plus grands.

Jacen

Merci pour ta réponse cependant tu emploies des expressions comme "entiers de même classe", tu parles aussi de "promotion". Je constate que ces termes sont aussi employés dans la norme, que veulent-ils dire ? Merci d’avance

De plus je me questionne sur le sens du mot "value" dans la norme. En effet, lors de l’utilisation du tableau dans la norme pour connaître le type d’une constante entière, il est dit que le type est choisi s’il permet de stocker la valeur du nombre. J’imagine qu’ici on parle de valeur au sens des bits, pas au sens mathématique (car le type n’ayant pas été attribué, il n’aurait pas de sens d’interpréter un -1 là où on voit seulement des bits à 1). En revanche, lors de l’explication des conversions par Aabu, on voit bien que cette fois, le mot valeur est employé au sens mathématique (on interprète un -1 s’il faut pour la conversion). Il me semble que cette ambiguïté est importante car cela change tout. Penses-tu que je vois juste sur ce point ?

+0 -0

Salut,

Merci pour ta réponse cependant tu emploies des expressions comme "entiers de même classe", tu parles aussi de "promotion". Je constate que ces termes sont aussi employés dans la norme, que veulent-ils dire ?

AScriabine

Le terme de promotions est utilisé à plusieurs endroits. Il en est question dans le tuto dans le chapitre sur les pointeurs de fonctions en ce qui concerne la promotion des arguments. Dans ce cas-ci, il s’agit de la promotion entière, c’est un mécanisme qui s’applique à différents moments, par exemple lors de l’emploi de l’opérateur unaire -.

The result of the unary- operator is the negative of its (promoted) operand. The integer promotions are performed on the operand, and the result has the promoted type.

ISO/IEC 9899:2017, doc. N2176, § 6.5.3.3 Unary arithmetic operators, al. 3, p 64.

Le mécanisme est décrit un peu avant dans la norme et, en très gros, chaque type entier se voit assigner un « rang » et tous les entiers d’un certain rang sont promus en int ou unsigned int avant qu’une opération ne soit effectuée.

The following may be used in an expression wherever an int or unsigned int may be used:

  • An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.
  • A bit-field of type _Bool , int, signed int, or unsigned int.

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.) All other types are unchanged by the integer promotions.

ISO/IEC 9899:2017, doc. N2176, 6.3.1.1 Boolean, characters, and integers, al. 2, p 38.

Les rangs sont décrits juste avant, je ne vais pas citer le paragraphe, mais en très gros : long long int > long int > int > short int > signed char > _Bool.

De plus je me questionne sur le sens du mot "value" dans la norme. En effet, lors de l’utilisation du tableau dans la norme pour connaître le type d’une constante entière, il est dit que le type est choisi s’il permet de stocker la valeur du nombre. J’imagine qu’ici on parle de valeur au sens des bits, pas au sens mathématique (car le type n’ayant pas été attribué, il n’aurait pas de sens d’interpréter un -1 là où on voit seulement des bits à 1). En revanche, lors de l’explication des conversions par Aabu, on voit bien que cette fois, le mot valeur est employé au sens mathématique (on interprète un -1 s’il faut pour la conversion). Il me semble que cette ambiguïté est importante car cela change tout. Penses-tu que je vois juste sur ce point ?

AScriabine

Je ne pense pas qu’il y ait une quelconque différence, dans tous les cas et sur le fond peu importe : la valeur doit être représentable dans un type donné.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte