Ich habe den folgenden Code in diesem Quora-Beitrag gesehen :
#include <stdio.h>
struct mystruct { int enabled:1; };
int main()
{
struct mystruct s;
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}
In C & C++ ist die Ausgabe des Codes unerwartet,
Ist behindert !!
Obwohl die Erklärung zum "Vorzeichenbit" in diesem Beitrag gegeben wird, kann ich nicht verstehen, wie es möglich ist, dass wir etwas einstellen und es dann nicht so widerspiegelt, wie es ist.
Kann jemand eine ausführlichere Erklärung geben?
Hinweis : Beide Tags c & c ++ sind erforderlich, da sich ihre Standards zur Beschreibung der Bitfelder geringfügig unterscheiden. Siehe Antworten für C-Spezifikation und C++ - Spezifikation .
Gemäß C++ - Standard n471 wird ein sehr ähnliches Code-Snippet bereitgestellt. Der verwendete Typ ist BOOL
(benutzerdefiniert), er kann jedoch auf jeden Typ angewendet werden.
12.2.4
4 Wenn der Wert true oder false in einem Bitfeld vom Typ
bool
beliebiger Größe (einschließlich eines Ein-Bit-Bitfelds) gespeichert wird, werden der ursprüngliche Wertbool
und der Wert des Bitfelds Feld soll gleich vergleichen. Wenn der Wert eines Enumerators in einem Bitfeld desselben Aufzählungstyps gespeichert ist und die Anzahl der Bits im Bitfeld groß genug ist, um alle Werte dieses Aufzählungstyps aufzunehmen ( 10.2), der ursprüngliche Enumeratorwert und der Wert des Bitfeldes sollen gleich sein. [Beispiel:enum BOOL { FALSE=0, TRUE=1 }; struct A { BOOL b:1; }; A a; void f() { a.b = TRUE; if (a.b == TRUE) // yields true { /* ... */ } }
- Ende Beispiel]
Auf den ersten Blick ist der fett gedruckte Teil offen für Interpretationen. Die richtige Absicht wird jedoch deutlich, wenn enum BOOL
leitet sich aus dem int
ab.
enum BOOL : int { FALSE=0, TRUE=1 }; // ***this line
struct mystruct { BOOL enabled:1; };
int main()
{
struct mystruct s;
s.enabled = TRUE;
if(s.enabled == TRUE)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}
Mit obigem Code gibt es eine Warnung ohne -Wall -pedantic
:
warnung: "mystruct :: enabled" ist zu klein, um alle Werte von "enum BOOL" aufzunehmen.
struct mystruct { BOOL enabled:1; };
Die Ausgabe ist:
Ist behindert !! (beim Benutzen
enum BOOL : int
)
Wenn enum BOOL : int
wird einfach gemacht enum BOOL
, dann ist die Ausgabe wie oben angegeben:
Ist aktiviert (bei Verwendung von
enum BOOL
)
Daher kann, wie auch nur wenige andere Antworten, der Schluss gezogen werden, dass der Typ int
nicht groß genug ist, um den Wert "1" in nur einem Bitbitfeld zu speichern.
Bitfelder sind durch den Standard unglaublich schlecht definiert. Angesichts dieses Codes struct mystruct {int enabled:1;};
, dann wissen wir nicht:
int:n
Bitfeld ist als signiert oder nicht signiert anzusehen.Zum letzten Teil in C17 6.7.2.1/10 heißt es:
Ein Bitfeld wird so interpretiert, dass es einen vorzeichenbehafteten oder vorzeichenlosen Integer-Typ hat, der aus der angegebenen Anzahl von Bits besteht 125)
Nicht normative Anmerkung zur Erläuterung des Vorstehenden:
125) Wie in 6.7.2 oben angegeben, ist es implementierungsdefiniert, ob das Bitfeld mit oder ohne Vorzeichen versehen ist, wenn der tatsächlich verwendete Typspezifizierer
int
oder ein alsint
definierter Typendefinitionsname ist .
Falls das Bitfeld als signed int
und du machst ein bisschen Größe 1
, dann ist kein Platz für Daten, nur für das Vorzeichenbit. Dies ist der Grund, warum Ihr Programm auf einigen Compilern möglicherweise seltsame Ergebnisse liefert.
Gute Übung:
int
mit Vorzeichen für jede Form der Bitmanipulation.Ich kann nicht verstehen, wie es möglich ist, dass wir etwas einstellen und es dann nicht so angezeigt wird, wie es ist.
Fragen Sie sich, warum es kompiliert oder Ihnen einen Fehler liefert?
Ja, es sollte im Idealfall einen Fehler geben. Dies ist auch der Fall, wenn Sie die Warnungen Ihres Compilers verwenden. In GCC mit -Werror -Wall -pedantic
:
main.cpp: In function 'int main()':
main.cpp:7:15: error: overflow in conversion from 'int' to 'signed char:1'
changes value from '1' to '-1' [-Werror=overflow]
s.enabled = 1;
^
Die Begründung, warum dies der Definition der Implementierung überlassen bleibt, im Vergleich zu einem Fehler, hat möglicherweise mehr mit historischen Verwendungen zu tun, bei denen das Erfordernis einer Umwandlung das Brechen alten Codes bedeuten würde. Die Autoren der Norm glauben möglicherweise, dass Warnungen ausreichten, um die Lücke für die Betroffenen zu schließen.
Um ein wenig Vorschrift einzubringen, werde ich die Aussage von @ Lundin wiederholen: "Verwenden Sie niemals Bitfelder für irgendeinen Zweck." Wenn Sie gute Gründe haben, auf ein niedriges Niveau zu kommen und Ihr Gedächtnis genau zu beschreiben Layout-Details, die Sie zu der Annahme bringen würden, dass Sie zunächst Bitfelder benötigen, und die anderen damit verbundenen Anforderungen, die Sie mit ziemlicher Sicherheit haben, werden auf ihre Unterspezifikation stoßen.
(TL; DR - Wenn Sie anspruchsvoll genug sind, um legitimerweise Bitfelder "zu benötigen", sind sie nicht gut genug definiert, um Ihnen zu dienen.)
Dies ist ein durch die Implementierung definiertes Verhalten. Ich gehe davon aus, dass die Computer, auf denen Sie dies ausführen, zwei vorzeichenbehaftete Ganzzahlen verwenden, und behandle int
in diesem Fall als vorzeichenbehaftete Ganzzahl, um zu erklären, warum Sie keinen if-Teil der if-Anweisung eingeben, wenn true.
struct mystruct { int enabled:1; };
deklariert enable
als 1-Bit-Bitfeld. Da es signiert ist, lauten die gültigen Werte -1
Und 0
. Das Setzen des Feldes auf 1
Überläuft das Bit, das auf -1
Zurückgeht (dies ist undefiniertes Verhalten).
Im Wesentlichen ist bei einem vorzeichenbehafteten Bitfeld der Maximalwert 2^(bits - 1) - 1
, was in diesem Fall 0
Ist.
Sie können sich das so vorstellen, dass im Zweierkomplementsystem das Bit ganz links das Vorzeichenbit ist. Jede vorzeichenbehaftete Ganzzahl mit dem am weitesten links stehenden Bit ist daher ein negativer Wert.
Wenn Sie eine 1-Bit-Ganzzahl mit Vorzeichen haben, enthält diese nur das Vorzeichenbit. Wenn Sie diesem einzelnen Bit also 1
Zuweisen, kann nur das Vorzeichenbit gesetzt werden. Beim Zurücklesen wird der Wert also als negativ interpretiert und ist somit -1.
Die Werte, die eine 1-Bit-Ganzzahl mit Vorzeichen enthalten kann, sind -2^(n-1)= -2^(1-1)= -2^0= -1
und 2^n-1= 2^1-1=0
Es ist nichts Falsches an Ihrem Verständnis von Bitfeldern, das ich sehen kann. Ich sehe, dass Sie mystruct zuerst als struct mystruct {int enabled: 1;} und dann als struct mystruct s; neu definiert haben. Was Sie hätten codieren sollen, war:
#include <stdio.h>
struct mystruct { int enabled:1; };
int main()
{
mystruct s; <-- Get rid of "struct" type declaration
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabled\n"); // --> we think this to be printed
else
printf("Is disabled !!\n");
}