Welche Methoden können in C und C ++ die versehentliche Verwendung der Zuweisung (=) verhindern, wenn Äquivalenz (==) erforderlich ist?

15

In C und C ++ ist es sehr einfach, den folgenden Code mit einem schwerwiegenden Fehler zu schreiben.

char responseChar = getchar();
int confirmExit = 'y' == tolower(responseChar);
if (confirmExit = 1)
{
    exit(0);
}

Der Fehler ist, dass die if-Anweisung hätte lauten sollen:

if (confirmExit == 1)

Wie codiert, wird es jedes Mal beendet, da die Zuweisung der confirmExitVariablen erfolgt, und confirmExitwird dann als Ergebnis des Ausdrucks verwendet.

Gibt es gute Möglichkeiten, solche Fehler zu vermeiden?

DeveloperDon
quelle
39
Ja. Aktivieren Sie Compiler-Warnungen. Behandle Warnungen als Fehler und es ist kein Problem.
Martin York
9
Im gegebenen Beispiel ist die Lösung einfach. Sie ordnen sie einen Booleschen Wert, so dass es als boolean verwenden if (confirmExit).
Sichern Sie sich den
4
Das Problem ist, dass der Fehler von den "Designern" der C-Sprache begangen wurde, als sie = für den Zuweisungsoperator und == für den Gleichheitsvergleich verwendeten. ALGOL verwendete: =, weil sie speziell = für den Gleichstellungsvergleich verwenden wollten, und PASCAL und Ada folgten der ALGOL-Entscheidung. (Es ist erwähnenswert, dass Bell Labs ablehnte, als DoD einen C-basierten Einstieg in das DoD1-Bake-off anforderte, der schließlich zu Ada führte. "C war nicht jetzt und würde niemals robust genug für DoD sein, um geschäftskritisch zu sein Software. "Man wünscht, DoD und die Auftragnehmer hätten Bell Labs diesbezüglich zugehört.)
John R. Strohm
4
@John, die Auswahl der Symbole ist nicht das Problem, sondern die Tatsache, dass Zuweisungen auch ein Ausdruck sind, der den zugewiesenen Wert zurückgibt und entweder eine Bedingung a = boder a == beine Bedingung zulässt .
Karl Bielefeldt

Antworten:

60

Die beste Technik besteht darin, die Warnstufe Ihres Compilers zu erhöhen. Es warnt Sie dann vor einer möglichen Zuordnung in der if-Bedingung.

Stellen Sie sicher, dass Sie Ihren Code mit Null-Warnungen kompilieren (was Sie trotzdem tun sollten). Wenn Sie pedantisch sein möchten, stellen Sie Ihren Compiler so ein, dass Warnungen als Fehler behandelt werden.

Die Verwendung von Yoda-Bedingungen (die Konstante auf die linke Seite setzen) war eine andere Technik, die vor etwa einem Jahrzehnt populär war. Sie erschweren jedoch das Lesen des Codes (und behalten ihn daher aufgrund der unnatürlichen Art des Lesens bei (es sei denn, Sie sind Yoda)) und bieten keinen größeren Nutzen als die Erhöhung der Warnstufe (was auch den zusätzlichen Nutzen von mehr Warnungen hat).

Warnungen sind wirklich logische Fehler im Code und sollten korrigiert werden.

Martin York
quelle
2
Gehen Sie auf jeden Fall mit den Warnungen und nicht mit den Yoda-Bedingungen um - Sie können vergessen, die bedingte Konstante zuerst so einfach zu machen, wie Sie es vergessen können ==.
Michael Kohne
8
Ich habe eine angenehme Überraschung erhalten, dass die am häufigsten gestimmte Antwort auf diese Frage "Compiler-Warnungen in Fehler verwandeln" ist. Ich hatte das if (0 == ret)Grauen erwartet .
James
2
@ James: ... ganz zu schweigen davon, dass es nicht funktionieren wird a == b!!
Emilio Garavaglia
6
@EmilioGaravaglia: Es gibt eine einfache Lösung dafür: Schreiben Sie einfach 0==a && 0==b || 1==a && 1==b || 2==a && 2==b || ...(wiederholen Sie dies für alle möglichen Werte). Vergessen Sie nicht den obligatorischen ...|| 22==a && 22==b || 23==a && 24==b || 25==a && 25==b ||... Fehler, oder die Wartungsprogrammierer werden keinen Spaß haben.
user281377
10

