Znaczenie ios_base :: sync_with_stdio (false); cin.tie (NULL);

146

Jakie jest znaczenie włączania

ios_base::sync_with_stdio(false);
cin.tie(NULL);

w programach C ++?

W moich testach przyspiesza to czas wykonania, ale czy istnieje przypadek testowy, o który powinienem się martwić, włączając go?

Czy te 2 stwierdzenia zawsze muszą być razem, czy też wystarczy pierwsze, tj. Ignorowanie cin.tie(NULL)?

Ponadto, czy dozwolone jest używanie jednoczesnych poleceń C i C ++, jeśli jego wartość została ustawiona na false?

https://www.codechef.com/viewsolution/7316085

Powyższy kod działał dobrze, dopóki nie użyłem scanf/printfw programie C ++ z wartością as true. W tym przypadku dało to błąd segmentacji. Jakie może być tego wytłumaczenie?

Kshitij Kohli
źródło
Właściwie użyłeś tego z fałszem. Twój kod tak mówi ???
Suraj Jain

Odpowiedzi:

231

Te dwa wezwania mają różne znaczenia, które nie mają nic wspólnego z wydajnością; fakt, że przyspiesza czas wykonywania, jest (lub może być ) tylko efektem ubocznym. Powinieneś zrozumieć, co robi każdy z nich i nie włączać ich na ślepo do każdego programu, ponieważ wyglądają jak optymalizacja.

ios_base::sync_with_stdio(false);

Spowoduje to wyłączenie synchronizacji między standardowymi strumieniami C i C ++. Domyślnie wszystkie standardowe strumienie są zsynchronizowane, co w praktyce pozwala na mieszanie I / O w stylu C i C ++ i uzyskanie rozsądnych i oczekiwanych rezultatów. Jeśli wyłączysz synchronizację, strumienie C ++ będą mogły mieć własne niezależne bufory, co sprawia, że ​​mieszanie operacji we / wy w stylu C i C ++ jest przygodą.

Należy również pamiętać, że zsynchronizowane strumienie C ++ są bezpieczne dla wątków (dane wyjściowe z różnych wątków mogą się przeplatać, ale nie pojawiają się wyścigi danych).

cin.tie(NULL);

To rozwiązuje cinz cout. Powiązane strumienie zapewniają, że jeden strumień jest automatycznie opróżniany przed każdą operacją we / wy w drugim strumieniu.

Domyślnie cinjest powiązany, coutaby zapewnić rozsądną interakcję użytkownika. Na przykład:

std::cout << "Enter name:";
std::cin >> name;

Jeśli cini coutsą powiązane, możesz oczekiwać, że dane wyjściowe zostaną opróżnione (tj. Będą widoczne na konsoli), zanim program poprosi użytkownika o dane wejściowe. Jeśli rozwiążesz strumienie, program może zablokować oczekiwanie na wprowadzenie przez użytkownika nazwy, ale komunikat „Wprowadź nazwę” nie jest jeszcze widoczny (ponieważ coutjest domyślnie buforowany, wyjście jest opróżniane / wyświetlane na konsoli tylko na żądanie lub gdy bufor jest pełny).

Więc jeśli untie cinz cout, należy upewnić się, aby opróżnić coutręcznie za każdym razem, gdy chcesz wyświetlić coś przed oczekując na wejście cin.

Podsumowując, wiedz, co robi każdy z nich, zrozum konsekwencje, a następnie zdecyduj, czy naprawdę chcesz lub potrzebujesz możliwego efektu ubocznego w postaci poprawy szybkości.

