Kontext
Wir portieren C-Code, der ursprünglich mit einem 8-Bit-C-Compiler für den PIC-Mikrocontroller kompiliert wurde. Eine gebräuchliche Redewendung, die verwendet wurde, um zu verhindern, dass vorzeichenlose globale Variablen (z. B. Fehlerzähler) auf Null zurückgesetzt werden, lautet wie folgt:
if(~counter) counter++;
Der bitweise Operator invertiert hier alle Bits und die Anweisung ist nur wahr, wenn sie counter
kleiner als der Maximalwert ist. Wichtig ist, dass dies unabhängig von der variablen Größe funktioniert.
Problem
Wir zielen jetzt auf einen 32-Bit-ARM-Prozessor mit GCC. Wir haben festgestellt, dass derselbe Code unterschiedliche Ergebnisse liefert. Soweit wir das beurteilen können, scheint die bitweise Komplementoperation einen Wert zurückzugeben, der eine andere Größe hat als wir erwarten würden. Um dies zu reproduzieren, kompilieren wir in 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
In der ersten Ausgabezeile erhalten wir das, was wir erwarten würden: i
1 Byte. Das bitweise Komplement von i
beträgt jedoch tatsächlich vier Bytes, was ein Problem verursacht, da Vergleiche mit diesen jetzt nicht die erwarteten Ergebnisse ergeben. Zum Beispiel, wenn Sie Folgendes tun (wo i
ist eine ordnungsgemäß initialisierte uint8_t
):
if(~i) i++;
Wir werden sehen, wie i
von 0xFF zurück zu 0x00 "gewickelt" wird. Dieses Verhalten unterscheidet sich in GCC von dem, als es früher wie im vorherigen Compiler und 8-Bit-PIC-Mikrocontroller vorgesehen funktioniert hat.
Wir sind uns bewusst, dass wir dies durch Casting wie folgt lösen können:
if((uint8_t)~i) i++;
Oder von
if(i < 0xFF) i++;
In beiden Problemumgehungen muss jedoch die Größe der Variablen bekannt sein und ist für den Softwareentwickler fehleranfällig. Diese Arten von Obergrenzenprüfungen finden in der gesamten Codebasis statt. Es gibt mehrere Größen von Variablen (zB., uint16_t
Und unsigned char
usw.) verwendet wird und dies in einer ansonsten arbeitet Code - Basis ist nicht etwas , das wir freuen uns auf.
Frage
Ist unser Verständnis des Problems korrekt und gibt es Optionen zur Lösung dieses Problems, die nicht einen erneuten Besuch in jedem Fall erfordern, in dem wir diese Redewendung verwendet haben? Ist unsere Annahme richtig, dass eine Operation wie das bitweise Komplement ein Ergebnis zurückgeben sollte, das dieselbe Größe wie der Operand hat? Es scheint, als würde dies je nach Prozessorarchitektur brechen. Ich habe das Gefühl, ich nehme verrückte Pillen und C sollte etwas tragbarer sein. Auch hier könnte unser Verständnis falsch sein.
Oberflächlich betrachtet scheint dies kein großes Problem zu sein, aber diese zuvor funktionierende Redewendung wird an Hunderten von Standorten verwendet, und wir sind gespannt darauf, dies zu verstehen, bevor wir mit teuren Änderungen fortfahren.
Hinweis: Hier gibt es eine scheinbar ähnliche, aber nicht exakte doppelte Frage: Die bitweise Operation auf char liefert ein 32-Bit-Ergebnis
Ich habe nicht gesehen, dass der eigentliche Kern des dort diskutierten Problems darin besteht, dass die Ergebnisgröße eines bitweisen Komplements anders ist als die, die an den Operator übergeben wird.