C ++: "std :: endl" проти "\ n"


569

Багато книг на C ++ містять приклад подібного коду ...

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

... тому я теж завжди робив це. Але я бачив дуже багато коду від працюючих розробників на зразок цього:

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

Чи є технічна причина віддати перевагу одній над іншою, чи це лише питання стилю кодування?




25
@derobert цей старший за інших
Кіра

3
@HediNaily насправді так є. Але відповідь з іншого вражає мене як дещо кращою, тому я вирішив це зробити так. Також інший трохи ширший, також покриває '\n'.
дероберт

stackoverflow.com/a/30968225/3163618 може бути значна різниця в продуктивності.
qwr

Відповіді:


473

Змінюються символи, що закінчуються рядком, не мають значення, якщо припустити, що файл відкритий у текстовому режимі, і це те, що ви отримуєте, якщо не попросите двійкові. Складена програма випише правильну річ для системи, складеної для.

Єдина відмінність полягає в тому, що std::endlзмиває вихідний буфер, а '\n'не. Якщо ви не хочете, щоб буфер часто промивався, використовуйте '\n'. Якщо ви це робите (наприклад, якщо ви хочете отримати весь результат, а програма нестабільна), використовуйте std::endl.


24
Або розглянути можливість використання ::std::cerrзамість того, що ::std::coutвін розблоковується та розмивається при кожній операції виводу.
всезначний

142
@Omnifarious: Ні один std :: cerr не повинен бути зарезервований для помилок. Два потоки не синхронізовані разом, тому якщо ви виведете якийсь текст для cout, він може бути буферним, і cerr перейде безпосередньо до виводу, що призведе до змішаного режиму відображення. Використовуйте cerr для того, що він повинен бути (помилки), а cout - для чого він призначений (нормальна взаємодія).
Мартін Йорк

23
@Lucas: Платформа не має більше ніж "\ n".
CB Bailey

32
@LokiAstari: Я б не сказав, що stderrце "помилки". Швидше, це для діапазонних діагностичних повідомлень, якщо ви хочете. Слід сказати ./prog > fileі зберігати лише справжню корисну програму, але програма може видати набагато більше інформації про стан навіть у звичайній взаємодії.
Керрек СБ

13
"У багатьох реалізаціях стандартний висновок буферизований рядками, і запис" \ n "все одно викликає флеш, якщо тільки не виконується std :: cout.sync_with_stdio (false)." скопійовано звідси
GuLearn

249

Різницю можна проілюструвати наступним чином:

std::cout << std::endl;

еквівалентно

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

Тому,

  • Використовуйте std::endlЯкщо ви хочете примусити негайний приплив до виходу.
  • Використовуйте, \nякщо ви турбуєтесь про продуктивність (що, мабуть, не так, якщо ви використовуєте <<оператора).

Я використовую \nв більшості ліній.
Потім використовуйте std::endlв кінці абзацу (але це лише звичка, а зазвичай не потрібна).

На відміну від інших тверджень, \nсимвол відображається у правильній послідовності кінця рядка платформи лише у тому випадку, якщо потік збирається у файл ( std::cinі std::coutє спеціальним, але все-таки файлом (або схожим на файл)).


5
У багатьох випадках «побачити вихід негайно» - це червона оселедець, оскільки coutвона прив’язана cin, це означає, що якщо ви прочитаєте вхід з cin, coutспочатку буде промиватися. Але якщо ви хочете відобразити панель прогресу чи щось, не читаючи cin, то переконайтеся, що промивання корисно.
Кріс Єстер-Янг

9
@LokiAstari: якщо ви використовуєте оператор <<, ви, мабуть, не турбуєтесь про продуктивність - чому? Я не знав, що operator<<це не виконавець, або яку альтернативу використовувати для виконання? Будь ласка, вкажіть мені якийсь матеріал, щоб зрозуміти це далі.
legends2k

8
@ legends2k: Існує давня розповідь про дружин, що потоки C ++ не такі ефективні, як C printf (). Хоча це правда в тій чи іншій мірі, головна різниця у швидкості викликається людьми, що використовують C ++ потоки неправильно. stackoverflow.com/a/1042121/14065 У програмі C ++ не забудьте синхронізувати iostreams з C-потоками sync_with_stdio(false)і не обробляйте вихід постійно. Нехай бібліотека розробить, коли це зробити. stackoverflow.com/a/1926432/14065
Мартін Йорк

6
@Loki: Існує міська легенда, яка sync_with_stdioробить iostreams такими ж швидкими, як і stdio. Це не так
Бен Войгт

2
@BenVoigt: Я був обережним із своїм формулюванням вище (тому я задоволений ними). Він не такий виконавський, як stdio (тому що робить більше). Але багато розбіжностей у ефективності, на які скаржаться люди, це викликано синхронізацією зі stdio.
Мартін Йорк


30

Там є інший виклик функції, який мається на увазі, якщо ви збираєтесь використовувати std::endl

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

а) телефонує оператору <<один раз.
б) дзвонить оператору <<двічі.


19
Це може бути очевидним, але це має величезний вплив на потокові програми, де, як правило, перша версія запише один рядок в один кадр, де друга версія може бути розділена записом з інших потоків. Досить часто мені доводиться писати std :: cout << "привіт \ n" << std :: flush, щоб цього уникнути.
smparkes

Про що std::cout << "Hello" << "\n";?
byxor

1
@byxor Майже те саме, за винятком розмивання буфера, як описано в інших відповідях. У будь-якому випадку, це зайве, коли ви можете з’єднати два рядкові букви в один.
iBug

