Jakie metody w C i C ++ mogą zapobiec przypadkowemu użyciu przypisania (=), gdy potrzebna jest równoważność (==)?

15

W językach C i C ++ bardzo łatwo jest napisać następujący kod z poważnym błędem.

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

Błąd polega na tym, że instrukcja if powinna była:

if (confirmExit == 1)

Po zakodowaniu wyjdzie za każdym razem, ponieważ confirmExitnastąpi przypisanie zmiennej, a następnie confirmExitzostanie użyte jako wynik wyrażenia.

Czy istnieją dobre sposoby, aby zapobiec tego rodzaju błędom?

DeveloperDon
źródło
39
Tak. Włącz ostrzeżenia kompilatora. Traktuj ostrzeżenia jako błędy i nie stanowi to problemu.
Martin York,
9
W podanym przykładzie rozwiązanie jest łatwe. Przypisać mu wartość logiczną, a więc używać go jako logiczną: if (confirmExit).
Zabezpiecz
4
Problem polega na tym, że błąd został popełniony przez „projektantów” języka C, którzy wybrali użycie = dla operatora przypisania i == do porównania równości. Użyto ALGOL: =, ponieważ specjalnie chcieli użyć = do porównania równości, a PASCAL i Ada zastosowali się do decyzji ALGOL. (Warto zauważyć, że kiedy DoD poprosił o wejście C do DoD1, co ostatecznie przyniosło Adę, Bell Labs odmówił, mówiąc, że „C nie był teraz i nigdy nie będzie wystarczająco solidny, aby mieć krytyczne znaczenie dla DoD oprogramowania. ”Chciałoby się, aby DoD i wykonawcy słuchali w tej sprawie Bell Labs.)
John R. Strohm
4
@John, wybór symboli nie jest problemem, to fakt, że przypisania są również wyrażeniem, które zwraca przypisaną wartość, pozwalając na warunek a = blub a == bwewnątrz.
Karl Bielefeldt,

Odpowiedzi:

60

Najlepszą techniką jest zwiększenie poziomu ostrzegania twojego kompilatora. Ostrzeże Cię wtedy o potencjalnym przypisaniu w warunkowym warunkowym.

Upewnij się, że skompilowałeś swój kod z zerowymi ostrzeżeniami (co i tak powinieneś robić). Jeśli chcesz być pedantyczny, ustaw kompilator tak, aby traktował ostrzeżenia jako błędy.

Używanie warunkowych Yoda (umieszczanie stałej po lewej stronie) było kolejną techniką, która była popularna około dziesięć lat temu. Ale sprawiają, że kod jest trudniejszy do odczytania (a tym samym zachowuje się z powodu nienaturalnego sposobu, w jaki czytają (chyba, że ​​jesteś Yodą)) i nie zapewniają większych korzyści niż zwiększenie poziomu ostrzeżenia (co ma również dodatkowe zalety w postaci większej liczby ostrzeżeń).

Ostrzeżenia są naprawdę logicznymi błędami w kodzie i powinny zostać poprawione.

Martin York
źródło
2
Zdecydowanie idź z ostrzeżeniami, a nie z warunkami warunkowymi Yody - możesz najpierw zapomnieć o zrobieniu stałej warunkowej, tak jak możesz zapomnieć o zrobieniu ==.
Michael Kohne,
8
Mam miłą niespodziankę, że najczęściej głosowaną odpowiedzią na to pytanie jest „zamień ostrzeżenia kompilatora w błędy”. Spodziewałem się if (0 == ret)horroru.
James
2
@James: ... nie wspominając, że to nie zadziała a == b!!
Emilio Garavaglia
6
@EmilioGaravaglia: Istnieje łatwe obejście tego problemu: po prostu napisz 0==a && 0==b || 1==a && 1==b || 2==a && 2==b || ...(powtórz dla wszystkich możliwych wartości). Nie zapomnij o obowiązkowym ...|| 22==a && 22==b || 23==a && 24==b || 25==a && 25==b ||... błędzie, bo programiści nie będą się dobrze bawić.
user281377,
10

Zawsze możesz zrobić coś radykalnego, np. Przetestować oprogramowanie. Nie mam nawet na myśli automatycznych testów jednostkowych, tylko testy, które każdy doświadczony programista przyzwyczaja, uruchamiając swój nowy kod dwa razy, raz potwierdzając wyjście, a raz nie. Dlatego większość programistów uważa to za problem.

