'printf' กับ 'cout' ใน C ++


คำตอบ:


332

ฉันประหลาดใจที่ทุกคนในคำถามนี้อ้างว่าstd::coutเป็นวิธีที่ดีกว่าprintfแม้ว่าคำถามจะถามถึงความแตกต่างก็ตาม ตอนนี้มีความแตกต่าง - std::coutคือ C ++ และprintfเป็น C ( แต่คุณสามารถใช้มันใน C ++, เช่นเดียวเกือบสิ่งอื่นจาก C) ตอนนี้ฉันจะซื่อสัตย์ที่นี่; ทั้งสองprintfและstd::coutมีข้อได้เปรียบ

ความแตกต่างที่แท้จริง

ขยาย

std::coutสามารถขยายได้ ฉันรู้ว่าผู้คนจะพูดว่าprintfสามารถขยายได้เช่นกัน แต่ส่วนขยายดังกล่าวไม่ได้กล่าวถึงในมาตรฐาน C (ดังนั้นคุณจะต้องใช้คุณสมบัติที่ไม่ได้มาตรฐาน - แต่ไม่มีแม้แต่คุณสมบัติที่ไม่ได้มาตรฐานทั่วไป) และส่วนขยายดังกล่าวเป็นตัวอักษรหนึ่งตัว (ดังนั้นจึงเป็นเรื่องง่ายที่จะขัดแย้งกับรูปแบบที่มีอยู่แล้ว)

ซึ่งแตกต่างจากprintf, std::coutขึ้นอยู่อย่างสมบูรณ์ในการดำเนินงานมากจึงมีปัญหากับรูปแบบที่กำหนดเองไม่ - สิ่งที่คุณทำคือการกำหนดสละย่อยstd::ostreamเป็นอาร์กิวเมนต์แรกและชนิดของคุณเป็นที่สอง ดังนั้นจึงไม่มีปัญหาเกี่ยวกับเนมสเปซ - ตราบใดที่คุณมีคลาส (ซึ่งไม่ จำกัด เพียงหนึ่งตัวอักษร) คุณสามารถทำงานstd::ostreamหนักเกินพิกัดได้

อย่างไรก็ตามฉันสงสัยว่าหลายคนต้องการที่จะขยายostream(พูดตามตรงฉันไม่ค่อยเห็นส่วนขยายดังกล่าวแม้ว่าพวกเขาจะทำง่าย) อย่างไรก็ตามมันอยู่ที่นี่ถ้าคุณต้องการ

วากยสัมพันธ์

เนื่องจากสังเกตได้ง่ายทั้งคู่printfและstd::coutใช้ไวยากรณ์ที่แตกต่างกัน printfใช้ไวยากรณ์ของฟังก์ชันมาตรฐานโดยใช้สตริงรูปแบบและรายการอาร์กิวเมนต์ที่มีความยาวผันแปรได้ ที่จริงแล้วprintfเป็นเหตุผลว่าทำไม C ถึงมีprintfรูปแบบนั้นซับซ้อนเกินกว่าจะใช้งานได้หากไม่มี อย่างไรก็ตามstd::coutใช้ API อื่น - operator <<API ที่ส่งคืนตัวเอง

โดยทั่วไปนั่นหมายถึงเวอร์ชั่น C จะสั้นลง แต่ในกรณีส่วนใหญ่จะไม่สำคัญ ความแตกต่างจะสังเกตได้เมื่อคุณพิมพ์อาร์กิวเมนต์จำนวนมาก หากคุณต้องเขียนบางอย่างเช่นError 2: File not found.สมมติว่าหมายเลขข้อผิดพลาดและคำอธิบายเป็นตัวยึดรหัสจะมีลักษณะเช่นนี้ ตัวอย่างทั้งสองทำงานได้เหมือนกัน ( std::endlจริง ๆ แล้วเรียงลำดับแล้วล้างบัฟเฟอร์จริง)

printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;

แม้ว่าสิ่งนี้จะไม่ปรากฏว่ามันบ้าเกินไป (มันยาวกว่าสองเท่า) แต่สิ่งต่าง ๆ จะบ้าคลั่งมากขึ้นเมื่อคุณจัดรูปแบบการโต้แย้งแทนที่จะพิมพ์ออกมา ตัวอย่างเช่นการพิมพ์สิ่งที่ชอบ0x0424เป็นเพียงแค่บ้า สิ่งนี้เกิดจากstd::coutการผสมสถานะและค่าจริง ฉันไม่เคยเห็นภาษาที่มีบางสิ่งที่เหมือนstd::setfillเป็นประเภท (นอกเหนือจาก C ++) printfแยกข้อโต้แย้งและประเภทที่ชัดเจนอย่างชัดเจน ฉันอยากจะรักษาprintfเวอร์ชันของมัน (แม้ว่ามันจะดูเป็นความลับ) เมื่อเทียบกับiostreamเวอร์ชั่นของมัน (เพราะมันมีเสียงรบกวนมากเกินไป)

printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;

การแปล

นี่คือข้อดีที่แท้จริงของการprintfโกหก printfสตริงรูปแบบเป็นอย่างดี ... สตริง ที่ทำให้มันเป็นเรื่องง่ายที่จะแปลเมื่อเทียบกับการละเมิดของoperator << iostreamสมมติว่าgettext()ฟังก์ชันแปลและคุณต้องการแสดงError 2: File not found.รหัสเพื่อรับการแปลของสตริงรูปแบบที่แสดงก่อนหน้านี้จะมีลักษณะดังนี้:

printf(gettext("Error %d: %s.\n"), id, errors[id]);

ตอนนี้สมมติว่าเราแปลเป็น Fictionish โดยที่หมายเลขข้อผิดพลาดอยู่หลังคำอธิบาย %2$s oru %1$d.\nสตริงแปลจะมีลักษณะ ตอนนี้จะทำอย่างไรใน C ++? ฉันไม่มีความคิด ฉันเดาว่าคุณสามารถสร้างของปลอมiostreamที่สร้างขึ้นprintfซึ่งคุณสามารถส่งต่อgettextหรือเพื่ออะไรก็ได้ แน่นอนว่า$ไม่ใช่มาตรฐาน C แต่เป็นเรื่องธรรมดาที่จะปลอดภัยในการใช้งานในความคิดของฉัน

ไม่จำเป็นต้องจำ / ค้นหาชนิดไวยากรณ์ของจำนวนเต็มเฉพาะ

C มีจำนวนเต็มหลายประเภทและ C ++ ก็เช่นกัน std::coutจัดการทุกประเภทสำหรับคุณในขณะที่printfต้องใช้ไวยากรณ์ที่เฉพาะเจาะจงขึ้นอยู่กับประเภทจำนวนเต็ม (มีประเภทที่ไม่ใช่จำนวนเต็ม แต่ประเภทที่ไม่ใช่จำนวนเต็มเท่านั้นที่คุณจะใช้ในทางปฏิบัติด้วยprintfคือconst char *(สตริง C สามารถได้รับโดยใช้to_cวิธีการstd::string)) ยกตัวอย่างเช่นในการพิมพ์size_tที่คุณจำเป็นต้องใช้%zdในขณะที่จะต้องใช้int64_t %"PRId64"ตารางที่มีอยู่ที่http://en.cppreference.com/w/cpp/io/c/fprintfและhttp://en.cppreference.com/w/cpp/types/integer