Sie könnten immer etwas radikales tun wie das Testen Ihrer Software. Ich meine nicht einmal automatisierte Komponententests, nur die Tests, die jeder erfahrene Entwickler aus Gewohnheit durch zweimaliges Ausführen seines neuen Codes durchführt, einmaliges Bestätigen des Ausgangs und einmaliges Nicht-Ausführen. Das ist der Grund, warum die meisten Programmierer es für kein Problem halten.

Karl Bielefeldt
quelle
1
Einfach zu machen, wenn das Verhalten Ihres Programms vollständig deterministisch ist.
James
3
Dieses spezielle Problem kann schwer zu testen sein. Ich habe schon rc=MethodThatRarelyFails(); if(rc = SUCCESS){mehr als einmal Menschen gesehen, die gebissen haben , besonders wenn die Methode nur unter Bedingungen versagt, die schwer zu testen sind.
Gort the Robot
3
@StevenBurnap: Dafür sind Mock-Objekte gedacht. Das ordnungsgemäße Testen umfasst das Testen von Fehlermodi.
Jan Hudec
Das ist die richtige Antwort.
Leichtigkeit Rennen mit Monica
6

Ein traditioneller Weg, um die falsche Verwendung von Zuweisungen innerhalb eines Ausdrucks zu verhindern, besteht darin, die Konstante links und die Variable rechts anzuordnen.

if (confirmExit = 1)  // Unsafe

if (1=confirmExit)    // Safe and detected at compile time.

Der Compiler meldet einen Fehler für die unzulässige Zuweisung zu einer Konstanten ähnlich der folgenden.

.\confirmExit\main.cpp:15: error: C2106: '=' : left operand must be l-value

Die überarbeitete if-Bedingung wäre:

  if (1==confirmExit)    

Wie die folgenden Kommentare zeigen, wird dies von vielen als unangemessene Methode angesehen.

DeveloperDon
quelle
13
Es sollte beachtet werden, dass diese Vorgehensweise Sie ziemlich unbeliebt macht.
Riwalk
11
Bitte empfehlen Sie diese Praxis nicht.
James
5
Es funktioniert auch nicht, wenn Sie zwei Variablen miteinander vergleichen müssen.
Dan04
In einigen Sprachen ist es tatsächlich möglich, einem neuen Wert eine 1
zuzuweisen
4

Ich bin damit einverstanden, dass jeder "Compiler-Warnungen" sagt, aber ich möchte eine andere Technik hinzufügen: Code-Überprüfungen. Wenn Sie den gesamten Code überprüfen, der festgeschrieben wird, vorzugsweise bevor er festgeschrieben wird, ist es wahrscheinlich, dass diese Art von Dingen während der Überprüfung abgefangen wird.

MatrixFrog
quelle
Ich stimme dir nicht zu. Vor allem = statt == kann man leicht durchsehen, indem man den Code überprüft.
Simon
2

Erstens schadet es nie, die Warnstufe zu erhöhen.

Wenn Sie nicht möchten, dass Ihre Bedingung das Ergebnis einer Zuweisung innerhalb der if-Anweisung selbst if(1 == val)testet, haben Sie im Laufe der Jahre mit vielen C- und C ++ - Programmierern zusammengearbeitet und noch nie gehört, dass es eine schlechte Sache ist, die Konstante zuerst zu vergleichen könnte dieses Konstrukt versuchen.

Wenn Ihr Projektleiter dies billigt, machen Sie sich keine Sorgen darüber, was andere Leute denken. Der wahre Beweis ist, ob Sie oder jemand anderes Ihren Code in Monaten und Jahren verstehen können.

Wenn Sie jedoch beabsichtigen, das Ergebnis einer Zuweisung zu testen, hätte die Verwendung höherer Warnungen die Zuweisung zu einer Konstanten möglicherweise aufgefangen.

Oktopusgrabbus
quelle
Ich hebe eine skeptische Augenbraue bei jedem Nicht-Anfänger-Programmierer, der eine Yoda-Bedingung sieht und sie nicht sofort versteht. Es ist nur ein Flip, nicht schwer zu lesen und schon gar nicht so schlecht, wie manche Kommentare behaupten.
Underscore_d
@underscore_d Bei den meisten meiner Arbeitgeber wurden Aufträge unter bestimmten Bedingungen verpönt. Das Denken war, es war besser, die Anweisung von der Bedingung zu trennen. Der Grund, selbst auf Kosten einer anderen Codezeile klarer zu sein, war der nachhaltige technische Faktor. Ich habe an Stellen gearbeitet, an denen eine große Codebasis und ein großer Wartungsstau vorlagen. Ich verstehe voll und ganz, dass jemand einen Wert zuweisen und unter der Bedingung verzweigen möchte, die sich aus der Zuweisung ergibt. Ich sehe, dass es in Perl häufiger gemacht wird, und wenn die Absicht klar ist, folge ich dem Entwurfsmuster des Autors.
Octopusgrabbus
Ich habe nur über Yoda-Bedingungen nachgedacht (wie Ihre Demo hier), nicht mit Auftrag (wie im OP). Ersteres stört mich nicht, Letzteres mag ich auch nicht. Die einzige Form, die ich absichtlich verwende, ist if ( auto myPtr = dynamic_cast<some_ptr>(testPtr) ) {, dass es vermieden wird, den nullptrGültigkeitsbereich unbrauchbar zu machen, wenn die Besetzung fehlschlägt. Dies ist vermutlich der Grund, warum C ++ diese eingeschränkte Zuweisungsmöglichkeit innerhalb einer Bedingung hat. Im übrigen sollte eine Definition eine eigene Linie bekommen, würde ich sagen - viel einfacher auf einen Blick zu sehen und weniger anfällig für verschiedene Irrtümer.
Underscore_d
@underscore_d Bearbeitete Antwort basierend auf Ihren Kommentaren. Guter Punkt.
Octopusgrabbus
1

Spät zur Party wie immer, aber hier ist die statische Code-Analyse der Schlüssel

Die meisten IDEs bieten jetzt SCA über die syntaktische Prüfung des Compilers hinaus an. Es stehen auch andere Tools zur Verfügung, einschließlich derer, die die MISRA- (*) und / oder CERT-C-Richtlinien implementieren.

Erklärung: Ich bin Teil der MISRA C-Arbeitsgruppe, poste aber in persönlicher Eigenschaft. Ich bin auch unabhängig von Werkzeugherstellern

Andrew
quelle
-2

Verwenden Sie einfach die Zuweisung für die linke Hand. Compiler-Warnungen können hilfreich sein, aber Sie müssen sicherstellen, dass Sie die richtige Stufe erreichen. Andernfalls werden Sie entweder mit sinnlosen Warnungen überflutet oder erhalten keine Anweisungen, die Sie sehen möchten.

Umgekehrtes Lama
quelle
4
Sie wären überrascht, wie wenig sinnlose Warnungen moderne Compiler generieren. Sie denken vielleicht nicht, dass eine Warnung wichtig ist, aber in den meisten Fällen liegen Sie einfach falsch.
Kristof Provost
Lesen Sie das Buch "Schreiben von Solid Code" unter amazon.com/Writing-Solid-Microsoft-Programming-Series/dp/… . Es beginnt mit einer großartigen Diskussion über Compiler und den möglichen Nutzen von Warnmeldungen und einer noch umfassenderen statischen Analyse.
DeveloperDon
@KristofProvost Ich habe es geschafft, Visual Studio dazu zu bringen, mir 10 Kopien der exakt gleichen Warnung für dieselbe Codezeile zu geben. Als dieses Problem behoben wurde, ergaben sich 10 identische Warnungen für dieselbe Codezeile, die besagten, dass ursprüngliche Methode war besser.
Umgekehrtes Lama