C ++: „std :: endl” vs „\ n”

569

Wiele książek w C ++ zawiera taki przykładowy kod ...

std::cout << "Test line" << std::endl;

... więc zawsze to robiłem. Ale zamiast tego widziałem dużo kodu od takich programistów:

std::cout << "Test line\n";

Czy istnieje techniczny powód, aby preferować jeden nad drugim, czy to tylko kwestia stylu kodowania?

Head Geek
źródło
25
@derobert ten jest starszy od drugiego
Kira
3
@HediNaily rzeczywiście tak jest. Ale odpowiedź na drugą wydaje mi się nieco lepsza, więc postanowiłem to zrobić w ten sposób. Również drugi jest nieco szerszy, obejmując również '\n'.
derobert
stackoverflow.com/a/30968225/3163618 może występować znacząca różnica w wydajności.
qwr

Odpowiedzi:

473

Różne znaki końca linii nie mają znaczenia, przy założeniu, że plik jest otwarty w trybie tekstowym, co otrzymasz, chyba że poprosisz o plik binarny. Skompilowany program wypisze poprawną rzecz dla skompilowanego systemu.

Jedyną różnicą jest to, że std::endlopróżnia bufor wyjściowy, a '\n'nie. Jeśli nie chcesz, aby bufor był często opróżniany, użyj '\n'. Jeśli tak (na przykład, jeśli chcesz uzyskać wszystkie dane wyjściowe, a program jest niestabilny), użyj std::endl.

David Thornley
źródło
24
Lub rozważ użycie ::std::cerrzamiast, ::std::coutponieważ jest niebuforowane i opróżniane przy każdej operacji wyjściowej.
Wszechobecny
142
@Omnifarious: Brak błędów std :: cerr na błędy. Dwa strumienie nie są zsynchronizowane, więc jeśli wyślesz jakiś tekst do cout, może on zostać buforowany, a cerr przejdzie bezpośrednio do wyjścia, co spowoduje wyświetlenie trybu mieszanego. Użyj cerr dla tego, co powinno być (błędy) i cout dla tego, do czego jest przeznaczony (normalna interakcja).
Martin York,
23
@Lucas: Nie więcej niż „\ n” jest świadomy platformy.
CB Bailey,
32
@LokiAstari: Nie powiedziałbym, że stderrto „błędy”. Raczej jest to dla komunikatów diagnostycznych poza pasmem, jeśli chcesz. Powinno być możliwe powiedzenie ./prog > filei zapisanie tylko prawdziwego ładunku programu, ale program może chcieć wyświetlać znacznie więcej informacji o stanie, nawet podczas normalnej interakcji.
Kerrek SB,
13
„W wielu implementacjach standardowe wyjście jest buforowane w linii, a pisanie„ \ n ”i tak powoduje opróżnienie, chyba że wykonano std :: cout.sync_with_stdio (false).” skopiowano stąd
GuLearn
249

Różnicę można zilustrować następująco:

std::cout << std::endl;

jest równa

std::cout << '\n' << std::flush;

Więc,

  • Użyj std::endlJeśli chcesz wymusić natychmiastowe wyrównanie do wyjścia.
  • Użyj, \njeśli martwisz się wydajnością (co prawdopodobnie nie ma miejsca w przypadku korzystania z <<operatora).

Używam \nna większości linii.
Następnie użyj std::endlna końcu akapitu (ale to tylko nawyk i zwykle nie jest konieczny).

W przeciwieństwie do innych twierdzeń, \nznak jest odwzorowany na prawidłową sekwencję końca linii na platformie tylko wtedy, gdy strumień przechodzi do pliku ( std::cini std::coutjest specjalnym, ale nadal plikiem (lub podobnym do pliku)).

