Ich habe bemerkt, dass in C meine boolesche Variable irgendwie auf eine Weise verändert wird, die ich nicht verstehe.
%Vor% Wenn ich einen Wert von 1
für x
und einen beliebigen Wert für y
( 1
in diesem Beispiel) eingib:
%Vor%
Am Ende gibt y
den korrekten Originalwert aus, aber x
ändert sich magisch in 0
dazwischen!
Dies ist kein Problem, wenn die Eingabe für x
0
ist, da die Ausgaben für sowohl x
als auch y
wie erwartet ihre jeweiligen ursprünglichen Werte sind.
Bitte erläutern Sie, was vor sich geht!
Sie übergeben die Adresse der booleschen Variablen an scanf()
, die eine Variable vom Typ int*
erwartet. Dies führt zu undefiniertem Verhalten und Sie können falsche Ergebnisse erhalten oder sogar abstürzen.
Um dieses Problem zu lösen, verwenden Sie das temporäre int
, um das Scannen eines booleschen Werts (als int) zu speichern und speichern Sie es anschließend als boolesche Variable.
Demo
%Vor%Auf der anderen Seite ist das Drucken der Boulan-Variablen eine andere Geschichte, wobei der boolesche Wert ohne Probleme zu int indexiert wird und korrekt gedruckt wird.
A bool
ist kein int
. Das Lesen mit dem Formatbezeichner %d
für int
ist ein nicht definiertes Verhalten.
Per 7.21.6.2 Die Funktion fscanf
, Absatz 13 des C-Standards :
Wenn eine Konvertierungsspezifikation ungültig ist, ist das Verhalten nicht definiert.
Beachten Sie, dass Absatz 9 von 7.21.6.1 Die Funktion fprintf
angibt:
Wenn eine Konvertierungsspezifikation ungültig ist, lautet das Verhalten nicht definiert. Wenn ein Argument nicht der richtige Typ für die entsprechende Umsetzungsspezifikation ist das Verhalten undefiniert.
Aber das ist für fprintf()
, nicht fscanf()
. Formatbezeichner sind für die scanf()
-Funktionen viel strenger, da es keine Argument-Promotion gibt
lässt ein Format wie %d
für ein char
oder bool
"arbeiten", das für einen int
-Aufruf in printf()
hochgestuft wird. Die scanf()
-Funktionen werden an die -Adresse des Arguments übergeben, und wenn die Adresse auf die falsche Größe von dem verweist, was im Formatspezifizierer erwartet wird, führt dies zu undefiniertem Verhalten - wie zum Beispiel unerklärte Änderungen an andere Variable.
OK, zwei Punkte hier.
bool
ist implementierungsdefiniert. bool
im Standard ist kein Formatbezeichner definiert. Während also Scannen der Wert ist, übergeben Sie die Adresse von bool
als Argument für %d
ist schlecht siehe Hinweis , da der angegebene Typ nicht der ist Gleich wie erwarteter Typ.
Sie können eine Zwischenzahl verwenden, den Wert einscannen und (nach der Validierung oder Umwandlung in true
und false
MACROs) das Ergebnis zurück an bool
type Variable zuweisen.
Bei Drucken kann jedoch ein bool
aufgrund der Standardargument-Promotion ein Kandidat für das Argument für %d
ohne ein Problem sein.
Hinweis:
%d
mit *scanf()
erwartet, dass das Argument ein int *
ist, stattdessen liefert ein bool*
ein undefiniertes Verhalten .
Bezugnehmend auf Kapitel §7.21.6.2, Absatz 10
[....] Sofern die Zuordnungsunterdrückung nicht durch
*
angezeigt wurde, wurde die Das Ergebnis der Konvertierung wird in das Objekt platziert, auf das das erste Argument folgt das Argumentformat
, das noch kein Konvertierungsergebnis erhalten hat. Wenn dieses Objekt hat keinen geeigneten Typ, oder wenn das Ergebnis der Konvertierung nicht dargestellt werden kann Im Objekt ist das Verhalten nicht definiert.
bool
ist ein anderer Datentyp als int
und wahrscheinlich ein einzelnes Byte.
scanf
ist keine typsichere Funktion, Sie sagen es mit dem %d
-Konvertierungsspezifizierer, der Zeiger auf ein int erwartet, und scanf
hat keine Möglichkeit zu wissen, was Sie haben übergeben pointer to bool
anstelle von Zeiger auf ein int . Dann erhalten Sie undefiniertes Verhalten .
clang Compiler generiert Warnung:
%Vor%Alle anderen Antworten sind technisch korrekt, helfen dir aber nicht wirklich.
Ihr Hauptproblem ist nicht x oder y, aber die Tatsache, dass Sie nicht in der Lage sind, selbst die einfachsten Programmierprobleme zu debuggen. Dies ist leicht zu beheben - aktivieren Sie Compiler-Warnungen (wenn Sie gcc
verwenden, fügen Sie die Option -Wall
hinzu). Der Compiler wird Sie über einen Fehler wie diesen und viele weitere informieren. Sie müssen nicht jedes Mal zu stackoverflow kommen:)
Betrachten Sie das folgende Programm
%Vor%Die Ausgabe:
%Vor% Nun, zwischen z
und s
gibt es eine 4 Byte Differenz, die in meinem Fall die Größe von int
ist.
Zwischen x
und y
gibt es nur 1 Byte Unterschied.
Jetzt mit dieser Zeile:
%Vor% Sie sagen mit dem %d
to scanf
, dass Sie eine 4 Byte lange Eingabe benötigen, eine int
.
Dieser int
wird gespeichert im Falle von y
von der Adresse 0028FF36
und mit einer Eingabe von 0x0001
wird das zweite Byte, das ein 0
ist, bei 0028FF37
gespeichert. Und das ist die Adresse von x
.
Sie können es mit einem anderen Eingabewert überprüfen. Wenn Sie zum Beispiel 65535
an y
geben, was 0xFFFF
ist, dann wird x
0xF
sein, was 255
dezimal ist.