คำตอบ:
ฉันประหลาดใจที่ทุกคนในคำถามนี้อ้างว่า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
\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);
^
std::sort
ซึ่งเป็นอย่างใดอย่างแปลกใจอย่างรวดเร็วเมื่อเทียบกับqsort
(2 ครั้ง) ที่ ค่าใช้จ่ายของขนาดที่สามารถใช้งานได้)
จากคำถามที่พบบ่อย 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 /)
printf()
ก็ควรที่จะยืดออก ดู "printf hooks" ที่udrepper.livejournal.com/20948.html
printf
ไม่มีความสามารถดังกล่าว กลไกไลบรารี่แบบไม่พกพานั้นแทบจะไม่อยู่ในระดับเดียวกับการขยายความสามารถของ iostreams ที่ได้มาตรฐานอย่างสมบูรณ์
คนมักจะอ้างว่า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
ก็เกือบจะเร็วหรือเร็วกว่า รายละเอียดเพิ่มเติมสามารถพบได้บนบล็อกของฉัน
ต้องมีความชัดเจนฉันไม่ได้พยายามที่จะบอกว่าiostream
s มักจะดีกว่า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);
}
}
printf()
และstd::ostream
นั่นคืออดีตเอาท์พุทขัดแย้งทั้งหมดในการเรียกหนึ่งเดียวในขณะที่เป็นกระบวนการที่ต้องโทรแยกต่างหากสำหรับแต่ละstd::ostream
<<
การทดสอบจะแสดงผลเพียงหนึ่งอาร์กิวเมนต์และขึ้นบรรทัดใหม่นั่นคือสาเหตุที่คุณไม่เห็นความแตกต่าง
printf
อาจทำการโทรจำนวนมากภายใต้หน้าปกเพื่อฟังก์ชั่นช่วยเหลือสำหรับตัวระบุการจัดรูปแบบที่หลากหลาย ... นั่นหรือเป็นฟังก์ชันเสาหินมหึมา และอีกครั้งเนื่องจากการอินไลน์มันไม่ควรสร้างความแตกต่างในเรื่องความเร็วเลย
sprintf
หรือfprintf
และหรือstringstream
fstream
และฉันพูด :
ในแง่ระดับสูงความแตกต่างที่สำคัญคือความปลอดภัยของประเภท (cstdio ไม่มี) ประสิทธิภาพ (การใช้งาน iostreams ส่วนใหญ่ช้ากว่า cstdio) และการเพิ่มความสามารถ (iostreams ช่วยให้เป้าหมายเอาต์พุตที่กำหนดเอง
หนึ่งคือฟังก์ชั่นที่พิมพ์ไปยัง stdout อีกรายการหนึ่งคือวัตถุที่มีฟังก์ชั่นสมาชิกมากมายและโอเวอร์โหลดของการoperator<<
พิมพ์นั้นไปยัง stdout มีความแตกต่างอีกมากมายที่ฉันสามารถระบุได้ แต่ฉันไม่แน่ใจว่าคุณเป็นใคร
สำหรับฉันความแตกต่างที่แท้จริงที่ทำให้ฉันไป '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
นั้นง่ายขึ้น
printf
เป็นไฟล์ได้อย่างง่ายดายโดยแทนที่ด้วยfprintf
...
สองจุดที่ไม่ได้กล่าวถึงที่นี่ฉันพบว่ามีนัยสำคัญ:
1) cout
มีสัมภาระจำนวนมากหากคุณยังไม่ได้ใช้ STL printf
มันจะเพิ่มกว่าสองเท่ารหัสมากที่จะไฟล์วัตถุของคุณเป็น นี่ก็เป็นจริงstring
เช่นกันและนี่คือเหตุผลสำคัญที่ฉันมักจะใช้ไลบรารีสตริงของตัวเอง
2) cout
ใช้<<
โอเปอเรเตอร์ที่โอเวอร์โหลดซึ่งฉันพบว่าโชคร้าย สิ่งนี้สามารถเพิ่มความสับสนหากคุณกำลังใช้<<
โอเปอเรเตอร์เพื่อจุดประสงค์ที่ต้องการไว้ (เลื่อนไปทางซ้าย) โดยส่วนตัวฉันไม่ชอบที่จะโอเวอร์โหลดโอเปอเรเตอร์เพื่อสัมผัสกับการใช้
บรรทัดล่าง: ฉันจะใช้cout
(และstring
) ถ้าฉันใช้ STL แล้ว มิฉะนั้นฉันมักจะหลีกเลี่ยง
ด้วยพื้นฐานมันอาจไม่สำคัญว่าคุณใช้ ฉันบอกว่ามันจะมีประโยชน์เมื่อคุณต้องการส่งออกวัตถุที่ซับซ้อน
ตัวอย่างเช่นถ้าคุณมีชั้นเรียน
#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" ในแอปพลิเคชันใหม่คุณไม่ต้องกังวลเกี่ยวกับผลลัพธ์
แน่นอนคุณสามารถเขียน "บางสิ่งบางอย่าง" ดีกว่าเพื่อให้การบำรุงรักษา:
#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'
มาก
endl
ล้างบัฟเฟอร์และ\n
ไม่ถึงแม้ว่าฉันไม่แน่ใจว่านี่เป็นเหตุผลที่ชัดเจน
ฉันต้องการชี้ให้เห็นว่าถ้าคุณต้องการเล่นกับเธรดใน 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
คุณสามารถใช้ที่จะได้รับมันขวาหรือคุณสามารถใช้printf
mutex
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
มีความสุข!
thread
s ไม่ทำให้ถั่วออกไป ฉันเพิ่งทำซ้ำและพบทั้งxyz
และABC
ในการส่งออก มีไม่ได้ mangling b / w เป็นABC
ABABAB
cout
ทำงานกับเธรด แต่ฉันรู้ว่ารหัสที่คุณกำลังแสดงนั้นไม่ใช่รหัสที่คุณใช้ในการรับเอาต์พุตเหล่านั้น รหัสของคุณผ่านสตริง"ABC"
สำหรับหัวข้อที่ 1 และ"xyz"
ด้าย 2 แต่แสดงให้เห็นว่าการส่งออกของคุณและAAA
BBB
โปรดแก้ไขเพราะตอนนี้มันสับสน
cout<< "Hello";
printf("%s", "Hello");
ทั้งสองถูกใช้เพื่อพิมพ์ค่า พวกเขามีไวยากรณ์ที่แตกต่างอย่างสิ้นเชิง C ++ มีทั้งสองอย่าง C มีเพียง printf
ฉันอยากจะบอกว่าการขาดความสามารถในการขยาย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()
ถ้าเราต้องการอ่าน แต่วิธีการเขียน?)
char*
จะไม่ถูกนำมาใช้
char*
ชีวิตและนานแค่ไหน การปลดเปลื้องโดยนัย
ความแตกต่างเพิ่มเติม: "printf" ส่งคืนค่าจำนวนเต็ม (เท่ากับจำนวนอักขระที่พิมพ์) และ "cout" จะไม่ส่งคืนสิ่งใด
และ.
cout << "y = " << 7;
ไม่ใช่อะตอม
printf("%s = %d", "y", 7);
เป็นอะตอม
ศาลดำเนินการพิมพ์ดีด printf ไม่ได้
ไม่มีสิ่งใดเทียบเท่ากับ iostream "% d"
cout
ไม่ส่งคืนสิ่งใดเนื่องจากเป็นวัตถุไม่ใช่ฟังก์ชัน operator<<
จะคืนค่าบางอย่าง (โดยปกติเป็นตัวถูกดำเนินการด้านซ้าย แต่เป็นค่าเท็จหากมีข้อผิดพลาด) และprintf
เรียกว่า "ปรมาณู" ในแง่ใด
printf("%s\n",7);
%s
คือ ?
printf
% sอาร์กิวเมนต์ต้องมีตัวชี้ที่ถูกต้องเพื่อสตริงยกเลิก ช่วงหน่วยความจำ '7' (ตัวชี้) ไม่ถูกต้อง ความผิดพลาดในการแบ่งส่วนอาจโชคดี ในบางระบบ '7' อาจพิมพ์ขยะจำนวนมากไปยังคอนโซลและคุณต้องดูเป็นเวลาหนึ่งวันก่อนที่โปรแกรมจะหยุด ในคำอื่น ๆ printf
นี้เป็นสิ่งที่ไม่ดีเกี่ยวกับ เครื่องมือวิเคราะห์แบบคงที่สามารถตรวจจับปัญหาเหล่านี้ได้หลายอย่าง
printf
ไม่ได้ทำ typechecking ฉันไม่เคยเคยใช้คอมไพเลอร์ที่ไม่ได้เตือนฉันเกี่ยวกับข้อผิดพลาดประเภทด้วยprintf
...
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 ++ และภาษาอื่น ๆ ที่เขียนเป็น function (พารามิเตอร์) ซึ่งเป็นไวยากรณ์ที่ใช้สำหรับความสัมพันธ์การทำงานในคณิตศาสตร์ในยุคก่อนคอมพิวเตอร์ printf()
ตามไวยากรณ์นี้และหากผู้เขียน C ++ ต้องการสร้างวิธีการที่แตกต่างกันอย่างมีเหตุผลสำหรับการอ่านและการเขียนไฟล์พวกเขาสามารถสร้างฟังก์ชั่นที่แตกต่างกันโดยใช้ไวยากรณ์ที่คล้ายกัน
ใน Python เราสามารถพิมพ์ได้โดยใช้object.method
ไวยากรณ์มาตรฐานเช่น variablename.print เนื่องจากตัวแปรเป็นวัตถุ แต่ใน C ++ พวกมันไม่ใช่
ฉันไม่ชอบไวยากรณ์ cout เนื่องจากตัวดำเนินการ << ไม่ปฏิบัติตามกฎใด ๆ มันเป็นวิธีการหรือฟังก์ชั่นคือมันใช้พารามิเตอร์และทำอะไรกับมัน อย่างไรก็ตามมันถูกเขียนราวกับว่ามันเป็นตัวดำเนินการเปรียบเทียบทางคณิตศาสตร์ นี่เป็นวิธีการที่ไม่ดีจากมุมมองของปัจจัยมนุษย์
printf
เป็นฟังก์ชั่นในขณะที่cout
เป็นตัวแปร
printf
เป็นฟังก์ชั่น แต่printf()
เป็นฟังก์ชั่นการโทร =)