C ++: Đây là std :: endl


569

Nhiều sách C ++ chứa mã ví dụ như thế này ...

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

... vì vậy tôi cũng luôn luôn làm điều đó. Nhưng tôi đã thấy rất nhiều mã từ các nhà phát triển đang làm việc như thế này:

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

Có một lý do kỹ thuật để thích cái này hơn cái kia, hay đó chỉ là vấn đề của phong cách mã hóa?




25
@derobert cái này cũ hơn cái kia
Kira

3
@HediN Daily thực sự là nó. Nhưng câu trả lời khác lại khiến tôi cảm thấy tốt hơn một chút, vì vậy tôi đã chọn làm theo cách đó. Ngoài ra, một cái khác là rộng hơn một chút, cũng bao gồm '\n'.
derobert

stackoverflow.com/a/30968225/3163618 có thể có sự khác biệt đáng kể về hiệu suất.
qwr

Câu trả lời:


473

Các ký tự kết thúc dòng khác nhau không quan trọng, giả sử tệp được mở ở chế độ văn bản, đó là những gì bạn nhận được trừ khi bạn yêu cầu nhị phân. Chương trình được biên dịch sẽ viết ra điều chính xác cho hệ thống được biên dịch cho.

Sự khác biệt duy nhất là làm sạch std::endlbộ đệm đầu ra, và '\n'không. Nếu bạn không muốn bộ đệm bị xóa thường xuyên, hãy sử dụng '\n'. Nếu bạn làm (ví dụ, nếu bạn muốn nhận tất cả đầu ra và chương trình không ổn định), hãy sử dụng std::endl.


24
Hoặc xem xét việc sử dụng ::std::cerrthay vì ::std::coutkể từ khi nó không có bộ đệm và được tuôn ra với mỗi hoạt động đầu ra.
Omnifarious

142
@Omnifarious: Không có std :: cerr nên được dành riêng cho các lỗi. Hai luồng không được đồng bộ hóa với nhau, vì vậy nếu bạn xuất một số văn bản sang cout thì nó có thể được đệm và cerr sẽ chuyển trực tiếp đến đầu ra, điều này dẫn đến hiển thị chế độ hỗn hợp. Sử dụng cerr cho những gì nó được cho là (lỗi) và cout cho những gì nó được thiết kế cho (tương tác bình thường).
Martin York

23
@Lucas: Không nhiều hơn '\ n' là nhận thức về nền tảng.
CB Bailey

32
@LokiAstari: Tôi không nói stderrlà vì "lỗi". Thay vào đó, nó dành cho các tin nhắn chẩn đoán ngoài băng, nếu bạn muốn. Có thể nói ./prog > filevà chỉ lưu trữ trọng tải chương trình thực sự, nhưng chương trình có thể muốn xuất ra nhiều thông tin trạng thái hơn, ngay cả trong tương tác bình thường.
Kerrek SB

13
"Trong nhiều triển khai, đầu ra tiêu chuẩn được đệm dòng và viết '\ n' gây ra hiện tượng lộn xộn, trừ khi std :: cout.sync_with_stdio (false) đã được thực thi." được sao chép từ đây
GuLearn

249

Sự khác biệt có thể được minh họa bằng cách sau:

std::cout << std::endl;

tương đương với

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

Vì thế,

  • Sử dụng std::endlNếu bạn muốn buộc xả ngay lập tức vào đầu ra.
  • Sử dụng \nnếu bạn lo lắng về hiệu suất (có lẽ không phải là trường hợp nếu bạn đang sử dụng <<toán tử).

Tôi sử dụng \ntrên hầu hết các dòng.
Sau đó sử dụng std::endlở cuối đoạn văn (nhưng đó chỉ là thói quen và thường không cần thiết).

Trái với các khiếu nại khác, \nký tự được ánh xạ đến cuối nền tảng chính xác của chuỗi dòng chỉ khi luồng đang đi đến một tệp ( std::cinstd::coutlà tệp đặc biệt nhưng vẫn là tệp (hoặc giống như tệp)).


5
Trong nhiều trường hợp, "xem đầu ra ngay lập tức" là một cá trích đỏ, vì coutđược gắn với cin, có nghĩa là nếu bạn đọc đầu vào từ cin, coutsẽ bị xóa trước. Nhưng nếu bạn muốn hiển thị một thanh tiến trình hoặc một cái gì đó mà không cần đọc từ cinđó, thì chắc chắn, xả nước là hữu ích.
Chris Jester-Young

9
@LokiAstari: nếu bạn đang sử dụng toán tử <<, có lẽ bạn không lo lắng về hiệu suất - tại sao? Tôi không biết đó operator<<không phải là hiệu suất, hoặc sử dụng thay thế nào cho hiệu suất? Xin vui lòng chỉ cho tôi một số tài liệu để hiểu thêm về điều này.
huyền thoại2k