Martin York
źródło
5
W wielu przypadkach „natychmiast zobacz wynik” to czerwony śledź, ponieważ coutjest powiązany z tym cin, co oznacza, że ​​jeśli przeczytasz dane wejściowe cin, coutnajpierw zostaną usunięte. Ale jeśli chcesz wyświetlić pasek postępu lub coś bez czytania z cin, to na pewno opróżnianie jest przydatne.
Chris Jester-Young
9
@LokiAstari: jeśli używasz operatora <<, prawdopodobnie nie martwisz się wydajnością - dlaczego? Nie wiedziałem, że operator<<to nie jest wydajne, ani jakiej alternatywy dla wydajności? Proszę wskazać mi jakiś materiał, aby lepiej to zrozumieć.
legends2k
8
@ legends2k: Istnieje opowieść starych żon, że strumienie C ++ nie są tak wydajne jak C printf (). Chociaż w pewnym stopniu jest to prawda, główna różnica w szybkości jest spowodowana przez osoby niepoprawnie używające strumieni C ++. stackoverflow.com/a/1042121/14065 W C ++ pamiętaj, aby zsynchronizować iostreamy ze strumieniami C sync_with_stdio(false)i nie opróżniaj swoich wyników w sposób ciągły. Niech biblioteka obliczy, kiedy to zrobić. stackoverflow.com/a/1926432/14065
Martin York
6
@Loki: Istnieje miejska legenda, która sync_with_stdiosprawia , że iostreams jest tak szybki, jak Stdio. Nie robi tego
Ben Voigt,
2
@BenVoigt: Byłem ostrożny z powyższym sformułowaniem (więc jestem z nich zadowolony). Nie jest tak wydajny jak stdio (ponieważ robi więcej). ALE duża część luki w wydajności, na którą narzekają ludzie, jest spowodowana synchronizacją ze standardem.
Martin York,
40

Mogą występować problemy z wydajnością, std::endlwymuszają opróżnianie strumienia wyjściowego.

Martin Beckett
źródło
1
I może wykonać dowolne inne przetwarzanie wymagane przez system lokalny, aby działało dobrze.
dmckee --- były moderator kociak
30

Istnieje sugerowane inne wywołanie funkcji, jeśli zamierzasz użyć std::endl

a) std::cout << "Hello\n";
b) std::cout << "Hello" << std::endl;

a) <<jeden raz dzwoni do operatora .
b) <<dwukrotnie wzywa operatora .

Nathan
źródło
19
Może to być oczywiste, ale ma ogromny wpływ na programy wątkowe, w których pierwsza wersja zapisuje jedną linię w jednym ujęciu, a druga wersja może być podzielona przez zapisy z innych wątków. Często wystarczy, że piszę std :: cout << "hello \ n" << std :: flush, aby tego uniknąć.
smparkes
Co std::cout << "Hello" << "\n";?
byxor
1
@ byxor Prawie takie same, z wyjątkiem opróżniania bufora, jak opisano w innych odpowiedziach. W każdym razie jest to zbędne, gdy można połączyć dwa literały łańcuchowe w jeden.
iBug
Cóż, jeśli ciąg, który ma być wydrukowany, nie jest literałem, to wywołania do <<byłoby również 2 w przypadku a , więc nie twierdziłbym, że potrzeba jednego lub dwóch <<(lub ogólnie dwóch wywołań funkcji) różnica między \ni endl.
Enrico Maria De Angelis
Lol nie, nie dlatego używam \ n.
Carlo Wood
28

Przypomniałem sobie o czytaniu o tym w standardzie, więc oto:

Zobacz standard C11, który określa, jak zachowują się standardowe strumienie, ponieważ programy C ++ łączą CRT, standard C11 powinien tutaj rządzić polityką opróżniania.

ISO / IEC 9899: 201x

7.21.3 §7

Podczas uruchamiania programu trzy strumienie tekstu są wstępnie zdefiniowane i nie muszą być jawnie otwierane - standardowe wejście (do odczytu konwencjonalnego wejścia), standardowe wyjście (do zapisu konwencjonalnego wyjścia) i standardowy błąd (do zapisu wyjścia diagnostycznego). Po pierwszym otwarciu standardowy strumień błędów nie jest w pełni buforowany; standardowe strumienie wejściowe i wyjściowe są w pełni buforowane wtedy i tylko wtedy, gdy można ustalić, że strumień nie odnosi się do urządzenia interaktywnego.

7.21.3 §3

Gdy strumień nie jest buforowany, postacie powinny pojawić się jak najszybciej ze źródła lub w miejscu docelowym. W przeciwnym razie znaki mogą być gromadzone i przesyłane do lub ze środowiska hosta jako blok. Gdy strumień jest w pełni buforowany, znaki mają być przesyłane do lub ze środowiska hosta jako blok, gdy bufor jest zapełniony. Gdy strumień jest buforowany w linii, znaki mają być przesyłane do lub ze środowiska hosta jako blok, gdy napotkany zostanie znak nowej linii. Ponadto znaki mają być przesyłane jako blok do środowiska hosta, gdy bufor jest zapełniony, gdy żądanie jest przesyłane w niebuforowanym strumieniu lub gdy żądanie jest wprowadzane w strumieniu buforowanym w linii, który wymaga transmisji znaków ze środowiska hosta .

Oznacza to, że std::couti std::cinsą w pełni buforowane tylko wtedy, gdy są one nawiązujące do nieinterakcyjnym urządzenia. Innymi słowy, jeśli stdout jest podłączony do terminala, nie ma różnicy w zachowaniu.

