C ++:“ std :: endl” กับ“ \ n”


569

หนังสือ C ++ หลายเล่มมีรหัสตัวอย่างเช่นนี้ ...

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

... ดังนั้นฉันก็ทำเช่นนั้นเสมอ แต่ฉันเห็นรหัสจำนวนมากจากนักพัฒนาที่ทำงานเช่นนี้แทน:

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

มีเหตุผลทางเทคนิคหรือไม่ที่จะเลือกอย่างใดอย่างหนึ่งหรือเป็นเรื่องของรูปแบบการเข้ารหัสหรือไม่?


7
คำอธิบายที่ดี: cppkid.wordpress.com/2008/08/27/why-i-prefer-n-to-stdendl

1
เป็นไปได้ที่ซ้ำกันของ"\ n" หรือ '\ n' หรือ std :: endl เป็น std :: cout?
Derobert

25
@derobert อันนี้เก่ากว่าที่อื่น
Kira

3
@HediNaily แน่นอนมันเป็น แต่คำตอบอีกอันทำให้ฉันดีขึ้นเล็กน้อยดังนั้นฉันเลือกที่จะทำแบบนั้น นอกจากนี้ยังมีคนอื่น ๆ '\n'เป็นวงกว้างออกไปเล็กน้อยยังครอบคลุม
Derobert

stackoverflow.com/a/30968225/3163618อาจมีความแตกต่างด้านประสิทธิภาพที่สำคัญ
qwr

คำตอบ:


473

อักขระสิ้นสุดบรรทัดแตกต่างกันไม่สำคัญสมมติว่าไฟล์เปิดอยู่ในโหมดข้อความซึ่งเป็นสิ่งที่คุณได้รับเว้นแต่คุณจะขอไบนารี โปรแกรมที่คอมไพล์แล้วจะเขียนสิ่งที่ถูกต้องสำหรับระบบที่คอมไพล์แล้ว

ข้อแตกต่างเพียงอย่างเดียวก็คือลบstd::endlบัฟเฟอร์เอาต์พุตและ'\n'ไม่ '\n'หากคุณไม่ต้องการบัฟเฟอร์ล้างบ่อยการใช้งาน ถ้าคุณทำ (ตัวอย่างเช่นถ้าคุณต้องการที่จะได้รับการส่งออกทั้งหมดและโปรแกรมไม่เสถียร), std::endlการใช้งาน


24
หรือลองพิจารณาใช้::std::cerrแทน::std::coutเนื่องจากมันไม่มีข้อผิดพลาดและถูกฟลัชด้วยการดำเนินการเอาต์พุตทุกครั้ง
Omnifarious

142
@Omnifarious: ไม่ควร std :: cerr สำหรับข้อผิดพลาด สตรีมทั้งสองไม่ได้ซิงค์กันดังนั้นหากคุณส่งข้อความออกไปยัง cout ข้อความนั้นอาจถูกบัฟเฟอร์และ cerr จะตรงไปยังเอาต์พุตนี้ทำให้เกิดการแสดงผลแบบผสม ใช้ cerr สำหรับสิ่งที่มันควรจะเป็นสำหรับ (ข้อผิดพลาด) และ cout สำหรับสิ่งที่มันถูกออกแบบมาสำหรับ (ปฏิสัมพันธ์ปกติ)
Martin York

23
@Lucas: ไม่เกิน '\ n' คือแพลตฟอร์มที่รับรู้
CB Bailey

32
@ LokiAstari: ฉันจะไม่พูดว่าstderrเป็น "ข้อผิดพลาด" แต่เป็นของสำหรับข้อความวินิจฉัยนอกวงถ้าคุณต้องการ มันควรจะเป็นไปได้ที่จะพูด./prog > fileและเก็บเฉพาะส่วนของโปรแกรมที่แท้จริง แต่โปรแกรมอาจต้องการส่งออกข้อมูลสถานะมากขึ้นแม้ในการโต้ตอบปกติ
Kerrek SB

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นั้นแน่นอนว่าการล้างข้อมูลมีประโยชน์
Chris Jester-Young

9
@LokiAstari: หากคุณใช้ตัวดำเนินการ << คุณอาจไม่กังวลเกี่ยวกับประสิทธิภาพ - ทำไมล่ะ ฉันไม่รู้ว่านั่นoperator<<ไม่ใช่นักแสดงหรือเป็นทางเลือกอื่นในการใช้เพื่อการแสดง โปรดชี้นำฉันไปยังเนื้อหาบางส่วนเพื่อทำความเข้าใจเพิ่มเติม
ตำนาน 2k

8
@ legends2k: มีภรรยาเก่าเรื่องหนึ่งที่สตรีม C ++ ไม่เป็นนักแสดงเหมือน C printf () แม้ว่าความจริงจะมีขอบเขต แต่ความแตกต่างหลักในเรื่องความเร็วนั้นเกิดจากผู้ใช้สตรีม C ++ อย่างไม่ถูกต้อง stackoverflow.com/a/1042121/14065ใน C ++ โปรดจำไว้ว่าให้ยกเลิกการซิงค์ iostreams ด้วย C-streams sync_with_stdio(false)และอย่าล้างเอาต์พุตของคุณอย่างต่อเนื่อง ปล่อยให้ห้องสมุดทำงานเมื่อต้องทำ stackoverflow.com/a/1926432/14065
Martin York

6
@ โลกิ: มีตำนานเมืองที่sync_with_stdioทำให้ iostreams เร็วเท่ากับ stdio มันไม่ได้
Ben Voigt

