DEV Community

Pierre Gradot
Pierre Gradot

Posted on • Updated on

Les joies des conversions d’entiers en C et C++

NOTE: L'article a été originellement publié sur mon blog perso, l'année dernière, pour fêter 7 ans de blogging ! Il m'a semble être un bon candidat pour tester dev.to : )

J'avais choisi un sujet sur lequel j'avais beaucoup écrit aux début de mon blog : le C et ses écueils. Allez, c'est parti ! Parlons d'un résultat inattendu d'une division entre entiers en C (par extension, le C++ est concerné), et allons faire un tour de la norme pour tirer les choses au clair !]

Question à 1 dollar : que retourne le code suivant ?

int main() {
    int a = -6;
    unsigned int b = 3;
    int c = a / b;
    return c;
}

Puisque je pose la question, vous vous doutez bien que ça ne retourne pas -2. Non, sur Windows 10 avec mingw64, ça retourne 1431655763.

Mais alors pourquoi ? Lors de la division, a est promu en unsigned int. Le calcul est donc fait entre deux entiers non signés puis remis dans un signé. Voilà, c'est aussi simple que ça.

Vous n'êtes pas contents ? Vous pensiez que les options -Wall -Wextra verraient ce genre de problèmes ? Je l'ai dit et je le répète encore : ces options sont le minimum vital mais il y en a bien d'autres à activer pour se protéger. Et surtout, c'est un comportement parfaitement normé du langage. Il suffit de se rendre à la section 6.3.1.8 Usual arithmetic conversions de n1256, la norme C99 pour y lire :

if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type

En fait, il y a moyen d'avoir un avertissement du compilateur. Il suffit de rajouter l'option -Wsign-conversion pour obtenir deux warnings sur la même ligne :

warning: conversion to 'unsigned int' from 'int' may change the sign of the result [-Wsign-conversion]
warning: conversion to 'int' from 'unsigned int' may change the sign of the result [-Wsign-conversion]

Le premier correspond à la conversion de b en unsigned int ; le second à la conversion du résultat en int pour l'affecter à c.

cppcheck dit aussi de faire attention :

cppcheck: (warning) Suspicious code: sign conversion of a in calculation,
 even though a can have a negative value

Notez au passage que a + b, a -b et a * c produisent les bons résultats (au moins sur mon PC, avec ma version de compilateur). Les 4 opérations génèrent des warnings avec GCC mais seules la multiplication et la division génèrent un warning de cppcheck.

PS : merci à Pulkomandy pour m'avoir posé cette colle :p

Discussion (0)