it-swarm.com.de

Warum gibt die Zuweisung eines Werts zu einem Bitfeld nicht denselben Wert zurück?

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 .

95
iammilind

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 Wert bool 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.

8
iammilind

Bitfelder sind durch den Standard unglaublich schlecht definiert. Angesichts dieses Codes struct mystruct {int enabled:1;};, dann wissen wir nicht:

  • Wie viel Platz dies einnimmt - wenn es Füllbits/Bytes gibt und wo sie sich im Speicher befinden.
  • Wo sich das Bit im Speicher befindet. Nicht definiert und hängt auch von der Endianess ab.
  • Ob ein 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 als int 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:

  • Verwenden Sie niemals Bitfelder für irgendeinen Zweck.
  • Vermeiden Sie die Verwendung des Typs int mit Vorzeichen für jede Form der Bitmanipulation.
76
Lundin

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.)

57
HostileFork

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.

22
NathanOliver

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

10
Paul Ogilvie

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");
}
0
ar18