Jednak jeśli std::cout.sync_with_stdio(false)zostanie wywołany, '\n'nie spowoduje koloru nawet w przypadku urządzeń interaktywnych. W przeciwnym razie '\n'jest to równoważne z std::endlwyjątkiem przesyłania potokowego do plików: c ++ ref on std :: endl .

Emily L.
źródło
19

Obaj napiszą odpowiednie znaki końca wiersza. Oprócz tego endl spowoduje, że bufor zostanie zatwierdzony. Zazwyczaj nie chcesz używać Endl podczas wykonywania operacji we / wy pliku, ponieważ niepotrzebne zatwierdzenia mogą wpływać na wydajność.

Ferruccio
źródło
10

Jeśli użyjesz Qt i endl, możesz przypadkowo skończyć się niepoprawnym, endlco daje bardzo zaskakujące wyniki. Zobacz następujący fragment kodu:

#include <iostream>
#include <QtCore/QtCore> 
#include <QtGui/QtGui>

// notice that there is no "using namespace std;"
int main(int argc, char** argv)
{
    QApplication qapp(argc,argv);
    QMainWindow mw;
    mw.show();
    std::cout << "Finished Execution!" << endl;
    // This prints something similar to: "Finished Execution!67006AB4"
    return qapp.exec();
}

Zauważ, że napisałem endlzamiast std::endl(co byłoby poprawne) i najwyraźniej istnieje endlfunkcja zdefiniowana w qtextstream.h (która jest częścią QtCore).

Używanie "\n"zamiast endlcałkowicie pomija wszelkie potencjalne problemy z przestrzenią nazw. Jest to również dobry przykład, dlaczego umieszczanie symboli w globalnej przestrzeni nazw (jak domyślnie Qt) jest złym pomysłem.

Smerlin
źródło
31
Urgh! Kto kiedykolwiek chciałby być using namespace std;? :-)
Steve Folly
2
Paskudny. Dzięki za komentarz, jestem pewien, że inni na to wpadną.
Head Geek
@ SteveFolly I tak. Dlaczego nie?
ʇolɐǝz ǝɥʇ qoq
@ ʇolɐǝzǝɥʇqoq Jest ok, o ile nie robisz tego w plikach nagłówkowych.
smerlin
1
@ ʇolɐǝzǝɥʇqoq Unikaj using namespace std;. Jest to uważane za złą praktykę. Zobacz, dlaczego „korzystanie z przestrzeni nazw std;” uważane za złą praktykę?
LF
2

Zawsze miałem zwyczaj używania std :: endl, ponieważ łatwo mi to zobaczyć.

Zee JollyRoger
źródło
2

std::endlManipulator jest równoważna '\n'. Ale std::endlzawsze opróżnia strumień.

std::cout << "Test line" << std::endl; // with flush
std::cout << "Test line\n"; // no flush
Newbyte
źródło
1

Jeśli zamierzasz uruchomić program na czymkolwiek innym niż na własnym laptopie, nigdy nie używaj tego endloświadczenia. Zwłaszcza jeśli piszesz dużo krótkich linii lub jak często widziałem pojedyncze znaki w pliku. Używanie endljest znane do zabijania sieciowych systemów plików, takich jak NFS.

John Damm Sørensen
źródło
Czy to z powodu spłukiwania? Widzę, jak to możliwe.
Head Geek
@ Head Indeed. Widziałem też, jak rujnuje wydajność operacji we / wy dysku.
sbi
0

Z odniesieniem Jest to manipulator we / wy tylko wyjściowy .

std::endlWstawia znak nowej linii do sekwencji wyjściowej os i opróżnia go, jakby wywoływał, os.put(os.widen('\n'))a następnie os.flush().

Kiedy użyć:

Tego manipulatora można użyć do natychmiastowego wygenerowania linii wyjściowej ,

na przykład

podczas wyświetlania wyniku długotrwałego procesu, rejestrowania aktywności wielu wątków lub rejestrowania aktywności programu, który może niespodziewanie ulec awarii.

Również

Wyraźny kolor std :: cout jest również konieczny przed wywołaniem std :: system, jeśli spawnowany proces wykonuje dowolne ekranowe We / Wy. W większości innych typowych interaktywnych scenariuszy we / wy std :: endl jest redundantne, gdy jest używane z std :: cout, ponieważ każde wejście z std :: cin, wyjście do std :: cerr lub zakończenie programu wymusza wywołanie std :: cout .spłukać(). Użycie std :: endl zamiast „\ n”, zachęcane przez niektóre źródła, może znacznie obniżyć wydajność wyjściową.

Kaleem Ullah
źródło