คุณไม่สามารถพิมพ์ NUL ไบต์ได้ \0

เพราะprintfสตริงใช้ C เมื่อเทียบกับ C ++ สายก็ไม่สามารถพิมพ์ไบต์ NUL โดยเทคนิคเฉพาะ ในบางกรณีก็เป็นไปได้ที่จะใช้%cกับ'\0'เป็นอาร์กิวเมนต์ แต่ที่เห็นได้ชัดสับ

ความแตกต่างที่ไม่มีใครใส่ใจ

ประสิทธิภาพ

อัปเดต: ปรากฎว่าiostreamช้ามากซึ่งปกติแล้วจะช้ากว่าฮาร์ดไดรฟ์ของคุณ (หากคุณเปลี่ยนเส้นทางโปรแกรมเป็นไฟล์) การปิดใช้งานการซิงโครไนซ์ด้วยstdioอาจช่วยได้หากคุณต้องการส่งออกข้อมูลจำนวนมาก หากผลการดำเนินงานที่เป็นความกังวลที่แท้จริง (เมื่อเทียบกับการเขียนหลายบรรทัดเพื่อ STDOUT) printfใช้เพียง

ทุกคนคิดว่าพวกเขาสนใจเรื่องการแสดง แต่ไม่มีใครมารบกวนการวัด คำตอบของฉันคือ I / O เป็นคอขวดอยู่แล้วไม่ว่าถ้าคุณใช้หรือprintf iostreamฉันคิดว่าprintf อาจเร็วขึ้นจากการดูอย่างรวดเร็วในการชุมนุม (รวบรวมด้วยเสียงดังกราวโดยใช้-O3ตัวเลือกคอมไพเลอร์) สมมติว่าตัวอย่างข้อผิดพลาดของฉันprintfตัวอย่างใช้วิธีการโทรน้อยกว่าcoutตัวอย่าง นี่คือint mainกับprintf:

main:                                   @ @main
@ BB#0:
        push    {lr}
        ldr     r0, .LCPI0_0
        ldr     r2, .LCPI0_1
        mov     r1, #2
        bl      printf
        mov     r0, #0
        pop     {lr}
        mov     pc, lr
        .align  2
@ BB#1:

คุณสามารถสังเกตเห็นได้อย่างง่ายดายว่าสองสายและ2(จำนวน) ถูกผลักเป็นprintfข้อโต้แย้ง เกี่ยวกับมัน; ไม่มีอะไรอื่นอีกแล้ว สำหรับการเปรียบเทียบนี้จะiostreamรวบรวมเพื่อประกอบ ไม่ไม่มีการอินไลน์ ทุกการoperator <<โทรครั้งเดียวหมายถึงการโทรอีกครั้งที่มีชุดของการขัดแย้ง