Ну, якщо рядок для друку не є буквальною, то і викликів до значень <<було б 2 у випадку a , тому я б не стверджував, що необхідність одного або двох <<(або взагалі двох викликів функцій) бути a різниця між \nі endl.
Енріко Марія Де Анджеліс

Lol ні, це не причина, що я використовую \ n.
Карло Вуд

28

Я згадав, читаючи про це в стандарті, так ось що:

Дивіться стандарт C11, який визначає, як поводяться стандартні потоки, оскільки програми C ++ інтерфейсують з CRT, стандарт C11 повинен регулювати тут політику змиву.

ISO / IEC 9899: 201x

7.21.3 §7

При запуску програми три текстові потоки заздалегідь визначені і їх не потрібно відкривати явно - стандартний вхід (для читання звичайного введення), стандартний вихід (для запису звичайного виводу) та стандартна помилка (для запису діагностичного виводу). Спочатку відкрившись, стандартний потік помилок не буферизований повністю; стандартні вхідні та стандартні вихідні потоки повністю буферизовані лише тоді, якщо потік можна визначити таким, що не відноситься до інтерактивного пристрою.

7.21.3 §3

Якщо потік не буферизований, символи повинні з’являтися з джерела або в пункт призначення якнайшвидше. В іншому випадку символи можуть накопичуватися і передаватися в хост-середовище або з нього як блок. Коли потік повністю буферизований, символи призначені для передачі в середовище хосту або з нього як блок при заповненні буфера. Коли потік є буферним рядком, символи призначені для передачі в середовище хосту або з нього як блок, коли зустрічається символ нового рядка. Крім того, символи призначені для передачі у вигляді блоку в середовище хоста, коли буфер заповнений, коли запит вводиться в небуферизований потік або коли запит вводиться в потоковий буферний потік, який вимагає передачі символів з хост-середовища .

Це означає , що std::coutі std::cinповністю буферізованние тоді і тільки тоді , коли вони мають в увазі не інтерактивного пристрою. Іншими словами, якщо stdout приєднаний до терміналу, різниці в поведінці немає.

Однак, якщо std::cout.sync_with_stdio(false)його зателефонують, то '\n'не викличе наближення навіть до інтерактивних пристроїв. Інакше '\n'еквівалентно, std::endlкрім випадків, коли для файлів: c ++ ref на std :: endl .


19

Вони обидва записують відповідні символи в кінці рядка. На додаток до цього endl призведе до здійснення буфера. Зазвичай ви не хочете використовувати endl під час виконання вводу-виводу файлів, оскільки непотрібні коміти можуть вплинути на продуктивність.



10

Якщо ви використовуєте Qt і endl, ви можете випадково опинитися неправильно, endlщо дає дуже дивовижні результати. Дивіться такий фрагмент коду:

#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();
}

Зауважте, що я писав endlзамість std::endl(що було б правильно) і, мабуть, є endlфункція, визначена в qtextstream.h (яка є частиною QtCore).

Використовуючи "\n"замість того, щоб endlповністю уникати можливих проблем з простором імен. Це також хороший приклад, чому розміщення символів у глобальному просторі імен (як Qt за замовчуванням) є поганою ідеєю.


31
Ургу! Хто б хотів колись бути using namespace std;?? :-)
Стів Фоллі

2
Неприємний. Дякую за коментар, я впевнений, що інші зіткнуться з цим.
Head Geek

@SteveFolly я. Чому ні?
ʇolɐǝz ǝɥʇ qoq

@ ʇolɐǝzǝɥʇqoq Це нормально, доки ви цього не робите у файлах заголовків.
смерлін

1
@ ʇolɐǝzǝɥʇqoq Будь ласка, уникайте using namespace std;. Це вважається поганою практикою. Див. Чому "використовується простір імен std;" вважається поганою практикою?
LF

2

Я завжди мав звичку використовувати std :: endl, тому що мені це легко бачити.


2

std::endlМаніпулятором еквівалентно '\n'. Але std::endlзавжди промиває струмок.

std::cout << "Test line" << std::endl; // with flush
std::cout << "Test line\n"; // no flush

1

Якщо ви маєте намір запустити свою програму на чомусь іншому, ніж на своєму власному ноутбуці, ніколи не використовуйте цю endlзаяву. Особливо, якщо ви пишете багато коротких рядків або, як я часто бачив поодинокі символи до файлу. Використання endlноу-хау для знищення мережевих файлових систем, таких як NFS.


Це пов’язано з промиванням? Я бачу, як це можливо.
Head Geek

@Head Дійсно. Я також бачив, як вона пошкоджує продуктивність IO на диску.
sbi

0

З посиланням Це маніпулятор вводу-виводу тільки для виходу .

std::endlВставляє символ нового рядка у вихідну послідовність os і промиває його так, ніби за допомогою виклику з os.put(os.widen('\n'))наступним os.flush().

Коли користуватися:

Цей маніпулятор може бути використаний для негайного отримання лінії виводу ,

напр

під час відображення результатів із тривалого процесу, активності журналу декількох потоків або активності журналу програми, яка може несподівано вийти з ладу.

Також

Явний змив std :: cout також необхідний перед викликом до std :: system, якщо спаєнізований процес виконує будь-який екран вводу / виводу. У більшості інших звичайних інтерактивних сценаріїв вводу / виводу std :: endl є надлишковим при використанні з std :: cout, оскільки будь-який вхід з std :: cin, вихід у std :: cerr або завершення програми змушує викликати std :: cout .flush (). Використання std :: endl замість '\ n', яке заохочується деякими джерелами, може значно погіршити продуктивність виводу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.