Le contexte
Nous portons du code C qui a été initialement compilé à l'aide d'un compilateur C 8 bits pour le microcontrôleur PIC. Un idiome commun utilisé pour empêcher les variables globales non signées (par exemple, les compteurs d'erreurs) de revenir à zéro est le suivant:
if(~counter) counter++;
L'opérateur au niveau du bit inverse ici tous les bits et l'instruction n'est vraie que si elle counter
est inférieure à la valeur maximale. Surtout, cela fonctionne indépendamment de la taille variable.
Problème
Nous visons maintenant un processeur ARM 32 bits utilisant GCC. Nous avons remarqué que le même code produit des résultats différents. Pour autant que nous puissions en juger, il semble que l'opération de complément au niveau du bit renvoie une valeur d'une taille différente de celle à laquelle nous nous attendions. Pour reproduire cela, nous compilons, dans GCC:
uint8_t i = 0;
int sz;
sz = sizeof(i);
printf("Size of variable: %d\n", sz); // Size of variable: 1
sz = sizeof(~i);
printf("Size of result: %d\n", sz); // Size of result: 4
Dans la première ligne de sortie, nous obtenons ce que nous attendons: i
est de 1 octet. Cependant, le complément au niveau du bit de i
est en fait de quatre octets, ce qui cause un problème car les comparaisons avec celui-ci ne donneront pas les résultats attendus. Par exemple, si vous faites (où i
est correctement initialisé uint8_t
):
if(~i) i++;
Nous verrons i
"boucler" de 0xFF à 0x00. Ce comportement est différent dans GCC par rapport à quand il fonctionnait comme nous le voulions dans le compilateur précédent et le microcontrôleur PIC 8 bits.
Nous sommes conscients que nous pouvons résoudre ce problème en effectuant un casting comme ceci:
if((uint8_t)~i) i++;
Ou, par
if(i < 0xFF) i++;
Toutefois, dans ces deux solutions de contournement, la taille de la variable doit être connue et est sujette à erreur pour le développeur de logiciels. Ces types de vérification des limites supérieures se produisent dans toute la base de code. Il y a plusieurs tailles de variables (par exemple, uint16_t
et unsigned char
etc.) et les changer dans une base de code qui fonctionne autrement n'est pas quelque chose que nous attendons avec impatience.
Question
Notre compréhension du problème est-elle correcte et existe-t-il des options pour résoudre ce problème qui ne nécessitent pas de revoir chaque cas où nous avons utilisé cet idiome? Notre hypothèse est-elle correcte, qu'une opération comme le complément au niveau du bit devrait retourner un résultat de la même taille que l'opérande? Il semble que cela se casserait, selon les architectures du processeur. J'ai l'impression de prendre des pilules folles et que C devrait être un peu plus portable que ça. Encore une fois, notre compréhension de cela pourrait être fausse.
En apparence, cela ne semble pas être un problème énorme, mais cet idiome fonctionnant précédemment est utilisé dans des centaines d'endroits et nous sommes impatients de le comprendre avant de procéder à des changements coûteux.
Remarque: Il y a une question en double apparemment similaire mais pas exacte ici: l' opération au niveau du bit sur char donne un résultat 32 bits
Je n'ai pas vu le véritable nœud du problème discuté ici, à savoir, la taille du résultat d'un complément au niveau du bit étant différente de celle transmise à l'opérateur.