main:                                   @ @main
@ BB#0:
        push    {r4, r5, lr}
        ldr     r4, .LCPI0_0
        ldr     r1, .LCPI0_1
        mov     r2, #6
        mov     r3, #0
        mov     r0, r4
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        mov     r0, r4
        mov     r1, #2
        bl      _ZNSolsEi
        ldr     r1, .LCPI0_2
        mov     r2, #2
        mov     r3, #0
        mov     r4, r0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r1, .LCPI0_3
        mov     r0, r4
        mov     r2, #14
        mov     r3, #0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r1, .LCPI0_4
        mov     r0, r4
        mov     r2, #1
        mov     r3, #0
        bl      _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        ldr     r0, [r4]
        sub     r0, r0, #24
        ldr     r0, [r0]
        add     r0, r0, r4
        ldr     r5, [r0, #240]
        cmp     r5, #0
        beq     .LBB0_5
@ BB#1:                                 @ %_ZSt13__check_facetISt5ctypeIcEERKT_PS3_.exit
        ldrb    r0, [r5, #28]
        cmp     r0, #0
        beq     .LBB0_3
@ BB#2:
        ldrb    r0, [r5, #39]
        b       .LBB0_4
.LBB0_3:
        mov     r0, r5
        bl      _ZNKSt5ctypeIcE13_M_widen_initEv
        ldr     r0, [r5]
        mov     r1, #10
        ldr     r2, [r0, #24]
        mov     r0, r5
        mov     lr, pc
        mov     pc, r2
.LBB0_4:                                @ %_ZNKSt5ctypeIcE5widenEc.exit
        lsl     r0, r0, #24
        asr     r1, r0, #24
        mov     r0, r4
        bl      _ZNSo3putEc
        bl      _ZNSo5flushEv
        mov     r0, #0
        pop     {r4, r5, lr}
        mov     pc, lr
.LBB0_5:
        bl      _ZSt16__throw_bad_castv
        .align  2
@ BB#6:

อย่างไรก็ตามความจริงแล้วสิ่งนี้ไม่ได้หมายความว่าอะไรเพราะ I / O เป็นคอขวดอยู่แล้ว ฉันแค่อยากแสดงให้เห็นว่าiostreamไม่เร็วขึ้นเพราะมันเป็น "ประเภทที่ปลอดภัย" ส่วนใหญ่การใช้งาน C ใช้printfรูปแบบการใช้ข้ามไปคำนวณเพื่อให้printfเป็นให้เร็วที่สุดเท่าที่จะสามารถได้โดยไม่ต้องคอมไพเลอร์จะรับรู้printf(ไม่ว่าพวกเขาจะไม่ได้ - คอมไพเลอร์บางส่วนสามารถเพิ่มประสิทธิภาพprintfในบางกรณี - สตริงคงที่ลงท้ายด้วย\nมักจะมีการเพิ่มประสิทธิภาพในการputs) .

มรดก

ฉันไม่รู้ว่าทำไมคุณถึงอยากสืบทอดostreamแต่ฉันไม่สนใจ เป็นไปได้ด้วยFILEเช่นกัน

class MyFile : public FILE {}

ประเภทความปลอดภัย

รายการอาร์กิวเมนต์ความยาวตัวแปรจริงมีความปลอดภัย แต่นั่นไม่สำคัญเนื่องจากคอมไพเลอร์ C ยอดนิยมสามารถตรวจพบปัญหากับprintfสตริงรูปแบบหากคุณเปิดใช้งานคำเตือน ในความเป็นจริงเสียงดังกราวสามารถทำได้โดยไม่เปิดใช้งานการเตือน

$ cat safety.c

#include <stdio.h>

int main(void) {
    printf("String: %s\n", 42);
    return 0;
}

$ clang safety.c

safety.c:4:28: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
    printf("String: %s\n", 42);
                    ~~     ^~
                    %d
1 warning generated.
$ gcc -Wall safety.c
safety.c: In function main’:
safety.c:4:5: warning: format ‘%s expects argument of type char *’, but argument 2 has type int [-Wformat=]
     printf("String: %s\n", 42);
     ^

18
คุณบอกว่า I / O เป็นคอขวดอยู่แล้ว เห็นได้ชัดว่าคุณไม่เคยทดสอบสมมติฐาน ฉันอ้างตัวเองว่า: "ในทางกลับกันรุ่น iostreams ที่ 75.3 MB / s ไม่สามารถบัฟเฟอร์ข้อมูลได้เร็วพอที่จะเก็บไว้ในฮาร์ดดิสก์นั่นแย่มากและมันยังไม่ได้ทำงานจริงเลยฉันไม่ได้ทำ ไม่คิดว่าฉันมีความคาดหวังสูงเกินไปเมื่อฉันพูดว่าไลบรารี I / O ของฉันควรจะสามารถทำให้ดิสก์คอนโทรลเลอร์ของฉันอิ่มตัวได้ "
Ben Voigt

4
@BenVoigt: ฉันยอมรับฉันพยายามหลีกเลี่ยง C ++ เมื่อเป็นไปได้ ฉันลองใช้มันเยอะ แต่มันน่ารำคาญกว่าและบำรุงรักษาได้น้อยกว่าภาษาการเขียนโปรแกรมอื่นที่ฉันใช้ นี่คืออีกเหตุผลสำหรับผมที่จะหลีกเลี่ยงการ c ++ อีก - นี่คือไม่ได้อย่างรวดเร็ว (มันไม่ได้ iostream - ทั้งไลบรารี c ++ ช้าในการใช้งานมากที่สุดอาจจะมีข้อยกเว้นสำหรับstd::sortซึ่งเป็นอย่างใดอย่างแปลกใจอย่างรวดเร็วเมื่อเทียบกับqsort(2 ครั้ง) ที่ ค่าใช้จ่ายของขนาดที่สามารถใช้งานได้)
Konrad Borowski

3
ไม่มีใครพูดถึงปัญหาในสภาพแวดล้อมแบบขนานเมื่อใช้ cout
Nicholas Hamilton

9
อาร์กิวเมนต์ประสิทธิภาพของคุณไม่สมเหตุสมผล การประกอบเพิ่มเติมในโปรแกรมของคุณไม่ได้หมายความว่าโปรแกรมจะช้าลงเพราะคุณไม่ได้คิดรหัสทั้งหมดที่ทำให้ฟังก์ชั่น printf ซึ่งเป็นรหัสจำนวนมาก ในความคิดของฉันมันเป็นไปได้ที่จะเพิ่มประสิทธิภาพ cout ด้วยตัวดำเนินการ << ให้ดีกว่า printf มากเพราะคอมไพเลอร์สามารถรับรู้ถึงตัวแปรและการจัดรูปแบบที่ดีขึ้น
Ignas2526

18
ฉันชอบหลายสิ่งหลายอย่างเกี่ยวกับคำตอบนี้ แต่บางทีส่วนที่ฉันโปรดปรานก็คือ "ทุกคนคิดว่าพวกเขาสนใจเรื่องการแสดง แต่ไม่มีใครมารบกวนการวัด"
Kyle Strand

203

จากคำถามที่พบบ่อย C ++ :

[15.1] ทำไมฉันจึงควรใช้<iostream> แทนแบบดั้งเดิม<cstdio>?

เพิ่มความปลอดภัยประเภทลดข้อผิดพลาดอนุญาตให้มีการขยายและมอบความสามารถในการสืบทอด

printf()ไม่แตกหักง่ายและscanf()น่าอยู่แม้จะมีข้อผิดพลาดเกิดขึ้นได้อย่างไรก็ตามทั้งคู่มีข้อ จำกัด เกี่ยวกับสิ่งที่ C ++ I / O สามารถทำได้ C ++ I / O (ใช้<<และ>>) คือสัมพันธ์กับ C (ใช้printf()และscanf()):

  • ปลอดภัยมากขึ้นประเภท: ด้วย<iostream>ประเภทของวัตถุที่เป็น I / O'd เป็นที่รู้จักกันแบบคงที่โดยคอมไพเลอร์ ในทางตรงกันข้ามให้<cstdio>ใช้ฟิลด์ "%" เพื่อค้นหาประเภทต่างๆแบบไดนามิก
  • ข้อผิดพลาดได้ง่ายน้อยลง: ด้วย<iostream>ไม่มีโทเค็น "%" ซ้ำซ้อนที่ต้องสอดคล้องกับวัตถุจริงที่เป็น I / O'd การลบความซ้ำซ้อนจะลบคลาสของข้อผิดพลาด
  • Extensible: <iostream>กลไกC ++ อนุญาตให้ผู้ใช้กำหนดชนิดใหม่เป็น I / O'd โดยไม่ทำลายโค้ดที่มีอยู่ ลองนึกภาพความโกลาหลถ้าทุกคนเพิ่มเขตข้อมูล "%" ที่เข้ากันไม่ได้ไปที่ printf()และscanf()?!
  • ที่สืบทอด: c ++ <iostream>กลไกที่ถูกสร้างขึ้นจากชั้นเรียนจริงเช่นและstd::ostream std::istreamซึ่งแตกต่างจาก<cstdio>ของ FILE*เหล่านี้เป็นชั้นเรียนจริงและสืบทอดได้ดังนั้น ซึ่งหมายความว่าคุณสามารถมีสิ่งที่ผู้ใช้กำหนดเองที่มีลักษณะและทำตัวเหมือนลำธาร แต่ก็ทำสิ่งที่แปลกและมหัศจรรย์ที่คุณต้องการ คุณจะได้รับการใช้ zillions ของบรรทัดของรหัส I / O ที่เขียนโดยผู้ใช้ที่คุณไม่รู้จักและพวกเขาไม่จำเป็นต้องรู้เกี่ยวกับคลาส "สตรีมแบบขยาย" ของคุณ

บนมืออื่น ๆ ที่printfเป็นอย่างเร็วซึ่งอาจแสดงให้เห็นถึงการใช้มันในการตั้งค่าcoutในมากกรณีที่เฉพาะเจาะจงและ จำกัด ควรทำการโพรไฟล์ก่อนเสมอ (ดูตัวอย่างเช่นhttp://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)


2
ในทางกลับกันก็มีห้องสมุด FastFormat ( fastformat.org ) ที่ให้ความปลอดภัยในการพิมพ์การแสดงออกและประสิทธิภาพในเวลาเดียวกัน (ไม่ใช่ว่าฉันได้ลองแล้ว ... )
xtofl

3
@Marcelo อาจเป็นเพราะมันเป็นบทสรุปที่ดีมีทุกอย่างที่อ้างถึง การจัดรูปแบบ ... ใช่แล้วมันค่อนข้างแย่ ฉันควรจะแก้ไขด้วยตัวเอง แต่ดูเหมือนว่าคนอื่น ๆ (รวมตัวคุณ) ดูแลมันซึ่งแน่นอนว่าสร้างสรรค์กว่าการส่งเสียงครวญคราง
Mikeage

2
เมื่อถึงปลายปีprintf()ก็ควรที่จะยืดออก ดู "printf hooks" ที่udrepper.livejournal.com/20948.html
Maxim Egorushkin

4
@ MaximYegorushkin: Standard printfไม่มีความสามารถดังกล่าว กลไกไลบรารี่แบบไม่พกพานั้นแทบจะไม่อยู่ในระดับเดียวกับการขยายความสามารถของ iostreams ที่ได้มาตรฐานอย่างสมบูรณ์
Ben Voigt

4
"ในทางตรงกันข้าม printf เร็วขึ้นอย่างมาก" printf นั้นสะอาดและใช้งานง่ายกว่าซึ่งก็เป็นเหตุผลว่าทำไมฉันจึงหลีกเลี่ยง cout เมื่อเป็นไปได้
FluorescentGreen5

43

คนมักจะอ้างว่าprintfเร็วกว่ามาก นี่คือตำนานส่วนใหญ่ ฉันเพิ่งทดสอบโดยมีผลลัพธ์ต่อไปนี้:

cout with only endl                     1461.310252 ms
cout with only '\n'                      343.080217 ms
printf with only '\n'                     90.295948 ms
cout with string constant and endl      1892.975381 ms
cout with string constant and '\n'       416.123446 ms
printf with string constant and '\n'     472.073070 ms
cout with some stuff and endl           3496.489748 ms
cout with some stuff and '\n'           2638.272046 ms
printf with some stuff and '\n'         2520.318314 ms

สรุป: หากคุณต้องการเพียงแค่บรรทัดใหม่ให้ใช้printf; ไม่อย่างนั้นcoutก็เกือบจะเร็วหรือเร็วกว่า รายละเอียดเพิ่มเติมสามารถพบได้บนบล็อกของฉัน

ต้องมีความชัดเจนฉันไม่ได้พยายามที่จะบอกว่าiostreams มักจะดีกว่าprintf; ฉันแค่พยายามจะบอกว่าคุณควรทำการตัดสินใจอย่างชาญฉลาดบนพื้นฐานของข้อมูลจริงไม่ใช่การคาดเดาที่ฉับไวจากข้อสันนิษฐานที่เข้าใจผิดบางประการ

อัปเดต: นี่คือรหัสเต็มที่ฉันใช้สำหรับการทดสอบ รวบรวมg++โดยไม่มีตัวเลือกเพิ่มเติม (นอกเหนือจาก-lrtช่วงเวลา)

#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    timespec d_start;
    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            clock_gettime(CLOCK_REALTIME, &d_start);
        }
        ~TimedSection() {
            timespec end;
            clock_gettime(CLOCK_REALTIME, &end);
            double duration = 1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
            std::cerr << d_name << '\t' << std::fixed << duration << " ms\n"; 
        }
};

int main() {
    const int iters = 10000000;
    char const *text = "01234567890123456789";
    {
        TimedSection s("cout with only endl");
        for (int i = 0; i < iters; ++i)
            std::cout << std::endl;
    }
    {
        TimedSection s("cout with only '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << '\n';
    }
    {
        TimedSection s("printf with only '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("\n");
    }
    {
        TimedSection s("cout with string constant and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789" << std::endl;
    }
    {
        TimedSection s("cout with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789\n";
    }
    {
        TimedSection s("printf with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("01234567890123456789\n");
    }
    {
        TimedSection s("cout with some stuff and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << std::endl;
    }
    {
        TimedSection s("cout with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << '\n';
    }
    {
        TimedSection s("printf with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("%s01234567890123456789%i\n", text, i);
    }
}

5
ในคะแนนของคุณ printf เต้นได้อย่างง่ายดาย (กรณีส่วนใหญ่) ฉันสงสัยว่าทำไมคุณแนะนำให้ใช้ cout เมื่อมันมาถึงสมบูรณ์ แม้ว่าฉันจะเห็นด้วยกับความสมบูรณ์แบบนั้นก็ไม่ได้แตกต่างกันมากนักในกรณีที่เกิดขึ้นจริง ..
mishal153

3
@ mishal153: ฉันแค่พยายามจะบอกว่าประสิทธิภาพไม่แตกต่างกันดังนั้นคำแนะนำทั่วไปที่เคยได้ยินของ "ไม่เคยใช้ cout เพราะมันช้า waaay" เป็นคนโง่ธรรมดา โปรดทราบว่าศาลมีข้อได้เปรียบที่ชัดเจนของความปลอดภัยประเภทและมักจะอ่านได้เช่นกัน (การจัดรูปแบบจุดลอยตัวด้วย iostreams น่ากลัวมาก ... )
Thomas

35
ความแตกต่างที่สำคัญระหว่างprintf()และstd::ostreamนั่นคืออดีตเอาท์พุทขัดแย้งทั้งหมดในการเรียกหนึ่งเดียวในขณะที่เป็นกระบวนการที่ต้องโทรแยกต่างหากสำหรับแต่ละstd::ostream <<การทดสอบจะแสดงผลเพียงหนึ่งอาร์กิวเมนต์และขึ้นบรรทัดใหม่นั่นคือสาเหตุที่คุณไม่เห็นความแตกต่าง
Maxim Egorushkin

12
คอมไพเลอร์ควรสามารถอินไลน์การเรียกเหล่านี้ นอกจากนี้printfอาจทำการโทรจำนวนมากภายใต้หน้าปกเพื่อฟังก์ชั่นช่วยเหลือสำหรับตัวระบุการจัดรูปแบบที่หลากหลาย ... นั่นหรือเป็นฟังก์ชันเสาหินมหึมา และอีกครั้งเนื่องจากการอินไลน์มันไม่ควรสร้างความแตกต่างในเรื่องความเร็วเลย
โทมัส

4
คุณหมดเวลาเทอร์มินัลของคุณ ใช้sprintfหรือfprintfและหรือstringstream fstream
Ben Voigt

41

และฉันพูด :

ในแง่ระดับสูงความแตกต่างที่สำคัญคือความปลอดภัยของประเภท (cstdio ไม่มี) ประสิทธิภาพ (การใช้งาน iostreams ส่วนใหญ่ช้ากว่า cstdio) และการเพิ่มความสามารถ (iostreams ช่วยให้เป้าหมายเอาต์พุตที่กำหนดเอง


โดยเฉพาะอย่างยิ่งในระบบยูนิกซ์ที่มี POSIX คุณจะไม่มีทางรู้ว่าขนาดของประเภทหนึ่งในประเภทใดมีขนาดใหญ่มากดังนั้นคุณจึงจำเป็นต้องใช้ casts จำนวนมากหรือเป็น 99% ของโปรแกรมที่คุณเพิ่งเสี่ยงกับ% d ใช้เวลานานก่อนที่% z จะมาพร้อมกับ C99 แต่สำหรับ time_t / off_t การแสวงหารูปแบบคำสั่งที่ถูกต้องจะดำเนินต่อ
Lothar

30

หนึ่งคือฟังก์ชั่นที่พิมพ์ไปยัง stdout อีกรายการหนึ่งคือวัตถุที่มีฟังก์ชั่นสมาชิกมากมายและโอเวอร์โหลดของการoperator<<พิมพ์นั้นไปยัง stdout มีความแตกต่างอีกมากมายที่ฉันสามารถระบุได้ แต่ฉันไม่แน่ใจว่าคุณเป็นใคร


12

สำหรับฉันความแตกต่างที่แท้จริงที่ทำให้ฉันไป 'cout' มากกว่า 'printf' คือ:

1) ผู้ประกอบการ<<สามารถโอเวอร์โหลดสำหรับชั้นเรียนของฉัน

2) กระแสเอาต์พุตสำหรับ cout สามารถเปลี่ยนเป็นไฟล์ได้อย่างง่ายดาย: (: copy copy :)

#include <iostream>
#include <fstream>
using namespace std;

int main ()
{
    cout << "This is sent to prompt" << endl;
    ofstream file;
    file.open ("test.txt");
    streambuf* sbuf = cout.rdbuf();
    cout.rdbuf(file.rdbuf());
    cout << "This is sent to file" << endl;
    cout.rdbuf(sbuf);
    cout << "This is also sent to prompt" << endl;
    return 0;
}

3) ฉันพบว่าศาลอ่านได้มากขึ้นโดยเฉพาะอย่างยิ่งเมื่อเรามีพารามิเตอร์มากมาย

หนึ่งในปัญหาที่เกิดขึ้นกับการcoutเป็นตัวเลือกการจัดรูปแบบ การจัดรูปแบบข้อมูล (ความแม่นยำ justificaton ฯลฯ ) ในprintfนั้นง่ายขึ้น


1
มันดีนะ. ฉันจะรู้ได้อย่างไรว่าไม่มีใครปรับเปลี่ยนโกลบอล cout ด้วยวิธีนี้ในบางเธรดห้องสมุดต่างประเทศ?
vp_arth

1
คุณสามารถเปลี่ยนprintfเป็นไฟล์ได้อย่างง่ายดายโดยแทนที่ด้วยfprintf...
CoffeeTableEspresso

5

สองจุดที่ไม่ได้กล่าวถึงที่นี่ฉันพบว่ามีนัยสำคัญ:

1) coutมีสัมภาระจำนวนมากหากคุณยังไม่ได้ใช้ STL printfมันจะเพิ่มกว่าสองเท่ารหัสมากที่จะไฟล์วัตถุของคุณเป็น นี่ก็เป็นจริงstringเช่นกันและนี่คือเหตุผลสำคัญที่ฉันมักจะใช้ไลบรารีสตริงของตัวเอง

2) coutใช้<<โอเปอเรเตอร์ที่โอเวอร์โหลดซึ่งฉันพบว่าโชคร้าย สิ่งนี้สามารถเพิ่มความสับสนหากคุณกำลังใช้<<โอเปอเรเตอร์เพื่อจุดประสงค์ที่ต้องการไว้ (เลื่อนไปทางซ้าย) โดยส่วนตัวฉันไม่ชอบที่จะโอเวอร์โหลดโอเปอเรเตอร์เพื่อสัมผัสกับการใช้

บรรทัดล่าง: ฉันจะใช้cout(และstring) ถ้าฉันใช้ STL แล้ว มิฉะนั้นฉันมักจะหลีกเลี่ยง


4

ด้วยพื้นฐานมันอาจไม่สำคัญว่าคุณใช้ ฉันบอกว่ามันจะมีประโยชน์เมื่อคุณต้องการส่งออกวัตถุที่ซับซ้อน

ตัวอย่างเช่นถ้าคุณมีชั้นเรียน

#include <iostream>
#include <cstdlib>

using namespace std;

class Something
{
public:
        Something(int x, int y, int z) : a(x), b(y), c(z) { }
        int a;
        int b;
        int c;

        friend ostream& operator<<(ostream&, const Something&);
};

ostream& operator<<(ostream& o, const Something& s)
{
        o << s.a << ", " << s.b << ", " << s.c;
        return o;
}

int main(void)
{
        Something s(3, 2, 1);

        // output with printf
        printf("%i, %i, %i\n", s.a, s.b, s.c);

        // output with cout
        cout << s << endl;

        return 0;
}

ตอนนี้สิ่งที่กล่าวมาข้างต้นอาจไม่ได้ยอดเยี่ยมเท่านี้ แต่สมมติว่าคุณต้องแสดงผลลัพธ์นี้ในหลาย ๆ ที่ในรหัสของคุณ ไม่เพียงแค่นั้นสมมติว่าคุณเพิ่มเขตข้อมูล "int d" ด้วย cout คุณจะต้องเปลี่ยนมันในที่เดียว อย่างไรก็ตามด้วย printf คุณต้องเปลี่ยนมันในหลาย ๆ ที่และไม่เพียงแค่นั้นคุณต้องเตือนตัวเองว่าควรจะพิมพ์ออกมาแบบไหน

ด้วยที่กล่าวไว้กับ cout คุณสามารถลดเวลาที่ใช้ในการบำรุงรักษาโค้ดของคุณได้มากและไม่เพียง แต่ถ้าคุณใช้วัตถุ "Something" ในแอปพลิเคชันใหม่คุณไม่ต้องกังวลเกี่ยวกับผลลัพธ์


นอกจากนี้เพื่อเพิ่มเกี่ยวกับสิ่งที่ประสิทธิภาพฉันจะบอกว่าคุณไม่ควรส่งออกอะไรเลยถ้าใบสมัครของคุณถูกสร้างขึ้นมาเพื่อประสิทธิภาพ เอาต์พุตประเภทใด ๆ ไปยัง std ค่อนข้างแพงและช้า ฉันพูดว่าคุณควรหลีกเลี่ยงและส่งออกเฉพาะเมื่อจำเป็นจริงๆเท่านั้น
Daniel

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

2

แน่นอนคุณสามารถเขียน "บางสิ่งบางอย่าง" ดีกว่าเพื่อให้การบำรุงรักษา:

#include <iostream>
#include <cstdlib>

using namespace std;

class Something
{
    public:
        Something(int x, int y, int z) : a(x), b(y), c(z) { }
        int a;
        int b;
        int c;

        friend ostream& operator<<(ostream&, const Something&);

        void print() const { printf("%i, %i, %i\n", a, b, c); }
};

ostream& operator<<(ostream& o, const Something& s)
{
    o << s.a << ", " << s.b << ", " << s.c;
    return o;
}

int main(void)
{
    Something s(3, 2, 1);

    // Output with printf
    s.print(); // Simple as well, isn't it?

    // Output with cout
    cout << s << endl;

    return 0;
}

และการทดสอบเพิ่มเติมเล็กน้อยของ cout เทียบกับ printf เพิ่มการทดสอบ 'double' หากใครต้องการทำการทดสอบเพิ่มเติม (Visual Studio 2008 รุ่นที่ปฏิบัติการได้):

#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    //timespec d_start;
    clock_t d_start;

    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            //clock_gettime(CLOCK_REALTIME, &d_start);
            d_start = clock();
        }
        ~TimedSection() {
            clock_t end;
            //clock_gettime(CLOCK_REALTIME, &end);
            end = clock();
            double duration = /*1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
                              */
                              (double) (end - d_start) / CLOCKS_PER_SEC;

            std::cerr << d_name << '\t' << std::fixed << duration * 1000.0 << " ms\n";
        }
};


int main() {
    const int iters = 1000000;
    char const *text = "01234567890123456789";
    {
        TimedSection s("cout with only endl");
        for (int i = 0; i < iters; ++i)
            std::cout << std::endl;
    }
    {
        TimedSection s("cout with only '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << '\n';
    }
    {
        TimedSection s("printf with only '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("\n");
    }
    {
        TimedSection s("cout with string constant and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789" << std::endl;
    }
    {
        TimedSection s("cout with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << "01234567890123456789\n";
    }
    {
        TimedSection s("printf with string constant and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("01234567890123456789\n");
    }
    {
        TimedSection s("cout with some stuff and endl");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << std::endl;
    }
    {
        TimedSection s("cout with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            std::cout << text << "01234567890123456789" << i << '\n';
    }
    {
        TimedSection s("printf with some stuff and '\\n'");
        for (int i = 0; i < iters; ++i)
            printf("%s01234567890123456789%i\n", text, i);
    }
    {
        TimedSection s("cout with formatted double (width & precision once)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;
        std::cout.width(8);
        for (int i = 0; i < iters; ++i)
            std::cout << text << 8.315 << i << '\n';
    }
    {
        TimedSection s("cout with formatted double (width & precision on each call)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;

        for (int i = 0; i < iters; ++i)
            { std::cout.width(8);
              std::cout.precision(3);
              std::cout << text << 8.315 << i << '\n';
            }
    }
    {
        TimedSection s("printf with formatted double");
        for (int i = 0; i < iters; ++i)
            printf("%8.3f%i\n", 8.315, i);
    }
}

ผลลัพธ์คือ:

cout with only endl    6453.000000 ms
cout with only '\n'    125.000000 ms
printf with only '\n'    156.000000 ms
cout with string constant and endl    6937.000000 ms
cout with string constant and '\n'    1391.000000 ms
printf with string constant and '\n'    3391.000000 ms
cout with some stuff and endl    9672.000000 ms
cout with some stuff and '\n'    7296.000000 ms
printf with some stuff and '\n'    12235.000000 ms
cout with formatted double (width & precision once)    7906.000000 ms
cout with formatted double (width & precision on each call)    9141.000000 ms
printf with formatted double    3312.000000 ms

ทำไมendlมีประสิทธิภาพน้อยกว่า'\n'มาก
Nicholas Hamilton

1
ฉันเชื่อว่าเป็นเพราะendlล้างบัฟเฟอร์และ\nไม่ถึงแม้ว่าฉันไม่แน่ใจว่านี่เป็นเหตุผลที่ชัดเจน
Caleb Xu

นี้ไม่ได้เป็น asnwer คำถามที่มันมากขึ้นเช่นคำตอบให้กับแดเนียลและโทมัส
Fabio พูดว่า Reinstate Monica

2

ฉันต้องการชี้ให้เห็นว่าถ้าคุณต้องการเล่นกับเธรดใน C ++ ถ้าคุณใช้coutคุณจะได้รับผลลัพธ์ที่น่าสนใจ

พิจารณารหัสนี้:

#include <string>
#include <iostream>
#include <thread>

using namespace std;

void task(int taskNum, string msg) {
    for (int i = 0; i < 5; ++i) {
        cout << "#" << taskNum << ": " << msg << endl;
    }
}

int main() {
    thread t1(task, 1, "AAA");
    thread t2(task, 2, "BBB");
    t1.join();
    t2.join();
    return 0;
}

// g++ ./thread.cpp -o thread.out -ansi -pedantic -pthread -std=c++0x

เอาท์พุทมาสับทั้งหมด มันสามารถให้ผลลัพธ์ที่แตกต่างเช่นกันลองดำเนินการหลายครั้ง:

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

คุณสามารถใช้ที่จะได้รับมันขวาหรือคุณสามารถใช้printfmutex

#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB

มีความสุข!


2
wtf threads ไม่ทำให้ถั่วออกไป ฉันเพิ่งทำซ้ำและพบทั้งxyzและABCในการส่งออก มีไม่ได้ mangling b / w เป็นABC ABABAB
Abhinav Gauniyal

1
ฉันไม่ทราบวิธีการcoutทำงานกับเธรด แต่ฉันรู้ว่ารหัสที่คุณกำลังแสดงนั้นไม่ใช่รหัสที่คุณใช้ในการรับเอาต์พุตเหล่านั้น รหัสของคุณผ่านสตริง"ABC"สำหรับหัวข้อที่ 1 และ"xyz"ด้าย 2 แต่แสดงให้เห็นว่าการส่งออกของคุณและAAA BBBโปรดแก้ไขเพราะตอนนี้มันสับสน
Fabio พูดว่า Reinstate Monica

1
cout<< "Hello";
printf("%s", "Hello"); 

ทั้งสองถูกใช้เพื่อพิมพ์ค่า พวกเขามีไวยากรณ์ที่แตกต่างอย่างสิ้นเชิง C ++ มีทั้งสองอย่าง C มีเพียง printf


19
... อะไร? คุณผสมบางสิ่งบางอย่าง?
xtofl

1
แก้ไขปัญหา -1 เนื่องจากต้องการการแก้ไขและคำตอบก็เป็นที่ต้องการอย่างมาก
Yacoby

3
ชื่อฟังก์ชันถูกย้อนกลับ: cout ถูกใช้กับไวยากรณ์ printf และ printf ถูกใช้กับไวยากรณ์ cout ไม่ควรได้รับการยอมรับ!
Mahmoud Al-Qudsi

2
และข้อเสียเปรียบหลักของศาลก็คือมันใช้โอเปอเรเตอร์ << ซึ่งมีการใช้งานในทางที่ผิดอย่างมากและน่าเกลียด :)
jalf

8
แม้ว่านี่จะเป็นคำตอบที่แน่นอนไม่ใช่คำตอบที่ดีที่สุด แต่ฉันไม่เข้าใจว่าสแคทแมนถูกลงโทษสำหรับคำตอบของเขาเพียงเพราะได้รับเลือกให้เป็นคำตอบที่ดีที่สุด xbit มีวิธีตอบ IMO ที่แย่กว่านั้น แต่มี -1 โหวต ฉันไม่ได้พูด xbit ควรจะลงลงมติใด ๆ เพิ่มเติม แต่ผมไม่เห็นว่ามันเป็นธรรมที่จะลงสแคทแมนคะแนนสำหรับความผิดพลาดของ OP อีกต่อไปกว่ามันจะต้องมี ...
เจส

1

ฉันอยากจะบอกว่าการขาดความสามารถในการขยายprintfนั้นไม่เป็นความจริงทั้งหมด:
ใน C มันเป็นความจริง แต่ใน C ไม่มีคลาสจริง
ใน C ++ เป็นไปได้ที่จะโอเวอร์โหลดโอเปอเรเตอร์คาสเตอร์ดังนั้นการโอเวอร์โหลดchar*โอเปอเรเตอร์และการใช้printfสิ่งนี้:

Foo bar;
...;
printf("%s",bar);

อาจเป็นไปได้ถ้า Foo ทำงานหนักเกินไปผู้ปฏิบัติงานที่ดี หรือถ้าคุณทำวิธีที่ดี กล่าวโดยย่อprintfคือสามารถขยายได้มากcoutสำหรับฉัน

อาร์กิวเมนต์ทางเทคนิคที่ฉันเห็นสำหรับ C ++ สตรีม (โดยทั่วไป ... ไม่เพียง แต่ cout) คือ:

  • Typesafety (และโดยวิธีถ้าฉันต้องการพิมพ์เดียวที่'\n'ฉันใช้putchar('\n')... ฉันจะไม่ใช้ระเบิดนิวเคลียร์เพื่อฆ่าแมลง)

  • ง่ายต่อการเรียนรู้ (ไม่มีพารามิเตอร์ "ซับซ้อน" เพื่อเรียนรู้เพียงใช้<<และ>>ตัวดำเนินการ)

  • ทำงานโดยกำเนิดด้วยstd::string(เพราะprintfมีstd::string::c_str()แต่เพื่อscanf?)

สำหรับprintfฉันเห็น:

  • การจัดรูปแบบที่ซับซ้อนง่ายขึ้นหรืออย่างน้อยสั้นกว่า (ในรูปแบบของอักขระ) อ่านได้มากขึ้นสำหรับฉัน (ฉันเดาเรื่อง)

  • ควบคุมสิ่งที่ฟังก์ชั่นได้ดีขึ้น (คืนจำนวนตัวอักษรที่เขียนและมีตัว%nจัดรูปแบบ: "ไม่มีสิ่งใดที่พิมพ์ออกมาอาร์กิวเมนต์ต้องเป็นตัวชี้ไปยัง int ที่มีการลงชื่อซึ่งจำนวนตัวอักษรที่เขียนจนถึงปัจจุบัน" (จากprintf - การอ้างอิง C ++ )

  • ความเป็นไปได้ในการดีบั๊ก ด้วยเหตุผลเดียวกับอาร์กิวเมนต์สุดท้าย

การตั้งค่าส่วนตัวของฉันไปที่ฟังก์ชั่นprintf(และscanf) ส่วนใหญ่เป็นเพราะฉันชอบเส้นสั้น ๆ และเพราะฉันไม่คิดว่าปัญหาการพิมพ์ข้อความในการพิมพ์เป็นเรื่องยากที่จะหลีกเลี่ยง สิ่งเดียวที่ฉันไม่พอใจกับฟังก์ชั่น C-style std::stringคือไม่รองรับ เราต้องผ่านchar*ก่อนที่จะให้มันprintf(ด้วยstd::string::c_str()ถ้าเราต้องการอ่าน แต่วิธีการเขียน?)


3
คอมไพเลอร์ไม่มีข้อมูลประเภทสำหรับฟังก์ชัน varargs ดังนั้นมันจะไม่แปลงพารามิเตอร์จริง (ยกเว้นการเลื่อนระดับอาร์กิวเมนต์เริ่มต้นเช่นการเลื่อนระดับมาตรฐานหนึ่งรายการ) ดู 5.2.2p7 การแปลงที่ผู้ใช้กำหนดเองchar*จะไม่ถูกนำมาใช้
Ben Voigt

แม้ว่ามันจะใช้งานได้ แต่มันก็ไม่ได้เป็นตัวอย่างของการเพิ่มความสามารถในการวิ่ง แต่เป็นแฮ็คที่ฉลาดที่จะให้ sprintf ตามที่คาดหวังและจะไม่สนใจปัญหาร้ายแรงบางอย่างเช่นที่char*ชีวิตและนานแค่ไหน การปลดเปลื้องโดยนัย
Marcelo Cantos

1

ความแตกต่างเพิ่มเติม: "printf" ส่งคืนค่าจำนวนเต็ม (เท่ากับจำนวนอักขระที่พิมพ์) และ "cout" จะไม่ส่งคืนสิ่งใด

และ.

cout << "y = " << 7; ไม่ใช่อะตอม

printf("%s = %d", "y", 7); เป็นอะตอม

ศาลดำเนินการพิมพ์ดีด printf ไม่ได้

ไม่มีสิ่งใดเทียบเท่ากับ iostream "% d"


3
coutไม่ส่งคืนสิ่งใดเนื่องจากเป็นวัตถุไม่ใช่ฟังก์ชัน operator<<จะคืนค่าบางอย่าง (โดยปกติเป็นตัวถูกดำเนินการด้านซ้าย แต่เป็นค่าเท็จหากมีข้อผิดพลาด) และprintfเรียกว่า "ปรมาณู" ในแง่ใด
Keith Thompson

9
มันเหมือนระเบิดปรมาณู printf("%s\n",7);
เสียงอึกทึก

@ ไม่มีเสียงรอทำไมแบ่งส่วนผิด? %sคือ ?
Abhinav Gauniyal

1
นั่นคือจุดของคำสั่ง 'ระเบิดปรมาณู' printf % sอาร์กิวเมนต์ต้องมีตัวชี้ที่ถูกต้องเพื่อสตริงยกเลิก ช่วงหน่วยความจำ '7' (ตัวชี้) ไม่ถูกต้อง ความผิดพลาดในการแบ่งส่วนอาจโชคดี ในบางระบบ '7' อาจพิมพ์ขยะจำนวนมากไปยังคอนโซลและคุณต้องดูเป็นเวลาหนึ่งวันก่อนที่โปรแกรมจะหยุด ในคำอื่น ๆ printfนี้เป็นสิ่งที่ไม่ดีเกี่ยวกับ เครื่องมือวิเคราะห์แบบคงที่สามารถตรวจจับปัญหาเหล่านี้ได้หลายอย่าง
เสียงอึกทึก

ในขณะที่ในทางเทคนิคprintfไม่ได้ทำ typechecking ฉันไม่เคยเคยใช้คอมไพเลอร์ที่ไม่ได้เตือนฉันเกี่ยวกับข้อผิดพลาดประเภทด้วยprintf...
CoffeeTableEspresso

1

TL; DR: เสมอทำวิจัยของคุณเองในเรื่องของขนาดที่สร้างรหัสเครื่อง , ประสิทธิภาพ , การอ่านและการเขียนโปรแกรมเวลาก่อนที่จะไว้วางใจความคิดเห็นสุ่มออนไลน์รวมทั้งคนนี้

ฉันไม่มีความเชี่ยวชาญ ฉันเพิ่งได้ยินเพื่อนร่วมงานสองคนพูดถึงวิธีที่เราควรหลีกเลี่ยงการใช้ C ++ ในระบบฝังตัวเนื่องจากปัญหาด้านประสิทธิภาพ ดีน่าสนใจพอฉันทำเกณฑ์อ้างอิงตามงานโครงการจริง

ในภารกิจดังกล่าวเราต้องเขียนการกำหนดค่าไปที่ RAM สิ่งที่ต้องการ:

กาแฟ =
น้ำตาลร้อน= ไม่มี
นม = เต้านม
mac = AA: BB: CC: DD: EE: FF

นี่คือโปรแกรมมาตรฐานของฉัน (ใช่ฉันรู้ว่า OP ถามเกี่ยวกับ printf () ไม่ใช่ fprintf () ลองจับสาระสำคัญและโดยวิธีการที่ลิงค์ของ OP ชี้ไปที่ fprintf () ต่อไป)

โปรแกรม C:

char coffee[10], sugar[10], milk[10];
unsigned char mac[6];

/* Initialize those things here. */

FILE * f = fopen("a.txt", "wt");

fprintf(f, "coffee=%s\nsugar=%s\nmilk=%s\nmac=%02X:%02X:%02X:%02X:%02X:%02X\n", coffee, sugar, milk, mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]);

fclose(f);

โปรแกรม C ++:

//Everything else is identical except:

std::ofstream f("a.txt", std::ios::out);

f << "coffee=" << coffee << "\n";
f << "sugar=" << sugar << "\n";
f << "milk=" << milk << "\n";
f << "mac=" << (int)mac[0] << ":"
    << (int)mac[1] << ":"
    << (int)mac[2] << ":"
    << (int)mac[3] << ":"
    << (int)mac[4] << ":"
    << (int)mac[5] << endl;
f.close();

ฉันพยายามทำให้ดีที่สุดเพื่อขัดมันก่อนที่จะวนลูปมันทั้ง 100,000 ครั้ง นี่คือผลลัพธ์:

โปรแกรม C:

real    0m 8.01s
user    0m 2.37s
sys     0m 5.58s

โปรแกรม C ++:

real    0m 6.07s
user    0m 3.18s
sys     0m 2.84s

ขนาดไฟล์วัตถุ:

C   - 2,092 bytes
C++ - 3,272 bytes

สรุป: ในที่เฉพาะเจาะจงมากของฉันแพลตฟอร์มที่มีความเฉพาะเจาะจงมากโปรเซสเซอร์ทำงานรุ่นที่เฉพาะเจาะจงมากของลินุกซ์เคอร์เนล , การเรียกใช้โปรแกรมที่จะรวบรวมกับรุ่นที่เฉพาะเจาะจงมากของGCCเพื่อที่จะประสบความสำเร็จอย่างมากโดยเฉพาะงานที่ผมจะบอกว่า วิธี C ++ นั้นเหมาะสมกว่าเพราะทำงานได้เร็วกว่ามากและอ่านได้ดีกว่ามาก ในทางกลับกัน C ให้บริการรอยเท้าเล็ก ๆ ในความคิดของฉันไม่มีความหมายอะไรเลยเพราะขนาดของโปรแกรมไม่ใช่เรื่องที่เรากังวล

Remeber, YMMV


ฉันไม่เห็นด้วยที่ C ++ สามารถอ่านได้มากขึ้นในตัวอย่างนี้เนื่องจากตัวอย่างของคุณบรรจุหลายบรรทัดในการเรียกใช้ printf ครั้งเดียว โดยปกติแล้วจะสามารถอ่านได้น้อยกว่าวิธีที่คุณใช้รหัส C ++ และไม่ค่อยทำใน C เพราะยากต่อการอ่านและบำรุงรักษายาก การเปรียบเทียบที่เป็นธรรมจะกระจาย C ไปเป็นงานพิมพ์แยกกันหนึ่งรายการสำหรับการเข้าถึงบรรทัด
maharvey67

1
@ maharvey67 มันเป็นความจริงที่คุณพูด อย่างไรก็ตามตัวอย่างที่ฉันให้ไว้ใน C กำลังพิจารณาถึงประสิทธิภาพ การเรียกแบบแพ็คอินวันไปยัง fprintf นั้นช้ากว่าการเทียบเท่า C ++ สองวินาที หากฉันต้องทำให้รหัส C สามารถอ่านได้มันอาจช้าลง คำเตือน: นี่เป็นหนึ่งปีที่ผ่านมาและฉันจำได้ว่าฉันพยายามอย่างดีที่สุดที่จะขัดทั้งรหัส C และ C ++ ฉันไม่มีหลักฐานของการโทรไปที่ fprintf ที่แยกจากกันจะเร็วกว่าการโทรเพียงครั้งเดียว แต่เหตุผลที่ฉันทำอย่างนี้อาจเป็นได้ว่ามันไม่ใช่
Wesley

0

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

มีฟังก์ชันมากมายใน C ++ และภาษาอื่น ๆ ที่เขียนเป็น function (พารามิเตอร์) ซึ่งเป็นไวยากรณ์ที่ใช้สำหรับความสัมพันธ์การทำงานในคณิตศาสตร์ในยุคก่อนคอมพิวเตอร์ printf()ตามไวยากรณ์นี้และหากผู้เขียน C ++ ต้องการสร้างวิธีการที่แตกต่างกันอย่างมีเหตุผลสำหรับการอ่านและการเขียนไฟล์พวกเขาสามารถสร้างฟังก์ชั่นที่แตกต่างกันโดยใช้ไวยากรณ์ที่คล้ายกัน

ใน Python เราสามารถพิมพ์ได้โดยใช้object.methodไวยากรณ์มาตรฐานเช่น variablename.print เนื่องจากตัวแปรเป็นวัตถุ แต่ใน C ++ พวกมันไม่ใช่

ฉันไม่ชอบไวยากรณ์ cout เนื่องจากตัวดำเนินการ << ไม่ปฏิบัติตามกฎใด ๆ มันเป็นวิธีการหรือฟังก์ชั่นคือมันใช้พารามิเตอร์และทำอะไรกับมัน อย่างไรก็ตามมันถูกเขียนราวกับว่ามันเป็นตัวดำเนินการเปรียบเทียบทางคณิตศาสตร์ นี่เป็นวิธีการที่ไม่ดีจากมุมมองของปัจจัยมนุษย์


-1

printfเป็นฟังก์ชั่นในขณะที่coutเป็นตัวแปร


6
ฉันย้อนกลับเพราะแม้ว่าคำตอบเองอาจผิด แต่ก็ยังเป็นคำตอบที่แท้จริง หากคุณ (ถูกต้อง) คิดว่าคำตอบนั้นผิดคุณมีสองตัวเลือก: 1) เพิ่มความคิดเห็นหรือ 2) เพิ่มคำตอบใหม่ (หรือทำทั้งสองอย่าง) อย่าเปลี่ยนคำตอบของใครบางคนเพื่อบอกว่าสิ่งที่แตกต่างอย่างสิ้นเชิงจากสิ่งที่ผู้เขียนตั้งใจ
ทำเครื่องหมาย

1
printfเป็นฟังก์ชั่น แต่printf()เป็นฟังก์ชั่นการโทร =)
vp_arth

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