8
@ legends2k: Có một câu chuyện về những người vợ cũ rằng các luồng C ++ không hoạt động như C printf (). Mặc dù đúng ở một mức độ nào đó, sự khác biệt chính về tốc độ là do những người sử dụng luồng C ++ không chính xác. stackoverflow.com/a/1042121/14065 Trong C ++, hãy nhớ không đồng bộ hóa iostreams với luồng C sync_with_stdio(false)và không xả liên tục đầu ra của bạn. Hãy để thư viện làm việc khi nào để làm điều đó. stackoverflow.com/a/1926432/14065
Martin York

6
@Loki: Có một truyền thuyết đô thị sync_with_stdiolàm cho iostream nhanh như stdio. Nó không
Ben Voigt

2
@BenVoigt: Tôi đã cẩn thận với từ ngữ của tôi ở trên (vì vậy tôi hài lòng với họ). Nó không phải là biểu diễn như stdio (vì nó làm nhiều hơn). NHƯNG rất nhiều khoảng cách hiệu suất mà mọi người phàn nàn là do đồng bộ hóa với stdio.
Martin York

40

Có thể có các vấn đề về hiệu suất, std::endlbuộc một luồng đầu ra.


1
Và nó có thể thực hiện bất kỳ xử lý nào khác mà hệ thống cục bộ yêu cầu để làm cho công việc này hoạt động tốt.
dmckee --- ex-moderator mèo con

30

Có một cuộc gọi chức năng khác ngụ ý trong đó nếu bạn sẽ sử dụng std::endl

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

a) gọi tổng đài <<một lần.
b) gọi tổng đài <<hai lần.


19
Điều này có thể rõ ràng, nhưng nó có tác động rất lớn đến các chương trình luồng, thông thường, phiên bản đầu tiên sẽ viết một dòng trong một lần chụp trong đó phiên bản thứ hai có thể được phân tách bằng cách ghi từ các luồng khác. Thường thì tôi thấy mình viết std :: cout << "hello \ n" << std :: flush để tránh điều này.
smparkes

Thế còn std::cout << "Hello" << "\n";?
byxor

1
@byxor Hầu như giống nhau ngoại trừ việc xóa bộ đệm như được mô tả trong các câu trả lời khác. Dù sao, nó là dư thừa khi bạn có thể hợp nhất hai chuỗi ký tự thành một.
iBug

Chà, nếu chuỗi được in không phải là một nghĩa đen, thì các cuộc gọi đến <<cũng sẽ là 2 trong trường hợp a , do đó tôi sẽ không yêu cầu một hoặc hai <<(hoặc hai lệnh gọi hàm nói chung) là một sự khác biệt giữa \nendl.
Enrico Maria De Angelis

Không, đó không phải là lý do tôi sử dụng \ n.
Carlo Wood

28

Tôi nhớ lại việc đọc về điều này trong tiêu chuẩn, vì vậy ở đây đi:

Xem tiêu chuẩn C11 xác định cách thức hoạt động của các luồng tiêu chuẩn, vì các chương trình C ++ giao tiếp với CRT, tiêu chuẩn C11 sẽ chi phối chính sách xả tại đây.

ISO / IEC 9899: 201x

7.21.3 §7

Khi khởi động chương trình, ba luồng văn bản được xác định trước và không cần phải mở một cách rõ ràng - đầu vào tiêu chuẩn (để đọc đầu vào thông thường), đầu ra tiêu chuẩn (để viết đầu ra thông thường) và lỗi tiêu chuẩn (để ghi đầu ra chẩn đoán). Khi mở lần đầu, luồng lỗi tiêu chuẩn không được đệm hoàn toàn; các luồng đầu vào tiêu chuẩn và đầu ra tiêu chuẩn được đệm hoàn toàn khi và chỉ khi luồng có thể được xác định không tham chiếu đến một thiết bị tương tác.

7.21.3 §3

Khi một luồng không có bộ đệm, các ký tự được dự định xuất hiện từ nguồn hoặc tại đích càng sớm càng tốt. Mặt khác, các ký tự có thể được tích lũy và truyền đến hoặc từ môi trường máy chủ dưới dạng một khối. Khi một luồng được đệm hoàn toàn, các ký tự được dự định được truyền đến hoặc từ môi trường máy chủ dưới dạng một khối khi bộ đệm được lấp đầy. Khi một luồng được đệm dòng, các ký tự được dự định được truyền đến hoặc từ môi trường máy chủ dưới dạng một khối khi gặp phải một ký tự dòng mới. Hơn nữa, các ký tự được dự định được truyền dưới dạng một khối đến môi trường máy chủ khi bộ đệm được lấp đầy, khi đầu vào được yêu cầu trên luồng không có bộ đệm hoặc khi đầu vào được yêu cầu trên luồng được đệm yêu cầu truyền các ký tự từ môi trường máy chủ .