2
@ BenVoigt: ฉันระวังข้อความข้างบน (ดังนั้นฉันมีความสุขกับพวกเขา) มันไม่ได้เป็นนักแสดงเช่นเดียวกับ stdio (เพราะมันมากกว่า) แต่ช่องว่างประสิทธิภาพจำนวนมากที่ผู้คนบ่นเกี่ยวกับสาเหตุมาจากการซิงค์กับ stdio
Martin York

40

อาจมีปัญหาประสิทธิภาพการทำงานstd::endlบังคับให้ล้างออกสตรีมเอาต์พุต


1
และสามารถทำการประมวลผลอื่น ๆ ที่ระบบโลคัลต้องการเพื่อให้ทำงานได้ดี
dmckee --- ผู้ดูแลอดีตลูกแมว

30

มีการเรียกใช้ฟังก์ชันอื่นโดยนัยถ้าคุณใช้ std::endl

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

ก) เรียกผู้ประกอบการ<<หนึ่งครั้ง
b) เรียกผู้ควบคุม<<สองครั้ง


19
อาจเห็นได้ชัด แต่มีผลกระทบอย่างมากต่อโปรแกรมเธรดโดยทั่วไปเวอร์ชันแรกจะเขียนบรรทัดเดียวในช็อตเดียวโดยที่เวอร์ชันที่สองอาจแยกจากการเขียนจากเธรดอื่น บ่อยครั้งที่ฉันพบว่าตัวเองกำลังเขียน std :: cout << "hello \ n" << std :: flush เพื่อหลีกเลี่ยงปัญหานี้
smparkes

เกี่ยวกับstd::cout << "Hello" << "\n";อะไร
byxor

1
@byxor เกือบเหมือนกันยกเว้นการล้างบัฟเฟอร์ตามที่อธิบายไว้ในคำตอบอื่น ๆ อย่างไรก็ตามมันซ้ำซ้อนเมื่อคุณสามารถรวมสองตัวอักษรสตริงเป็นหนึ่ง
iBug

ถ้าสายที่จะพิมพ์ไม่ใช่ตัวอักษรการเรียกไปที่<<จะเป็น 2 ในกรณีaเช่นกันดังนั้นฉันจะไม่เรียกร้องความต้องการหนึ่งหรือสอง<<(หรือสองฟังก์ชันทั่วไป) เป็น ความแตกต่างระหว่างและ\n endl
Enrico Maria De Angelis

ไม่เลยนั่นไม่ใช่เหตุผลที่ฉันใช้ \ n
Carlo Wood

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 ++ โทษในมาตรฐาน


19

พวกเขาทั้งสองจะเขียนอักขระสิ้นสุดบรรทัดที่เหมาะสม นอกจาก endl นั้นจะทำให้บัฟเฟอร์ถูกคอมมิต โดยปกติคุณไม่ต้องการใช้ endl เมื่อทำไฟล์ I / O เนื่องจากการกระทำที่ไม่จำเป็นอาจส่งผลกระทบต่อประสิทธิภาพการทำงาน



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
Urgh! ใครที่อยากจะเป็นusing namespace std;?? :-)
Steve Folly

2
น่ารังเกียจ ขอบคุณสำหรับความคิดเห็นฉันแน่ใจว่าคนอื่นจะเจอ
Head Geek

@SteveFolly ฉันทำ ทำไมจะไม่ล่ะ?
ʇolɐǝzǝɥʇ qoq

@ ʇolɐǝzǝɥʇqoqมันก็โอเคตราบใดที่คุณไม่ทำในไฟล์ส่วนหัว
smerlin

1
@ using namespace std;ʇolɐǝzǝɥʇqoqกรุณาหลีกเลี่ยง ถือว่าเป็นการปฏิบัติที่ไม่ดี ดูเหตุใดจึง“ ใช้ namespace 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

ด้วยการอ้างอิงนี่คือเครื่องมือจัดการ I / O เอาต์พุตเท่านั้น

std::endlแทรกตัวอักษรขึ้นบรรทัดใหม่เข้าไปในระบบปฏิบัติการลำดับส่งออกและวูบวาบมันราวกับว่าโดยการเรียกตามos.put(os.widen('\n'))os.flush()

ควรใช้เมื่อใด:

หุ่นยนต์นี้อาจถูกนำมาใช้ในการผลิตเส้นของการส่งออกในทันที ,

เช่น

เมื่อแสดงเอาต์พุตจากกระบวนการที่รันนานกิจกรรมการบันทึกของหลายเธรดหรือกิจกรรมการบันทึกของโปรแกรมที่อาจล้มเหลวโดยไม่คาดคิด

ด้วย

การล้างข้อมูล std :: cout อย่างชัดเจนจำเป็นต้องมีก่อนการเรียกไปยังระบบ std :: หากกระบวนการที่เกิดขึ้นนั้นดำเนินการหน้าจอ I / O ใด ๆ ในสถานการณ์จำลอง I / O เชิงโต้ตอบอื่น ๆ ส่วนใหญ่ std :: endl จะซ้ำซ้อนเมื่อใช้กับ std :: cout เนื่องจากอินพุตจาก std :: cin ใด ๆ เอาต์พุตไปยัง std :: cerr หรือการยุติโปรแกรมบังคับให้โทรไปยัง std :: cout .flush () การใช้ std :: endl แทนที่ '\ n' ซึ่งได้รับการสนับสนุนจากบางแหล่งอาจลดประสิทธิภาพของเอาต์พุต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.