Co sprawia, że ​​programiści C są tak ciekawi, jeśli „i ++ == ++ i”? [Zamknięte]


15

Przypadkowa obserwacja wydaje się, że na StackOverflow.com pojawiają się pytania, czy „++ i == i ++”. To pytanie jest ciągle zadawane, myślę, że widziałem je 6 lub 7 razy w ciągu ostatnich 2 miesięcy.

Zastanawiam się tylko, dlaczego programiści C są tak zainteresowani? Ta sama koncepcja / pytanie istnieje również dla programistów C # i Java, ale myślę, że widziałem tylko jedno pytanie związane z C #.

Czy to dlatego, że tak wiele przykładów używa ++ i? Czy dlatego, że istnieje popularna książka lub samouczek? Czy to dlatego, że programiści C po prostu uwielbiają wcisnąć jak najwięcej w jedną linię dla „wydajności” / „wydajności”, a zatem częściej spotykają się z „dziwnymi” konstrukcjami używającymi operatora ++?


23
(Dla mnie jako deweloperów C #, i ++ i ++ i są równe. Wiem, że nie są, ale jeśli napiszę kod, który zależy od różnicy, wolę przefakturować go, aby był bardziej czytelny i oczekuję, że kompilator i JITter się tym zajmą tego, ale jako programista C # nie próbuję zapisać jednego lub dwóch cykli zegara, ponieważ i tak jestem już stosunkowo mało wydajny)
Michael Stum

1
Jako początkujący programiści uwielbiamy bawić się arytmetyką, logiką i jesteśmy ciekawi, jak to działa. Myślę, że to duży powód, dla którego noobowie lubią takie pytania. może to nie być przełomowa funkcja, ale z pewnością jest interesująca! czy to nie powód, dla którego zostaliśmy programistami?
Chani

2
Czy dosłownie masz na myśli to wyrażenie ++i == i++, czy bardziej ogólnie różnicę w znaczeniu między ++ii i++?
Keith Thompson

Odpowiedzi:


30

Podejrzewam, że przynajmniej część tego jest nieco prostsza: nawet teraz na początku roku szkolnego widzimy wiele takich pytań, które stopniowo się zmniejszają przez cały rok.

W związku z tym uważam, że można zgadywać, że sporo z nich jest po prostu wynikiem zajęć, w których nauczyciel mówi co najmniej o tym, ale nie wyjaśnia zbyt dobrze swoich argumentów (tak często, jak nie bo on tak naprawdę ich nie rozumie). Zwłaszcza w oparciu o osoby, które wydają się zadawać te pytania, niewiele opiera się na faktycznym kodowaniu.


14
Czy to nie jest przeciwieństwo Wiecznego września? Wieczny wrzesień był wtedy, gdy AOL zaczął oferować dostęp do swoich klientów przez Usenet, przez co wyglądało to na wrzesień (miesiąc, w którym nowi uczniowie rozpoczynający szkołę tradycyjnie bombardowali tablice jako nowicjusze) przez cały rok.
nlawalker

Jest to ciekawa teoria, ale może nie zawsze wina nauczycieli, jeśli ktoś nie zrozumiał materiału: może uczniowie (1) nie zwracali wystarczającej uwagi na zajęciach i (2) byli zbyt leniwi, by szukać odpowiedzi na przepełnienie stosu przed zadając to samo pytanie ponownie.
Giorgio

12

Ponieważ programiści C MUSZĄ zrozumieć kolejność operacji. Programiści C # niekoniecznie używają operatorów bitowych (&, |, ~) lub operatorów prefiksów, ponieważ zwykle bardziej martwimy się o inne rzeczy. Jednak my, deweloperzy C #, nie zdajemy sobie sprawy, że powinniśmy wiedzieć, co robią operatorzy, z których korzystamy na co dzień i jak z nich właściwie korzystać. Większość z nas po prostu omija miejsca, w których może to stanowić problem.

Jaki jest wynik tego fragmentu?

    double x = 5.5;
    Console.WriteLine(x++);
    x = 5.5;
    Console.WriteLine(++x);

Powiedziałbym, że spora liczba doświadczonych deweloperów C # nie ma pojęcia, jaki jest wynik działania konsoli.


3
+1. Znajomość różnicy między operatorami inkrementacji / dekrementacji przed i po fiksacji jest tego warta w takich sytuacjach.
Maulrus

4
Trochę rozumiem sens i też nie poznałbym wyniku (wydaje mi się, że jest to 5.5 i 6.5, ale nie jestem pewien), a dla mnie nie miałoby to znaczenia, ponieważ przełożyłem go na x ++; Console.WriteLine (x); natychmiast. Nie jestem jednak całkowicie ignorantem, po prostu myślę, że to duży zapach kodu o zerowej użyteczności.
Michael Stum

9
Odpowiedź na pytanie nie ma nic wspólnego z kolejnością operacji ani różnicą przyrostów przedrostka i przyrostka. Jest to kwestia określonego i nieokreślonego zachowania.
David Thornley,

2
Spotkałem to dzisiaj. Coś jak if (myList[i++] == item). Sprytny. Znalazłem to, szukając źródła wyjątku IndexOutOfRange ... Rzeczywiście, bardzo „sprytne”.
rmac

7
Myślę, że jest to 5.5 i 6.5, ale nigdy wcześniej nie widziałem, żeby był ++używany na float i muszę powiedzieć, że nie wydaje się to zbyt odpowiednie. Czy ludzie to robią? (Wolałbym += 1)
Bart van Heukelom,

8

Myślę, że to oczywiste. W języku C #, dla int i, i++ == ++ijest zawsze fałszywe natomiast ++i == i++jest zawsze prawdziwe, niezawodnie, a każdy, kto jest zainteresowany może dowiedzieć się łatwo po prostu poprzez uczenie zasad C # (lub nawet po prostu uruchomienie go).

Z drugiej strony w C i C ++ jest prawie niezdefiniowany, ponieważ zależy to od kompilatora, środowiska wykonawczego, platformy itp., Więc odpowiedź na pytanie jest trudniejsza, więc wiele osób go zadaje.


+1 - ale nie jest to trudniejsze pytanie. „niezdefiniowany” to jedno słowo. Wszystkie rzeczy „zależy od ...” to rzeczy, o które większość ludzi się nie martwi. Nawet jeśli piszesz tylko dla jednej konkretnej platformy, powinieneś używać normalnych idiomów dla języka jako całości, a większość zastosowań operatorów zwiększających i zwiększających jest częścią kilku popularnych idiomów.
Steve314

+1: To faktycznie odpowiada na zadane pytanie (to znaczy, że nie czytasz między wierszami).
Thomas Eding

7

To popularne pytanie, ponieważ jest to podchwytliwe pytanie. Nie jest zdefiniowane .

Powyższy link prowadzi do FAQ na stronie głównej Bjarnesa Stroustrupa, który szczegółowo zajmuje się tym pytaniem i wyjaśnia, dlaczego ta konstrukcja jest niezdefiniowana. Mówi to:

Zasadniczo, w C i C ++, jeśli czytasz zmienną dwa razy w wyrażeniu, w którym ją również zapisujesz, wynik jest niezdefiniowany.

Uważa się, że niezdefiniowana kolejność oceny pozwala uzyskać kod o wyższej wydajności.


5

Najprawdopodobniej dlatego, że w C naprawdę ma to znaczenie, jeśli piszesz i++lub ++irobisz arytmetykę wskaźników. Tam ikluczowe znaczenie może mieć zwiększenie przed operacją lub po niej. Dam ci przykład, ale ostatni raz napisałem C 10 lat temu ...

W języku C # nigdy nie spotkałem się z sytuacją, która wymagałaby ode mnie myślenia i++lub ++iponieważ AFAIK, dla pętli for / while, to naprawdę nie ma znaczenia.


3
ważne jest, aby zrozumieć różnice w C # - tylko dlatego, że wydaje się, że używasz ich tylko do / każdej pętli, nie oznacza to, że jest to jedyne miejsce, w którym je znajdziesz
STW

całkowicie to prawda.
Mladen Prajdic

3
Nie dotyczy to pytania. Pytanie nie dotyczy różnicy między i++i ++i, ale łączy je w tym samym wyrażeniu.
David Thornley,

@David Pytanie brzmi, dlaczego programiści C są ciekawi tego. Odpowiedź (ma znaczenie w C, nie tyle w C #) jest całkowicie aktualna "
Florian F

2

Kilka lat temu przeczytałem, że operatorzy ci byli uważani za niebezpiecznych, więc spróbowałem znaleźć więcej informacji na ten temat. Gdyby w tym czasie istniał przepływ stosów, zapytałbym go o to.

Teraz dzieje się tak, ponieważ niektórzy pokręceni ludzie piszą takie pętle jak

while( [something] )
{
  a[++j] = ++j;
}

Nawet teraz nie jestem zbyt pewien, co się stanie / powinno się zdarzyć, ale jest dla mnie całkiem jasne, że wiele ++ [var] w tej samej linii prosi o kłopoty.


2
Przyrosty mają niezdefiniowane zachowanie w ramach zadania w C
tzenes,

7
Podobne przypisania x = ++j;są dobrze zdefiniowane w C. Powyższe jest niezdefiniowane, ponieważ jjest modyfikowane dwukrotnie bez pośrednich punktów sekwencji.
Matthew Flaschen

2

Dwa powody.

Prawidłową implementacją ++ i jest zwiększenie i, a następnie zwrócenie go. Prawidłową implementacją i ++ jest zapisanie bieżącej wartości, zwiększenie i i zwrócenie zapisanej wartości. Znajomość tych nie jest zaimplementowana, ponieważ synonimy są ważne.

Powstaje zatem pytanie, kiedy kompilator stosuje je podczas oceny wyrażenia, takiego jak równość.

Jeśli test równości jest wykonywany jako pierwszy, to operatorzy przed i po inkrementacji będą musieli napisać inną logikę niż wtedy, gdy najpierw oceniana jest lewa strona (lhs) i prawa strona (rhs), a następnie równość.

Chodzi o kolejność operacji, a to z kolei wpływa na kod, który się pisze. I nie jest zaskakujące, że nie wszystkie języki są zgodne.

Lista testów podstawowych pozwala programistom sprawdzić, czy ich założenia są poprawne, a także czy rzeczywista implementacja jest zgodna ze specyfikacją języka.

Może to mieć różny wpływ na pracę ze wskaźnikami, pętlami lub zwracaniem wartości.


5
Podstawowe testy w tym zakresie będą wprowadzać w błąd, ponieważ jest to nieokreślone zachowanie. Jeśli zadziała dokładnie tak, jak tego oczekujesz, nie ma gwarancji, że będzie następnym razem.
David Thornley,

Odpowiedziałeś na złe pytanie. Powinieneś odpowiedzieć, dlaczego programiści C są tego ciekawi.
Hugo,

Twój drugi akapit i tak jest niepoprawny (przynajmniej przed C ++ 11): zachowanie ++ipolega na użyciu wartościi+1 i w pewnym momencie, być może przed lub po tym, ale przed kolejnym punktem sekwencji, przyrostemi .
MM

@MattMcNabb - Czy możesz wskazać mi specyfikację tego, byłbym ciekawy, czy rzeczy się zmieniły we wdrażaniu. W czasach, gdy C ładnie odwzorowano na zestaw PDP, ++ i wstrzyknął instrukcję INC, podczas gdy wersja sufiksu musiała skopiować wartość do rejestru, aby użyć instrukcji po inkrementie. Co stanowiło konieczność opisanej przez ciebie zmiany?
Walt Stoneburner

2

Myślę, że to szok kulturowy.

Jak już wspomniano, zachowanie wyrażenia w C lub C ++ może być nieokreślone, ponieważ kolejność wykonywania przyrostów itp. Nie jest ściśle określona. Niezdefiniowane zachowanie jest dość powszechne w C i oczywiście wiele z nich zostało zaimportowanych do C ++.

Dla kogoś, kto dokonał programowania w innym języku - kogoś, kto wpadł na pomysł, że języki programowania powinny być precyzyjne i że komputery powinny robić to, co im kazano ... no cóż, szokiem jest odkryć, że C jest celowo niejasny w niektórych kwestiach.


C jest w ten sposób, ponieważ został zaprojektowany, aby obsługiwać znacznie więcej platform niż nawet istniało, gdy zdefiniowano C #, w tym najdziwniejsze platformy wbudowane lub mainframe, jakie można sobie wyobrazić. Z drugiej strony C # działa na ... x86 i 360? może też ARM? Nic więcej. Dlatego C # może zdefiniować znacznie więcej.
DeadMG,

++ Ponadto, czy C nie zostało po raz pierwszy zbudowane na PDP-11? Gdzie były instrukcje automatycznego przyrostu? C chciał być „blisko maszyny”, aby mógł pożytecznie wykorzystać zestaw instrukcji.
Mike Dunlavey,

@MikeDunlavey: Nie, C ++i --operatory nie były oparte na PDP-11, który nie istniał, gdy operatory te zostały wprowadzone w języku C poprzednika B. Patrz cm.bell-labs.com/who/dmr/chist.html i wyszukaj „auto-inkrement”.
Keith Thompson

1

Być może po prostu programiści C częściej używają inkrementacji ++ ogólnie - widząc, jak C nie ma „foreach” lub podobnej instrukcji do iteracji przez tablice. Aha, i oczywiście wskaźnik arytmetyki, jak wcześniej wspomniano!


1
Dobrze, zapomniałem, że foreach to stosunkowo nowoczesna koncepcja.
Michael Stum

-1

Różnica między obiema częściami: poczta i wstępne zwiększenie działają tak, jak te dwa fragmenty kodu powinny działać w każdym języku. TO NIE JEST W ZALEŻNOŚCI JĘZYKOWE !!!

++i

To jest przyrost. Ten fragment kodu najpierw zwiększy wartość, izanim wyśle ​​wartość do wiersza, w którym jest używany.

i++

To jest przyrost postu. Ten fragment kodu zwróci wartość, izanim zwiększy wartość iw wierszu, w którym jest używany.

W innym małym przykładzie:

int main(void)
{
  int i = 0; 
  int result; 
  result = 10 + ++i; 
  printf("%d \n", result); // = 11 

  int j = 0; 
  result = 10 + j++; 
  printf("%d \n", result);// = 10
  return 0;
}

Ten kod kompiluje się z wynikami na codepad.

Ma to związek z wewnętrzną implementacją przeciążenia operatora.

++i bezpośrednio zwiększa wartość (i dlatego jest nieco szybszy)

i++najpierw tworzy kopię, która jest zwracana tam, gdzie jest potrzebna, a następnie pierwotna wartość izmiennej jest zwiększana o jeden.

Mam nadzieję, że teraz jest to jasne.


Ale to nie dotyczy faktycznego pytania: dlaczego programiści C pytają o to bardziej niż inni. PO rozumie operatorów.
Martijn Pieters

Również ta odpowiedź jest nieprawidłowa pod względem faktycznym. ++inie jest szybszy i i++nie tworzy kopii. Ponadto ++izachowuje się nieco inaczej w C niż w C ++, nie mówiąc już o innych językach.
MM
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.