Điều này có nghĩa là std::coutstd::cinđược đệm hoàn toàn khi và chỉ khi chúng đề cập đến một thiết bị không tương tác. Nói cách khác, nếu thiết bị xuất chuẩn được gắn vào một thiết bị đầu cuối thì không có sự khác biệt trong hành vi.

Tuy nhiên, nếu std::cout.sync_with_stdio(false)được gọi, thì '\n'sẽ không gây ra ngay cả các thiết bị tương tác. Mặt khác '\n'là tương đương với std::endltrừ khi đường ống đến tập tin: c ++ ref trên std :: endl .


19

Cả hai sẽ viết (các) ký tự cuối dòng thích hợp. Thêm vào đó endl sẽ làm cho bộ đệm được cam kết. Bạn thường không muốn sử dụng endl khi thực hiện I / O vì các cam kết không cần thiết có thể ảnh hưởng đến hiệu suất.



10

Nếu bạn sử dụng Qt và endl, bạn có thể vô tình sử dụng sai endl, điều này mang lại cho bạn kết quả rất đáng ngạc nhiên. Xem đoạn mã sau:

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

Lưu ý rằng tôi đã viết endlthay vì std::endl(điều này sẽ đúng) và rõ ràng có một endlhàm được định nghĩa trong qtextstream.h (là một phần của QtCore).

Sử dụng "\n"thay vì endlhoàn toàn bỏ qua bất kỳ vấn đề không gian tên tiềm năng nào. Đây cũng là một ví dụ tốt tại sao đưa các biểu tượng vào không gian tên toàn cầu (như Qt làm theo mặc định) là một ý tưởng tồi.


31
Ôi trời! Ai sẽ muốn trở thành using namespace std;?? :-)
Steve Folly 17/210

2
Bẩn thỉu. Cảm ơn vì nhận xét, tôi chắc chắn những người khác sẽ gặp phải điều đó.
Head Geek

@SteveFolly tôi làm. Tại sao không?
ʇolɐǝz qoq

@ olɐǝzǝɥʇqoq Không sao miễn là bạn không làm như vậy trong các tệp tiêu đề.
smerlin

1
@ olɐǝzǝɥʇqoq Hãy tránh using namespace std;. Nó được coi là thực hành xấu. Xem tại sao là sử dụng không gian tên std; coi hành vi xấu?
LF

2

Tôi luôn có thói quen chỉ sử dụng std :: endl vì tôi rất dễ nhìn thấy.


2

Các std::endlthao tác tương đương với '\n'. Nhưng std::endlluôn tuôn ra dòng suối.

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

1

Nếu bạn có ý định chạy chương trình của mình trên bất kỳ thứ gì khác ngoài máy tính xách tay của riêng bạn, đừng bao giờ sử dụng endlcâu lệnh. Đặc biệt là nếu bạn đang viết rất nhiều dòng ngắn hoặc như tôi thường thấy các ký tự đơn vào một tệp. Việc sử dụng endllà biết để tiêu diệt các hệ thống tệp được nối mạng như NFS.


Đó có phải là do sự bốc hỏa? Tôi có thể thấy làm thế nào nó có thể có thể.
Head Geek

@Head Thật vậy. Tôi cũng đã thấy nó phá hỏng hiệu suất IO của đĩa.
sbi

0

Với tham chiếu Đây là một trình thao tác I / O chỉ đầu ra .

std::endlChèn một ký tự dòng mới vào os trình tự đầu ra và xóa nó như thể bằng cách gọi os.put(os.widen('\n'))theo sau os.flush().

Khi nào nên sử dụng:

Trình thao tác này có thể được sử dụng để tạo ra một dòng đầu ra ngay lập tức ,

ví dụ

khi hiển thị đầu ra từ một quy trình chạy dài, hoạt động ghi nhật ký của nhiều luồng hoặc hoạt động ghi nhật ký của chương trình có thể bị sập bất ngờ.

Cũng thế

Một dòng rõ ràng của std :: cout cũng là cần thiết trước khi gọi đến std :: system, nếu quá trình sinh ra thực hiện bất kỳ I / O màn hình nào. Trong hầu hết các kịch bản I / O tương tác thông thường khác, std :: endl là không cần thiết khi được sử dụng với std :: cout vì bất kỳ đầu vào nào từ std :: cin, đầu ra đến std :: cerr hoặc chấm dứt chương trình đều buộc một lệnh gọi đến std :: cout .tuôn ra(). Việc sử dụng std :: endl thay cho '\ n', được khuyến khích bởi một số nguồn, có thể làm giảm đáng kể hiệu suất đầu ra.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.