Karl Bielefeldt
źródło
1
Łatwe do zrobienia, gdy zachowanie twojego programu jest całkowicie deterministyczne.
James
3
Ten konkretny problem może być trudny do przetestowania. Widziałem ludzi ugryzionych rc=MethodThatRarelyFails(); if(rc = SUCCESS){więcej niż jeden raz, szczególnie jeśli metoda zawiedzie tylko w trudnych warunkach.
Gort the Robot
3
@StevenBurnap: Po to są fałszywe obiekty. Właściwe testowanie obejmuje testowanie trybów awarii.
Jan Hudec
To jest poprawna odpowiedź.
Lekkość ściga się z Monicą
6

Tradycyjnym sposobem zapobiegania niepoprawnemu użyciu przypisań w wyrażeniu jest umieszczenie stałej po lewej stronie i zmiennej po prawej stronie.

if (confirmExit = 1)  // Unsafe

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

Kompilator zgłosi błąd dotyczący nielegalnego przypisania do stałej podobnej do poniższej.

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

Zmieniony warunek byłby:

  if (1==confirmExit)    

Jak pokazują poniższe komentarze, wielu uważa to za nieodpowiednią metodę.

DeveloperDon
źródło
13
Należy zauważyć, że robienie rzeczy w ten sposób sprawi, że będziesz dość niepopularny.
riwalk
11
Proszę nie polecać tej praktyki.
James
5
To również nie działa, jeśli musisz porównać ze sobą dwie zmienne.
dan04
Niektóre języki faktycznie pozwalają przypisać 1 do nowej wartości (tak, wiem, że Q to walka c / c ++)
Matsemann
4

Zgadzam się ze wszystkimi, którzy mówią „ostrzeżenia kompilatora”, ale chcę dodać inną technikę: recenzje kodu. Jeśli masz zasady sprawdzania całego kodu, który zostanie zatwierdzony, najlepiej przed jego zatwierdzeniem, prawdopodobnie podczas przechwytywania tego rodzaju problem zostanie przechwycony.

MatrixFrog
źródło
Nie zgadzam się. Zwłaszcza = zamiast == można łatwo prześledzić, przeglądając kod.
Simon
2

Po pierwsze, podnoszenie poziomów ostrzegawczych nigdy nie boli.

Jeśli nie chcesz, aby warunek testował wynik przypisania w samej instrukcji if, to pracując z wieloma programistami C i C ++ przez lata i nigdy nie słysząc, że porównywanie stałej jako pierwszej if(1 == val)było złą rzeczą, mógł wypróbować ten konstrukt.

Jeśli lider projektu wyrazi na to zgodę, nie martw się o to, co myślą inni ludzie. Prawdziwym dowodem jest to, czy ty lub ktoś inny możesz zrozumieć swój kod za miesiące i lata.

Jeśli jednak chciałbyś przetestować wynik przypisania, użycie wyższych ostrzeżeń mogłoby [prawdopodobnie] uchwycić przypisanie do stałej.

octopusgrabbus
źródło
Podnoszę sceptycznie brew na każdego początkującego programistę, który widzi warunek Yody i nie od razu go rozumie. To tylko klapka, nie jest trudna do odczytania, a na pewno nie tak zła, jak twierdzą niektóre komentarze.
underscore_d
@underscore_d U większości moich pracodawców braki w zadaniach warunkowych były niezadowolone. Myślenie było takie, że lepiej oddzielić zadanie od warunkowego. Powodem bycia jaśniejszym, nawet kosztem innej linii kodu, był utrzymujący się czynnik inżynierski. Pracowałem w miejscach, które miały duże bazy kodu i wiele zaległości w konserwacji. W pełni rozumiem, że ktoś może chcieć przypisać wartość i rozgałęzić się pod warunkiem wynikającym z przypisania. Widzę, że robi się to częściej w Perlu i, jeśli intencja jest jasna, podążę za wzorcem projektowym autora.
octopusgrabbus
Myślałem o samych warunkach Yody (jak twoje demo tutaj), a nie z przypisaniem (jak w OP). Nie mam nic przeciwko temu pierwszemu, ale też nie lubię tego drugiego. Jedyną formą, której celowo używam, jest if ( auto myPtr = dynamic_cast<some_ptr>(testPtr) ) {to, że unika się utrzymywania bezużytecznego nullptrzakresu, jeśli rzutowanie się nie powiedzie - co jest prawdopodobnie przyczyną, dla której C ++ ma ograniczoną możliwość przypisywania w ramach warunku. Co do reszty, tak, definicja powinna mieć swoją własną linię, powiedziałbym - znacznie łatwiej zobaczyć na pierwszy rzut oka i mniej podatna na różne wpadki.
underscore_d
@underscore_d Edytowana odpowiedź na podstawie twoich komentarzy. Słuszna uwaga.
octopusgrabbus
1

Spóźniamy się na imprezę jak zawsze, ale kluczem jest tu analiza kodu statycznego

Większość IDE zapewnia teraz SCA poza kontrolą składniową kompilatora, a inne narzędzia są dostępne, w tym te, które implementują wytyczne MISRA (*) i / lub CERT-C.

Deklaracja: Należę do grupy roboczej MISRA C, ale zamieszczam się osobiście. Jestem także niezależny od jakiegokolwiek dostawcy narzędzi

Andrzej
źródło
-2

Po prostu użyj przypisania lewej ręki, ostrzeżenia kompilatora mogą pomóc, ale musisz upewnić się, że osiągniesz odpowiedni poziom, w przeciwnym razie albo zostaniesz zalany bezsensownymi ostrzeżeniami, albo nie powiesz tym, których chcesz zobaczyć.

Odwrócona lama
źródło
4
Zdziwiłbyś się, jak mało bezsensownych ostrzeżeń generują nowoczesne kompilatory. Może nie uważasz, że ostrzeżenie jest ważne, ale w większości przypadków po prostu się mylisz.
Kristof Provost
Sprawdź książkę „Pisanie kodu stałego” amazon.com/Writing-Solid-Microsoft-Programming-Series/dp/… . Zaczyna się od świetnej dyskusji o kompilatorach i korzyściach, jakie możemy uzyskać z komunikatów ostrzegawczych i jeszcze bardziej szczegółowej analizy statycznej.
DeveloperDon
@KristofProvost Udało mi się zdobyć studio wizualne, które daje mi 10 kopii dokładnie tego samego ostrzeżenia dla tego samego wiersza kodu, a kiedy ten problem został „naprawiony”, pojawiło się 10 identycznych ostrzeżeń o tym samym wierszu kodu, mówiąc, że oryginalna metoda była lepsza.
Odwrócona lama,