Ionut
źródło
Kiedy mówisz "musisz upewnić się, że opróżniłeś cout ręcznie za każdym razem, gdy chcesz coś wyświetlić przed oczekiwaniem na wejście na cin", może to być tak proste, jak dodanie "... << std :: flush" lub "... < <std :: endl "do końca każdej linii zaczynającej się od" std :: cout << ... ", prawda?
Alan
4
Tak, to takie proste, ale uważaj na część „koniec każdego wiersza”. coutjest buforowany z jakiegoś powodu, jeśli opróżniasz go zbyt często, gdy w rzeczywistości go nie potrzebujesz, możesz zobaczyć spadek wydajności.
Ionut
@Ionut czy jest coś równoważnego funkcji tie () w C dla scanf, printf?
iajnr
1
@iajnr Nie, nie bezpośrednio. W C możesz albo opróżnić ręcznie wcześniej scanf(), całkowicie wyłączyć buforowanie lub przełączyć się na buforowanie linii (które powinno opróżniać się po nowej linii lub po odczytaniu wejścia stdin- zobacz linux.die.net/man/3/setlinebuf ).
Ionut
1
W leetcode znacznie poprawia czas działania, być może te konkurencyjne strony internetowe robią coś specjalnego dla testów wejściowych.
P0W
18

Ma to na celu zsynchronizowanie operacji we / wy ze świata C i C ++. Jeśli zsynchronizujesz, masz gwarancję, że zamówienia we wszystkich zamówieniach reklamowych są dokładnie takie, jakich oczekujesz. Ogólnie problem polega na buforowaniu IO, które powoduje problem, synchronizacja pozwala obu światom na współużytkowanie tych samych buforów. Na przykład cout << "Hello"; printf("World"); cout << "Ciao";; bez synchronizacji nigdy nie dowiesz się, HelloCiaoWorldczy otrzymasz, HelloWorldCiaoczy WorldHelloCiao...

tiepozwala mieć gwarancję, że kanały IO w świecie C ++ są ze sobą powiązane , co oznacza na przykład, że każde wyjście zostało opróżnione przed pojawieniem się danych wejściowych (pomyśl o cout << "What's your name ?"; cin >> name;).

Zawsze możesz mieszać IO C lub C ++, ale jeśli chcesz rozsądnego zachowania, musisz zsynchronizować oba światy. Uważaj, generalnie nie zaleca się ich mieszania, jeśli programujesz w C używaj C stdio, a jeśli programujesz w C ++ używaj strumieni. Ale możesz chcieć wymieszać istniejące biblioteki C z kodem C ++, aw takim przypadku konieczne jest zsynchronizowanie obu.

Jean-Baptiste Yunès
źródło
4
Nawet bez synchronizacji różne wywołania cout <<nie mogą zmienić kolejności, więc CiaoHelloWorldnie jest to możliwe w Twoim przykładowym przypadku. Synchronizacja dotyczy wyłącznie różnych metod buforowania.
Mikko Rantalainen
3

Użycie ios_base::sync_with_stdio(false);jest wystarczające do oddzielenia strumieni Ci C++. Możesz znaleźć dyskusję na ten temat w Standard C ++ IOStreams and Locales autorstwa Langera i Krefta. Zauważają, że sposób, w jaki to działa, jest określony przez implementację.

cin.tie(NULL)Rozmowa wydaje się zainteresowanie oddzielenia między działaniami na cini cout. Nie potrafię wyjaśnić, dlaczego użycie tego z inną optymalizacją powinno spowodować awarię. Jak wspomniano, podany link jest zły, więc nie ma tu żadnych spekulacji.

Don Wakefield
źródło
0

To zwykła rzecz, dzięki której wejście cin działa szybciej.

Dla szybkiego wyjaśnienia: pierwsza linia wyłącza synchronizację bufora między strumieniem cin i narzędziami stdio w stylu C (takimi jak scanf lub gets) - więc cin działa szybciej, ale nie można go używać jednocześnie z narzędziami stdio .

Druga linia odłącza cin od cout - domyślnie bufor cout opróżnia się za każdym razem, gdy czytasz coś z cin . Może to być powolne, gdy wielokrotnie czytasz coś małego, a potem wielokrotnie piszesz coś małego. Tak więc linia wyłącza tę synchronizację (dosłownie wiążąc cin do null zamiast cout ).

Sravya
źródło