Widziałem to pytanie w teście, w którym musimy podać dane wyjściowe następującego kodu.
#include<stdio.h>
int main(){
int k = 0;
while(+(+k--)!=0)
k=k++;
printf("%d\n", k);
return 0;
}
Dane wyjściowe to -1
. Nie jestem jednak pewien, dlaczego to jest odpowiedź.
Co to wyrażenie +(+k--)
oznacza w C?
k=k++
jest niezdefiniowany, ale nie jest nieokreślony, ponieważ nigdy nie jest wykonywany z powodu zaciemnionego stanu - głosuję za jego zamknięciem jako duplikat tego pytania .Odpowiedzi:
[Dla przypomnienia, edytowałem tę odpowiedź dość znacząco, odkąd została zaakceptowana i poddana pod głosowanie. Jednak nadal mówi w zasadzie te same rzeczy.]
Ten kod jest głęboko, być może celowo, zagmatwany. Zawiera wąsko unikniętą instancję przerażającego niezdefiniowanego zachowania . Zasadniczo nie można ustalić, czy osoba, która skonstruowała to pytanie, była bardzo, bardzo sprytna, czy bardzo, bardzo głupia. A „lekcja”, jaką ten kod może uczyć lub wypytywać o ciebie - a mianowicie, że operator jednoargumentowy plus niewiele robi - z pewnością nie jest wystarczająco ważna, aby zasłużyć na tego rodzaju wywrotowe złe kierowanie.
Istnieją dwa mylące aspekty kodu, dziwny warunek:
oraz oświadczenie, które kontroluje:
Najpierw omówię drugą część.
Jeśli masz taką zmienną
k
, którą chcesz zwiększyć o 1, C daje ci nie jeden, nie dwa, nie trzy, ale cztery różne sposoby:k = k + 1
k += 1
++k
k++
Mimo tej nagrody (a może właśnie z tego powodu) niektórzy programiści są zdezorientowani i wykrztuszają skręty
Jeśli nie możesz zrozumieć, co to ma zrobić, nie martw się: nikt nie może. To wyrażenie zawiera dwie różne próby zmiany
k
wartości (k =
część ik++
część), a ponieważ w C nie ma reguły określającej, która z prób modyfikacji „wygrywa”, wyrażenie takie jest formalnie niezdefiniowane , co oznacza nie tylko, że to nie zdefiniowane znaczenie, ale że cały program zawierający jest podejrzany.Teraz, jeśli spojrzysz bardzo uważnie, zobaczysz, że w tym konkretnym programie linia
k = k++
nie jest tak naprawdę wykonywana, ponieważ (jak zobaczymy) warunek kontrolny jest początkowo fałszywy, więc pętla działa 0 razy . Więc ten konkretny program nie może faktycznie być zdefiniowana - ale to wciąż patologicznie mylące.Zobacz także te kanoniczne SO odpowiedzi na wszystkie pytania dotyczące tego rodzaju niezdefiniowanego zachowania.
Ale nie pytałeś o tę
k=k++
część. Zapytałeś o pierwszą mylącą część,+(+k--)!=0
warunek. To wygląda dziwnie, ponieważ jest dziwne. Nikt nigdy nie napisałby takiego kodu w prawdziwym programie. Nie ma więc powodu, aby nauczyć się go rozumieć. (Tak, to prawda, odkrywanie granic systemu może pomóc ci dowiedzieć się o jego drobnych punktach, ale w mojej książce jest dość wyraźna granica między pomysłowymi, prowokującymi do eksploracji eksploracjami a bzdurnymi, obraźliwymi eksploracjami, a to wyrażenie jest bardzo wyraźnie widoczne zła strona tej linii).W każdym razie przyjrzyjmy się
+(+k--)!=0
. (A po zrobieniu tego, zapomnijmy o tym.) Każde takie wyrażenie musi być rozumiane od wewnątrz. Zakładam, że wiesz corobi. Pobiera
k
bieżącą wartość i „zwraca” ją do reszty wyrażenia i mniej więcej jednocześnie zmniejszak
, tzn. Zapisujek-1
ponownie wartośćk
.Ale co to
+
robi? Jest to jednoskładnikowa, Plus, Plus nie binarny. To jest jak jednoargumentowy minus. Wiesz, że binarny minus robi odejmowanie: wyrażenieodejmuje b od a. I wiesz, że jednoargumentowy minus neguje rzeczy: wyrażenie
daje ci minus od. To, co
+
robi jedno , jest ... w zasadzie niczym.+a
dajea
wartość po zmianie dodatnich wartości na dodatnie i ujemnych na ujemne. Więc wyrażeniedaje ci wszystko, co ci
k--
dało, czylik
starą wartość.Ale nie skończyliśmy, bo mamy
To po prostu bierze wszystko, co
+k--
ci dano, i+
znów odnosi się do tego jedno raz. Więc daje ci to, co ci+k--
dało, co byłok--
ci, co dawało, co byłok
dawną wartością.Tak więc w końcu warunek
robi dokładnie to samo, co znacznie bardziej zwyczajny warunek
zrobiłby. (Robi to samo, co
while(+(+(+(+k--)))!=0)
zrobiłby to nawet bardziej skomplikowany wygląd . I te nawiasy nie są tak naprawdę konieczne; robi to samo, cowhile(+ + + +k--!=0)
by zrobił).Nawet zastanawianie się, jaki jest „normalny” stan
jest dość trudne. W tej pętli dzieją się dwie rzeczy: Ponieważ pętla działa potencjalnie wiele razy, zamierzamy:
k--
rób dalej , aby robićk
coraz mniejsze, ale takżeAle wykonujemy tę
k--
część od razu, zanim (lub w trakcie) decydujemy, czy podjąć kolejną podróż przez pętlę. I pamiętaj, żek--
„zwraca” starą wartośćk
przed jej zmniejszeniem. W tym programie początkowa wartośćk
wynosi 0. Więck--
„zwróci” starą wartość 0, a następnie zaktualizujek
do -1. Ale reszta warunku jest!= 0
- ale jak właśnie widzieliśmy, przy pierwszym testowaniu warunku otrzymaliśmy 0. Więc nie będziemy robić żadnych pętli przez pętlę, więc nie będziemy próbować wykonać problematyczne stwierdzeniek=k++
w ogóle.Innymi słowy, w tej konkretnej pętli, chociaż powiedziałem, że „dzieje się coś w rodzaju dwóch rzeczy”, okazuje się, że rzecz 1 dzieje się raz, ale rzecz 2 dzieje się zero razy.
W każdym razie mam nadzieję, że teraz jest wystarczająco jasne, dlaczego ta marna wymówka dla programu kończy się na wydrukowaniu -1 jako wartości końcowej
k
. Zwykle nie lubię odpowiadać na pytania w quizie - czuję się jak oszukiwanie - ale w tym przypadku, ponieważ tak głośno nie zgadzam się z całym celem ćwiczenia, nie mam nic przeciwko.źródło
Na pierwszy rzut oka wygląda na to, że ten kod wywołuje niezdefiniowane zachowanie, jednak tak nie jest.
Najpierw sformatujmy kod poprawnie:
Teraz widzimy, że instrukcja
k=k++;
znajduje się w pętli.Teraz prześledźmy program:
Gdy warunek pętli jest oceniany po raz pierwszy,
k
ma wartość 0. Wyrażeniek--
ma bieżącą wartośćk
, która wynosi 0, ik
jest zmniejszane jako efekt uboczny. Więc po tej instrukcji wartośćk
wynosi -1.Początek
+
tego wyrażenia nie ma wpływu na wartość, dlatego jest+k--
oceniany na 0 i podobnie+(+k--)
ocenia się na 0.Następnie
!=
operator jest oceniany. Ponieważ0!=0
jest to fałsz, treść pętli nie jest wprowadzana . Gdyby ciało zostało wprowadzone, wywołałbyś niezdefiniowane zachowanie, ponieważk=k++
zarówno czyta, jak i piszek
bez punktu sekwencyjnego. Ale pętla nie została wprowadzona, więc nie ma UB.Na koniec
k
wypisywana jest wartość, która wynosi -1.źródło
if (x != NULL) *x = 42;
Czy to nie jest określone, kiedyx == NULL
? Oczywiście nie. Niezdefiniowane zachowanie nie występuje w częściach kodu, które nie są wykonywane. Słowo zachowanie jest wskazówką. Kod, który nie jest wykonywany, nie ma zachowania, jest niezdefiniowany lub w inny sposób.k=k++
jakościowo różni się od*x=42
. Ten drugi jest dobrze zdefiniowany, jeślix
jest poprawnym wskaźnikiem, ale ten pierwszy jest nieokreślony bez względu na wszystko. (Przyznaję, że masz rację, ale znowu nie zamierzam się z tym kłócić i jestem coraz bardziej zaniepokojony tym, że zostaliśmy po mistrzowsku trollowani).Oto wersja tego, która pokazuje pierwszeństwo operatora:
Dwaj jednoargumentowi
+
nic nie robią, więc to wyrażenie jest dokładnie równoważne zk--
. Osoba, która to napisała, najprawdopodobniej próbowała zepsuć ci umysł.źródło