Câu trả lời:
Tôi ngạc nhiên khi tất cả mọi người trong câu hỏi này khẳng định đó std::cout
là cách tốt hơn printf
, ngay cả khi câu hỏi chỉ yêu cầu sự khác biệt. Bây giờ, có một sự khác biệt - std::cout
là C ++ và printf
là C (tuy nhiên, bạn có thể sử dụng nó trong C ++, giống như hầu hết mọi thứ khác từ C). Bây giờ, tôi sẽ thành thật ở đây; cả hai printf
và std::cout
có lợi thế của họ.
std::cout
có thể mở rộng. Tôi biết rằng mọi người cũng sẽ nói rằng nó printf
có thể mở rộng, nhưng phần mở rộng đó không được đề cập trong tiêu chuẩn C (vì vậy bạn sẽ phải sử dụng các tính năng không chuẩn - nhưng thậm chí không có tính năng phi tiêu chuẩn phổ biến nào) và các tiện ích mở rộng đó chỉ là một chữ cái (vì vậy rất dễ xung đột với định dạng đã tồn tại).
Không giống như printf
, std::cout
phụ thuộc hoàn toàn vào quá tải toán tử, do đó không có vấn đề gì với các định dạng tùy chỉnh - tất cả những gì bạn làm là xác định một chương trình con lấy std::ostream
làm đối số đầu tiên và loại của bạn là thứ hai. Như vậy, không có vấn đề về không gian tên - miễn là bạn có một lớp (không giới hạn ở một ký tự), bạn có thể phải làm việc std::ostream
quá tải cho nó.
Tuy nhiên, tôi nghi ngờ rằng nhiều người sẽ muốn gia hạn ostream
(thành thật mà nói, tôi hiếm khi thấy các tiện ích mở rộng như vậy, ngay cả khi chúng dễ thực hiện). Tuy nhiên, nó ở đây nếu bạn cần nó.
Vì nó có thể dễ dàng nhận thấy, cả hai printf
và std::cout
sử dụng cú pháp khác nhau. printf
sử dụng cú pháp hàm tiêu chuẩn bằng cách sử dụng chuỗi mẫu và danh sách đối số có độ dài thay đổi. Trên thực tế, printf
là một lý do tại sao C có chúng - printf
các định dạng quá phức tạp để có thể sử dụng mà không có chúng. Tuy nhiên, std::cout
sử dụng một API khác - operator <<
API tự trả về.
Nói chung, điều đó có nghĩa là phiên bản C sẽ ngắn hơn, nhưng trong hầu hết các trường hợp, nó không thành vấn đề. Sự khác biệt là đáng chú ý khi bạn in nhiều đối số. Nếu bạn phải viết một cái gì đó như Error 2: File not found.
, giả sử số lỗi và mô tả của nó là giữ chỗ, mã sẽ trông như thế này. Cả hai ví dụ đều hoạt động giống hệt nhau (tốt, sắp xếp, std::endl
thực sự xóa bộ đệm).
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
Mặc dù điều này dường như không quá điên rồ (nó chỉ dài hơn hai lần), mọi thứ trở nên điên rồ hơn khi bạn thực sự định dạng các đối số, thay vì chỉ in chúng. Ví dụ, in một cái gì đó như 0x0424
chỉ là điên rồ. Điều này được gây ra bởi std::cout
sự pha trộn giữa trạng thái và giá trị thực tế. Tôi chưa bao giờ thấy một ngôn ngữ mà một thứ như thế std::setfill
sẽ là một loại (tất nhiên không phải là C ++). printf
phân tách rõ ràng đối số và loại thực tế. Tôi thực sự muốn duy trì printf
phiên bản của nó (ngay cả khi nó trông có vẻ khó hiểu) so với iostream
phiên bản của nó (vì nó chứa quá nhiều tiếng ồn).
printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;
Đây là nơi lợi thế thực sự của printf
dối trá. Chuỗi printf
định dạng cũng ... một chuỗi. Điều đó làm cho nó thực sự dễ dàng để dịch, so với operator <<
lạm dụng iostream
. Giả sử rằng gettext()
hàm dịch và bạn muốn hiển thị Error 2: File not found.
, mã để nhận bản dịch của chuỗi định dạng được hiển thị trước đó sẽ trông như thế này:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Bây giờ, giả sử rằng chúng tôi dịch sang F hư cấu, trong đó số lỗi nằm sau phần mô tả. Chuỗi dịch sẽ như thế nào %2$s oru %1$d.\n
. Bây giờ, làm thế nào để làm điều đó trong C ++? Vâng, tôi không có ý tưởng. Tôi đoán bạn có thể làm giả iostream
những cấu trúc printf
mà bạn có thể chuyển đến gettext
, hoặc một cái gì đó, cho mục đích dịch thuật. Tất nhiên, $
không phải là tiêu chuẩn C, nhưng theo tôi thì nó phổ biến đến mức an toàn khi sử dụng.
C có rất nhiều kiểu số nguyên và C ++ cũng vậy. std::cout
xử lý tất cả các loại cho bạn, trong khi printf
yêu cầu cú pháp cụ thể tùy thuộc vào loại số nguyên (có loại không nguyên, nhưng loại không nguyên duy nhất bạn sẽ sử dụng trong thực tế printf
là const char *
(chuỗi C, có thể thu được bằng to_c
phương thức std::string
)). Ví dụ, để in size_t
, bạn cần sử dụng %zd
, trong khi int64_t
sẽ yêu cầu sử dụng %"PRId64"
. Các bảng có sẵn tại http://en.cppreference.com/w/cpp/io/c/fprintf và http://en.cppreference.com/w/cpp/types/integer .
\0
Vì printf
sử dụng các chuỗi C trái ngược với các chuỗi C ++, nó không thể in byte NUL mà không có các thủ thuật cụ thể. Trong một số trường hợp nó có thể sử dụng %c
với '\0'
như một cuộc tranh cãi, mặc dù đó là rõ ràng một hack.
Cập nhật: Hóa ra iostream
là chậm đến mức nó thường chậm hơn ổ cứng của bạn (nếu bạn chuyển hướng chương trình của mình sang tệp). Vô hiệu hóa đồng bộ hóa với stdio
có thể giúp đỡ, nếu bạn cần xuất nhiều dữ liệu. Nếu hiệu suất là một mối quan tâm thực sự (trái ngược với việc viết một vài dòng cho STDOUT), chỉ cần sử dụng printf
.
Mọi người đều nghĩ rằng họ quan tâm đến hiệu suất, nhưng không ai bận tâm để đo lường nó. Câu trả lời của tôi là I / O vẫn bị nghẽn cổ chai, bất kể bạn sử dụng printf
hay iostream
. Tôi nghĩ rằng printf
có thể nhanh hơn từ một cái nhìn nhanh vào lắp ráp (được biên dịch bằng tiếng kêu bằng cách sử dụng -O3
tùy chọn trình biên dịch). Giả sử ví dụ lỗi của tôi, printf
ví dụ thực hiện cách gọi ít hơn so với cout
ví dụ. Đây là int main
với 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:
Bạn có thể dễ dàng nhận thấy rằng hai chuỗi và 2
(số) được đẩy làm printf
đối số. Đó là về nó; Không có gì khác cả. Để so sánh, điều này được iostream
biên soạn để lắp ráp. Không, không có nội tuyến; mỗi operator <<
cuộc gọi có nghĩa là một cuộc gọi khác với một bộ đối số khác.
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:
Tuy nhiên, thành thật mà nói, điều này có nghĩa là không có gì, vì dù sao I / O cũng là nút cổ chai. Tôi chỉ muốn chứng tỏ rằng iostream
nó không nhanh hơn vì nó "an toàn". Hầu hết các triển khai C thực hiện printf
các định dạng bằng goto được tính toán, vì vậy printf
tốc độ nhanh nhất có thể, ngay cả khi không có trình biên dịch nhận biết printf
(không phải là chúng không - một số trình biên dịch có thể tối ưu hóa printf
trong một số trường hợp - chuỗi kết thúc liên tục \n
thường được tối ưu hóa puts
) .
Tôi không biết tại sao bạn muốn thừa kế ostream
, nhưng tôi không quan tâm. Điều đó cũng có thể xảy ra FILE
.
class MyFile : public FILE {}
Các danh sách đối số độ dài thay đổi không có độ an toàn, nhưng điều đó không quan trọng, vì trình biên dịch C phổ biến có thể phát hiện các vấn đề với printf
chuỗi định dạng nếu bạn bật cảnh báo. Trên thực tế, Clang có thể làm điều đó mà không cần cảnh báo.
$ 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
, nhanh đến mức đáng ngạc nhiên so với qsort
(2 lần), tại chi phí kích thước thực thi).
Từ C ++ FAQ :
[15.1] Tại sao tôi nên sử dụng
<iostream>
thay vì truyền thống<cstdio>
?Tăng độ an toàn của loại, giảm lỗi, cho phép mở rộng và cung cấp khả năng kế thừa.
printf()
được cho là không bị phá vỡ, vàscanf()
có lẽ là có thể sống được mặc dù dễ bị lỗi, tuy nhiên cả hai đều bị giới hạn đối với những gì I / O C ++ có thể làm. C / I / O (sử dụng<<
và>>
) là, liên quan đến C (sử dụngprintf()
vàscanf()
):
- An toàn hơn về kiểu: Với
<iostream>
, loại đối tượng là I / O'd được trình biên dịch biết một cách tĩnh. Ngược lại,<cstdio>
sử dụng các trường "%" để tìm ra các loại động.- Ít xảy ra lỗi hơn: Với
<iostream>
, không có mã thông báo "%" dự phòng nào phải phù hợp với các đối tượng thực tế là I / O'd. Loại bỏ sự dư thừa sẽ loại bỏ một lớp lỗi.- Mở rộng:
<iostream>
Cơ chế C ++ cho phép các loại do người dùng xác định mới là I / O'd mà không phá vỡ mã hiện có. Hãy tưởng tượng sự hỗn loạn nếu tất cả mọi người đồng thời thêm các trường "%" không tương thích mới vàoprintf()
vàscanf()
?!- Kế thừa:
<iostream>
Cơ chế C ++ được xây dựng từ các lớp thực nhưstd::ostream
vàstd::istream
. Không giống như<cstdio>
'sFILE*
, đây là những lớp học thực tế và do đó thừa kế. Điều này có nghĩa là bạn có thể có những thứ do người dùng định nghĩa khác trông giống và hoạt động như luồng, nhưng điều đó làm bất cứ điều gì kỳ lạ và tuyệt vời mà bạn muốn. Bạn tự động được sử dụng hàng trăm dòng mã I / O được viết bởi người dùng mà bạn thậm chí không biết và họ không cần biết về lớp "luồng mở rộng" của bạn.
Mặt khác, printf
nhanh hơn đáng kể, điều này có thể biện minh cho việc sử dụng nó theo sở thích cout
trong các trường hợp rất cụ thể và hạn chế. Luôn luôn hồ sơ đầu tiên. (Xem, ví dụ: http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)
printf()
cũng được cho là có thể mở rộng. Xem " hookf print
printf
không có khả năng đó. Các cơ chế thư viện không di động hầu như không cùng mức độ mở rộng được chuẩn hóa hoàn toàn của iostreams.
Mọi người thường cho rằng printf
nhanh hơn nhiều. Điều này phần lớn là một huyền thoại. Tôi vừa thử nó, với kết quả như sau:
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
Kết luận: nếu bạn chỉ muốn dòng mới, sử dụng printf
; mặt khác, cout
gần như là nhanh, hoặc thậm chí nhanh hơn. Thông tin chi tiết có thể được tìm thấy trên blog của tôi .
Để rõ ràng, tôi không cố nói rằng iostream
s luôn tốt hơn printf
; Tôi chỉ đang cố gắng nói rằng bạn nên đưa ra quyết định dựa trên dữ liệu thực chứ không phải là phỏng đoán hoang dã dựa trên một số giả định phổ biến, sai lệch.
Cập nhật: Đây là mã đầy đủ tôi sử dụng để thử nghiệm. Được biên dịch g++
mà không có bất kỳ tùy chọn bổ sung nào (ngoài -lrt
thời gian).
#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()
và std::ostream
là trước đây xuất ra tất cả các đối số trong một cuộc gọi duy nhất trong khi std::ostream
phát sinh một cuộc gọi riêng cho mỗi cuộc gọi <<
. Bài kiểm tra chỉ đưa ra một đối số và một dòng mới, đó là lý do tại sao bạn không thể thấy sự khác biệt.
printf
có thể thực hiện nhiều cuộc gọi dưới vỏ bọc cho các chức năng trợ giúp cho các trình xác định định dạng khác nhau ... hoặc đó là một chức năng nguyên khối quái dị. Và một lần nữa, vì nội tuyến, nó không nên tạo ra sự khác biệt về tốc độ.
sprintf
hoặc fprintf
và stringstream
hoặc fstream
.
Và tôi trích dẫn :
Ở cấp độ cao, sự khác biệt chính là an toàn loại (cstdio không có), hiệu suất (hầu hết các triển khai iostream đều chậm hơn so với cstdio) và khả năng mở rộng (iostreams cho phép mục tiêu đầu ra tùy chỉnh và đầu ra liền mạch của các loại do người dùng xác định).
Một là một chức năng in ra thiết bị xuất chuẩn. Đối tượng kia là một đối tượng cung cấp một số hàm thành viên và quá tải operator<<
bản in đó cho thiết bị xuất chuẩn. Có nhiều sự khác biệt nữa mà tôi có thể liệt kê, nhưng tôi không chắc bạn sẽ làm gì sau đó.
Đối với tôi, sự khác biệt thực sự sẽ khiến tôi đi theo 'cout' thay vì 'printf' là:
1) << toán tử có thể bị quá tải cho các lớp của tôi.
2) Luồng đầu ra cho cout có thể dễ dàng thay đổi thành một tệp: (: sao chép dán :)
#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) Tôi thấy cout dễ đọc hơn, đặc biệt là khi chúng ta có nhiều tham số.
Một vấn đề với cout
là các tùy chọn định dạng. Định dạng dữ liệu (độ chính xác, chính xác, v.v.) trong printf
dễ dàng hơn.
printf
thành một tệp bằng cách thay thế nó bằng fprintf
...
Hai điểm không được đề cập ở đây mà tôi thấy có ý nghĩa:
1) cout
mang theo nhiều hành lý nếu bạn chưa sử dụng STL. Nó thêm nhiều hơn hai lần mã vào tệp đối tượng của bạn printf
. Điều này cũng đúng vớistring
và đây là lý do chính khiến tôi có xu hướng sử dụng thư viện chuỗi của riêng mình.
2) cout
sử dụng các <<
toán tử quá tải , điều mà tôi thấy không may. Điều này có thể thêm sự nhầm lẫn nếu bạn cũng đang sử dụng<<
toán tử cho mục đích dự định của mình (dịch chuyển sang trái). Cá nhân tôi không muốn quá tải các nhà khai thác cho các mục đích tiếp tuyến với mục đích sử dụng của họ.
Điểm mấu chốt: Tôi sẽ sử dụng cout
(và string
) nếu tôi đã sử dụng STL. Nếu không, tôi có xu hướng tránh nó.
Với người nguyên thủy, có lẽ hoàn toàn không quan trọng bạn sử dụng cái nào. Tôi nói nơi nó có ích là khi bạn muốn xuất các đối tượng phức tạp.
Ví dụ, nếu bạn có một lớp,
#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;
}
Bây giờ ở trên có vẻ không tốt lắm, nhưng giả sử bạn phải xuất cái này ở nhiều nơi trong mã của bạn. Không chỉ vậy, giả sử bạn thêm một trường "int d." Với cout, bạn chỉ phải thay đổi nó ở một nơi. Tuy nhiên, với printf, bạn phải thay đổi nó ở nhiều nơi và không chỉ vậy, bạn phải tự nhắc nhở mình nên xuất cái nào.
Như đã nói, với cout, bạn có thể giảm rất nhiều thời gian cho việc bảo trì mã của mình và không chỉ vậy nếu bạn sử dụng lại đối tượng "Something" trong một ứng dụng mới, bạn thực sự không phải lo lắng về đầu ra.
Tất nhiên bạn có thể viết "một cái gì đó" tốt hơn một chút để duy trì bảo trì:
#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;
}
Và một thử nghiệm mở rộng một chút về cout so với printf, đã thêm một thử nghiệm 'double', nếu có ai muốn thực hiện thêm thử nghiệm (Visual Studio 2008, phiên bản phát hành của tệp thực thi):
#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);
}
}
Kết quả là:
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
kém hiệu quả hơn nhiều '\n'
?
endl
xả bộ đệm, và \n
không, mặc dù tôi không chắc đây là lý do chắc chắn tại sao.
Tôi muốn chỉ ra rằng nếu bạn muốn chơi với các luồng trong C ++, nếu bạn sử dụng, cout
bạn có thể nhận được một số kết quả thú vị.
Xem xét mã này:
#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
Bây giờ, đầu ra tất cả xáo trộn. Nó cũng có thể mang lại kết quả khác nhau, hãy thử thực hiện nhiều lần:
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
Bạn có thể sử dụng printf
để làm cho đúng, hoặc bạn có thể sử dụng mutex
.
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
Chúc vui vẻ!
thread
s không làm cho đầu ra đi hạt. Tôi chỉ sao chép và tìm thấy cả xyz
và ABC
trong đầu ra. Không có xlingling b / w ABC
như ABABAB
.
cout
việc với các chủ đề, nhưng tôi biết chắc chắn rằng mã bạn đang hiển thị không phải là mã mà bạn đã sử dụng để có được các đầu ra đó. Mã của bạn chuyển chuỗi "ABC"
cho luồng 1 và "xyz"
cho luồng 2, nhưng đầu ra của bạn hiển thị AAA
và BBB
. Vui lòng sửa nó, bởi vì ngay bây giờ nó khó hiểu.
cout<< "Hello";
printf("%s", "Hello");
Cả hai đều được sử dụng để in các giá trị. Họ có cú pháp hoàn toàn khác nhau. C ++ có cả hai, C chỉ có printf.
Tôi muốn nói rằng sự thiếu mở rộng printf
không hoàn toàn đúng:
Trong C, nó là sự thật. Nhưng trong C, không có lớp học thực sự.
Trong C ++, có thể quá tải toán tử cast, do đó, quá tải một char*
toán tử và sử dụng printf
như thế này:
Foo bar;
...;
printf("%s",bar);
có thể, nếu Foo quá tải toán tử tốt. Hoặc nếu bạn thực hiện một phương pháp tốt. Tóm lại, printf
là mở rộng như cout
đối với tôi.
Đối số kỹ thuật tôi có thể thấy đối với các luồng C ++ (nói chung ... không chỉ cout.) Là:
Loại an toàn. (Và, nhân tiện, nếu tôi muốn in một bản '\n'
tôi sử dụng putchar('\n')
... tôi sẽ không sử dụng bom nuke để giết côn trùng.).
Đơn giản hơn để học hỏi. (không có tham số "phức tạp" để tìm hiểu, chỉ để sử dụng <<
và >>
vận hành)
Làm việc tự nhiên với std::string
(vì printf
có std::string::c_str()
, nhưng choscanf
?)
Vì printf
tôi thấy:
Định dạng phức tạp dễ hơn, hoặc ít nhất là ngắn hơn (về các ký tự được viết). Dễ đọc hơn, đối với tôi (vấn đề của hương vị tôi đoán).
Kiểm soát tốt hơn những gì hàm tạo ra (Trả về số lượng ký tự được viết và có %n
định dạng: "Không có gì được in. Đối số phải là một con trỏ tới một int đã ký, trong đó số lượng ký tự được viết cho đến nay được lưu trữ." (Từ printf - Tham khảo C ++ )
Khả năng sửa lỗi tốt hơn. Đối với lý do tương tự như đối số cuối cùng.
Sở thích cá nhân của tôi dành cho các chức năng printf
(và scanf
), chủ yếu là vì tôi yêu thích các dòng ngắn và vì tôi không nghĩ các vấn đề về kiểu in trên văn bản thực sự khó tránh. Điều duy nhất tôi làm với các chức năng kiểu C std::string
là không được hỗ trợ. Chúng ta phải trải qua một char*
trước khi đưa nó cho printf
(với std::string::c_str()
nếu chúng ta muốn đọc, nhưng làm thế nào để viết?)
char*
sẽ không được sử dụng.
char*
cuộc sống và trong bao lâu, và những nguy hiểm do người dùng xác định diễn viên ngầm.
Khác biệt hơn: "printf" trả về một giá trị nguyên (bằng số lượng ký tự được in) và "cout" không trả về bất cứ thứ gì
Và.
cout << "y = " << 7;
không phải là nguyên tử.
printf("%s = %d", "y", 7);
là nguyên tử.
cout thực hiện đánh máy, printf không.
Không có iostream tương đương với "% d"
cout
không trả lại bất cứ điều gì vì nó là một đối tượng, không phải là một chức năng. operator<<
không trả về một cái gì đó (thông thường là toán hạng bên trái của nó, nhưng giá trị sai nếu có lỗi). Và theo nghĩa nào là printf
cuộc gọi "nguyên tử"?
printf("%s\n",7);
%s
Là ?
printf
% s lập luận phải có một con trỏ hợp lệ để một chuỗi null chấm dứt. Phạm vi bộ nhớ '7' (một con trỏ) thường không hợp lệ; một lỗi phân khúc có thể là may mắn. Trên một số hệ thống, '7' có thể in rất nhiều rác lên bàn điều khiển và bạn sẽ phải xem xét nó trong một ngày trước khi chương trình dừng lại. Nói cách khác, đây là một điều xấu về printf
. Các công cụ phân tích tĩnh có thể nắm bắt được nhiều vấn đề này.
printf
không thực hiện đánh máy, tôi chưa bao giờ sử dụng trình biên dịch không cảnh báo tôi về lỗi loại với printf
...
TL; DR: Luôn luôn thực hiện nghiên cứu của riêng bạn, liên quan đến kích thước mã máy , hiệu suất , khả năng đọc và thời gian mã hóa được tạo trước khi tin tưởng các nhận xét ngẫu nhiên trực tuyến, bao gồm cả nhận xét này.
Tôi không phải là chuyên gia. Tôi tình cờ nghe thấy hai đồng nghiệp nói về cách chúng ta nên tránh sử dụng C ++ trong các hệ thống nhúng vì vấn đề hiệu năng. Chà, đủ thú vị, tôi đã làm một điểm chuẩn dựa trên một nhiệm vụ dự án thực sự.
Trong nhiệm vụ nói trên, chúng tôi đã phải viết một số cấu hình vào RAM. Cái gì đó như:
cà phê =
đường nóng = không
sữa = vú
mac = AA: BB: CC: DD: EE: FF
Đây là các chương trình điểm chuẩn của tôi (Có, tôi biết OP đã hỏi về printf (), không phải fprintf (). Hãy cố gắng nắm bắt bản chất và nhân tiện, liên kết của OP chỉ đến fprintf ().)
Chương trình 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);
Chương trình 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();
Tôi đã làm hết sức mình để đánh bóng chúng trước khi tôi lặp cả hai lần 100.000 lần. Đây là kết quả:
Chương trình C:
real 0m 8.01s
user 0m 2.37s
sys 0m 5.58s
Chương trình C ++:
real 0m 6.07s
user 0m 3.18s
sys 0m 2.84s
Kích thước tệp đối tượng:
C - 2,092 bytes
C++ - 3,272 bytes
Kết luận: Trên nền tảng rất cụ thể của tôi , với bộ xử lý rất cụ thể , chạy phiên bản nhân Linux rất cụ thể , để chạy chương trình được biên dịch với phiên bản GCC rất cụ thể , để hoàn thành một nhiệm vụ rất cụ thể , tôi sẽ nói Cách tiếp cận C ++ phù hợp hơn vì nó chạy nhanh hơn đáng kể và cung cấp khả năng đọc tốt hơn nhiều. Mặt khác, C cung cấp dấu chân nhỏ, theo tôi, có nghĩa là gần như không có gì vì quy mô chương trình không phải là mối quan tâm của chúng tôi.
Người nhớ, YMMV.
Tôi không phải là lập trình viên, nhưng tôi là một kỹ sư nhân tố. Tôi cảm thấy một ngôn ngữ lập trình nên dễ học, hiểu và sử dụng, và điều này đòi hỏi nó phải có cấu trúc ngôn ngữ đơn giản và nhất quán. Mặc dù tất cả các ngôn ngữ là tượng trưng và do đó, ở cốt lõi, tùy ý, có những quy ước và tuân theo chúng làm cho ngôn ngữ dễ học và sử dụng hơn.
Có một số lượng lớn các hàm trong C ++ và các ngôn ngữ khác được viết dưới dạng hàm (tham số), một cú pháp ban đầu được sử dụng cho các mối quan hệ chức năng trong toán học trong thời kỳ tiền máy tính. printf()
tuân theo cú pháp này và nếu các tác giả của C ++ muốn tạo bất kỳ phương thức logic nào khác để đọc và ghi tệp, họ có thể chỉ cần tạo một hàm khác bằng một cú pháp tương tự.
Trong Python tất nhiên chúng ta có thể in bằng cách sử dụng object.method
cú pháp khá chuẩn , ví dụ: matrixblename.print, vì các biến là các đối tượng, nhưng trong C ++ thì không.
Tôi không thích cú pháp cout vì toán tử << không tuân theo bất kỳ quy tắc nào. Nó là một phương thức hoặc hàm, tức là nó lấy một tham số và thực hiện một cái gì đó cho nó. Tuy nhiên, nó được viết như thể nó là một toán tử so sánh toán học. Đây là một cách tiếp cận kém từ quan điểm yếu tố con người.
printf
là một hàm trong khi đó cout
là một biến.
printf
là một hàm, nhưng printf()
là một hàm gọi =)