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?




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.


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)).


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,


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 .


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 .


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ść.



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.


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

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

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.


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ą.

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.