Odpowiedzi:
Dziwi mnie, że wszyscy w tym pytaniu twierdzą, że std::cout
jest o wiele lepszy niż printf
, nawet jeśli pytanie dotyczyło tylko różnic. Teraz jest różnica - std::cout
to C ++ i printf
C (jednak można go używać w C ++, podobnie jak prawie wszystko inne z C). Będę tu szczery; oba printf
i std::cout
mają swoje zalety.
std::cout
jest rozszerzalny. Wiem, że ludzie powiedzą, że printf
jest również rozszerzalny, ale takie rozszerzenie nie jest wspomniane w standardzie C (więc musiałbyś użyć niestandardowych funkcji - ale nawet nie istnieje wspólna niestandardowa funkcja), a takie rozszerzenia to jedna litera (więc łatwo jest konfliktować z już istniejącym formatem).
W przeciwieństwie do printf
, std::cout
zależy całkowicie od przeciążenia operatora, więc nie ma problemu z niestandardowymi formatami - wystarczy, że zdefiniujesz podprogram std::ostream
jako pierwszy argument, a typ jako drugi. W związku z tym nie ma problemów z przestrzenią nazw - dopóki masz klasę (która nie jest ograniczona do jednej postaci), możesz mieć std::ostream
nadmiar pracy .
Wątpię jednak, aby wiele osób chciało przedłużać ostream
(szczerze mówiąc, rzadko widziałem takie rozszerzenia, nawet jeśli są łatwe do wykonania). Jest jednak tutaj, jeśli go potrzebujesz.
Jak można to łatwo zauważyć, jak printf
i std::cout
używać innej składni. printf
używa standardowej składni funkcji przy użyciu łańcucha wzorca i list argumentów o zmiennej długości. W rzeczywistości printf
jest to powód, dla którego C je ma - printf
formaty są zbyt złożone, aby można je było bez nich zastosować. Jednak std::cout
używa innego API - ten operator <<
interfejs API, który wraca sam.
Ogólnie oznacza to, że wersja C będzie krótsza, ale w większości przypadków nie będzie to miało znaczenia. Różnica jest zauważalna przy drukowaniu wielu argumentów. Jeśli musisz napisać coś takiego Error 2: File not found.
, zakładając numer błędu, a jego opis jest symbolem zastępczym, kod wyglądałby tak. Oba przykłady działają identycznie (no cóż, std::endl
właściwie opróżniają bufor).
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
Chociaż nie wydaje się to zbyt szalone (jest tylko dwa razy dłuższe), rzeczy stają się bardziej szalone, gdy faktycznie formatujesz argumenty, zamiast po prostu je drukować. Na przykład drukowanie czegoś takiego 0x0424
jest po prostu szalone. Jest to spowodowane std::cout
stanem mieszania i wartościami rzeczywistymi. Nigdy nie widziałem języka, w którym coś takiego std::setfill
byłoby typem (oczywiście innym niż C ++). printf
wyraźnie oddziela argumenty i rzeczywisty typ. Naprawdę wolałbym zachować jego printf
wersję (nawet jeśli wygląda to trochę tajemniczo) w porównaniu do iostream
wersji (ponieważ zawiera zbyt dużo hałasu).
printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;
To jest prawdziwa zaleta printf
kłamstw. printf
Ciąg format jest dobrze ... ciąg. Dzięki temu tłumaczenie jest naprawdę łatwe w porównaniu z operator <<
nadużyciami iostream
. Zakładając, że gettext()
funkcja tłumaczy i chcesz pokazać Error 2: File not found.
, kod do uzyskania tłumaczenia pokazanego wcześniej ciągu formatu wyglądałby następująco:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Załóżmy teraz, że tłumaczymy na fikcyjny, gdzie numer błędu znajduje się po opisie. Tak wyglądałby przetłumaczony ciąg %2$s oru %1$d.\n
. Jak to zrobić w C ++? Nie mam pojęcia. Wydaje mi się, że można zrobić fałszywe iostream
konstrukcje printf
, które można przekazać gettext
lub coś w celu tłumaczenia. Oczywiście $
nie jest standardem C, ale jest tak powszechny, że moim zdaniem można go bezpiecznie używać.
C ma wiele typów całkowitych, podobnie jak C ++. std::cout
obsługuje wszystkie typy dla Ciebie, podczas gdy printf
wymaga określonej składni w zależności od typu liczby całkowitej (istnieją typy niecałkowate, ale jedynym typem niecałkowitym, którego będziesz używać w praktyce, printf
jest const char *
(ciąg C, można uzyskać to_c
metodą std::string
). Na przykład, aby wydrukować size_t
, musisz użyć %zd
, podczas gdy int64_t
będzie wymagać użycia %"PRId64"
. Tabele są dostępne na stronie http://en.cppreference.com/w/cpp/io/c/fprintf i http://en.cppreference.com/w/cpp/types/integer .
\0
Ponieważ printf
używa ciągów C w przeciwieństwie do ciągów C ++, nie można wydrukować bajtu NUL bez określonych sztuczek. W niektórych przypadkach jest to możliwe korzystać %c
z '\0'
jako argument, mimo to wyraźnie hack.
Aktualizacja: Okazuje się, że iostream
jest tak wolny, że zwykle jest wolniejszy niż dysk twardy (jeśli przekierujesz program do pliku). Wyłączenie synchronizacji z stdio
może pomóc, jeśli musisz wyprowadzać dużo danych. Jeśli wydajność stanowi poważny problem (w przeciwieństwie do pisania kilku linii do STDOUT), po prostu użyj printf
.
Wszyscy myślą, że zależy im na wydajności, ale nikt nie stara się jej zmierzyć. Moja odpowiedź jest taka, że We / Wy jest wąskie gardło, bez względu na to, czy używasz, printf
czy iostream
. Myślę, że printf
może to być szybsze od szybkiego spojrzenia na asembler (skompilowany z clang za pomocą -O3
opcji kompilatora). Zakładając mój przykład błędu, printf
przykład wykonuje znacznie mniej wywołań niż cout
przykład. To jest int main
z 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:
Możesz łatwo zauważyć, że dwa ciągi i 2
(liczba) są wypychane jako printf
argumenty. O to chodzi; nie ma nic więcej. Dla porównania iostream
kompiluje się to w asemblerze. Nie, nie ma inklinacji; każde pojedyncze operator <<
połączenie oznacza kolejne połączenie z innym zestawem argumentów.
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:
Jednak szczerze mówiąc, to nic nie znaczy, ponieważ we / wy jest wąskim gardłem. Chciałem tylko pokazać, że iostream
nie jest to szybsze, ponieważ jest „bezpieczne dla typu”. Większość implementacji C implementuje printf
formaty wykorzystujące obliczone goto, więc printf
jest tak szybkie, jak to tylko możliwe, nawet bez wiedzy kompilatora printf
(nie to, że nie są - niektóre kompilatory mogą zoptymalizować printf
w niektórych przypadkach - ciąg zakończony ciągiem \n
jest zwykle optymalizowany puts
) .
Nie wiem, dlaczego chcesz dziedziczyć ostream
, ale mnie to nie obchodzi. To też jest możliwe FILE
.
class MyFile : public FILE {}
To prawda, że listy argumentów o zmiennej długości nie mają bezpieczeństwa, ale to nie ma znaczenia, ponieważ popularne kompilatory C mogą wykryć problemy z printf
łańcuchem formatu, jeśli włączysz ostrzeżenia. W rzeczywistości Clang może to zrobić bez włączania ostrzeżeń.
$ 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
, która jest zaskakująco szybka w porównaniu do qsort
(2 razy), w koszt rozmiaru pliku wykonywalnego).
Z C ++ FAQ :
[15.1] Dlaczego powinienem używać
<iostream>
zamiast tradycyjnego<cstdio>
?Zwiększyć bezpieczeństwo typu, zmniejszyć liczbę błędów, umożliwić rozszerzalność i zapewnić dziedziczenie.
printf()
prawdopodobnie nie jest zepsuty iscanf()
może być znośny pomimo podatności na błędy, jednak oba są ograniczone w odniesieniu do tego, co potrafi C / I we / wy. C ++ I / O (używając<<
i>>
) jest względem C (używającprintf()
iscanf()
):
- Bardziej bezpieczny dla typu: Z
<iostream>
, typ obiektu będącego we / wy znany jest statycznie przez kompilator. W przeciwieństwie do tego<cstdio>
używa pól „%” do dynamicznego obliczania typów.- Mniej podatne na błędy: dzięki
<iostream>
nie ma zbędnych tokenów „%”, które musiałyby być spójne z rzeczywistymi obiektami będącymi we / wy. Usunięcie redundancji usuwa klasę błędów.- Rozszerzalny:
<iostream>
mechanizm C ++ pozwala nowym typom zdefiniowanym przez użytkownika na operacje wejścia / wyjścia bez zerwania istniejącego kodu. Wyobraź sobie chaos, jeśli wszyscy jednocześnie dodawali nowe niekompatybilne pola „%” doprintf()
iscanf()
?!- Dziedziczony:
<iostream>
mechanizm C ++ jest zbudowany z prawdziwych klas, takich jakstd::ostream
istd::istream
. W odróżnieniu od<cstdio>
„sFILE*
, są to prawdziwe klasy i stąd dziedziczone. Oznacza to, że możesz mieć inne zdefiniowane przez użytkownika rzeczy, które wyglądają i działają jak strumienie, a jednak robią wszystko, co chcesz, dziwne i wspaniałe. Możesz automatycznie użyć zillionów wierszy kodu we / wy napisanych przez użytkowników, których nawet nie znasz, i nie muszą oni wiedzieć o twojej klasie „rozszerzonego strumienia”.
Z drugiej strony printf
jest znacznie szybszy, co może uzasadniać korzystanie z niego zamiast cout
w bardzo szczególnych i ograniczonych przypadkach. Zawsze najpierw profiluj. (Zobacz na przykład http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)
printf()
ma być również rozszerzalny. Zobacz „haczyki printf” na stronie udrepper.livejournal.com/20948.html
printf
nie ma takiej zdolności. Mechanizmy bibliotek nieprzenośnych nie są na tym samym poziomie co w pełni znormalizowana rozszerzalność iostreamów.
Ludzie często twierdzą, że printf
jest to znacznie szybsze. To w dużej mierze mit. Właśnie go przetestowałem z następującymi wynikami:
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
Wniosek: jeśli chcesz tylko nowe wiersze, użyj printf
; w przeciwnym razie cout
jest prawie tak samo szybkie lub nawet szybsze. Więcej informacji można znaleźć na moim blogu .
Dla jasności nie próbuję powiedzieć, że iostream
zawsze są lepsze niż printf
; Próbuję tylko powiedzieć, że powinieneś podjąć świadomą decyzję w oparciu o rzeczywiste dane, a nie dzikie przypuszczenia oparte na jakimś powszechnym, mylącym założeniu.
Aktualizacja: Oto pełny kod, którego użyłem do testowania. Kompilowany g++
bez żadnych dodatkowych opcji (oprócz -lrt
czasu).
#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()
i std::ostream
polega na tym, że poprzednie generuje wszystkie argumenty w jednym wywołaniu, podczas gdy std::ostream
dla każdego wywołuje osobne wywołanie <<
. Test generuje tylko jeden argument i nowy wiersz, dlatego nie widać różnicy.
printf
może wykonywać wiele wywołań pod pokrywami do funkcji pomocniczych dla różnych specyfikatorów formatowania ... to, lub jest to monolityczna funkcja monolityczna. I znowu, ze względu na nachylenie, w ogóle nie powinno mieć znaczenia prędkość.
sprintf
lub fprintf
i stringstream
lub fstream
.
I cytuję :
W kategoriach wysokiego poziomu główne różnice to bezpieczeństwo typu (cstdio go nie ma), wydajność (większość implementacji iostreams jest wolniejsza niż cstdio) i rozszerzalność (iostreams pozwala na niestandardowe cele wyjściowe i bezproblemową produkcję typów zdefiniowanych przez użytkownika).
Jedna to funkcja, która drukuje na standardowe wyjście. Drugi to obiekt, który udostępnia kilka funkcji składowych i przeciążenia operator<<
tego wydruku na standardowe wyjście. Istnieje wiele innych różnic, które mógłbym wymienić, ale nie jestem pewien, o co ci chodzi.
Dla mnie rzeczywiste różnice, które skłoniłyby mnie do użycia „cout” zamiast „printf” to:
1) << Operator może być przeciążony dla moich klas.
2) Strumień wyjściowy dla cout można łatwo zmienić na plik: (: kopiuj wklej :)
#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) Uważam, że jest bardziej czytelny, szczególnie gdy mamy wiele parametrów.
Jednym z problemówcout
są opcje formatowania. Formatowanie danych (precyzja, uzasadnienie itp.) printf
Jest łatwiejsze.
printf
plik, zastępując go fprintf
...
Dwie kwestie, które nie zostały tu wymienione inaczej, które uważam za istotne:
1) cout
przewozi dużo bagażu, jeśli jeszcze nie korzystasz z STL. Dodaje ponad dwukrotnie więcej kodu do pliku obiektowego niż printf
. Dotyczy to równieżstring
i jest to główny powód, dla którego używam własnej biblioteki ciągów.
2) cout
używa przeciążonych <<
operatorów, co uważam za niefortunne. Może to powodować zamieszanie, jeśli również używasz<<
operatora zgodnie z jego przeznaczeniem (przesuń w lewo). Osobiście nie lubię przeciążać operatorów do celów związanych z ich przeznaczeniem.
Konkluzja: Użyję cout
(i string
), jeśli już używam STL. W przeciwnym razie staram się tego unikać.
W przypadku prymitywów prawdopodobnie nie ma to żadnego znaczenia, którego używasz. Mówię, że przydaje się, gdy chcesz wyprowadzać złożone obiekty.
Na przykład, jeśli masz zajęcia,
#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;
}
Teraz powyższe może nie wydawać się aż tak świetne, ale załóżmy, że musisz to wyprowadzić w wielu miejscach w kodzie. Mało tego, powiedzmy, że dodajesz pole „int d”. Dzięki cout musisz go zmienić tylko w jednym miejscu. Jednak z printf musiałbyś go zmienić w możliwie wielu miejscach i nie tylko, musisz sobie przypomnieć, które z nich wydrukować.
Powiedziawszy to, dzięki cout możesz skrócić wiele czasu poświęcanego na utrzymanie swojego kodu i nie tylko, że jeśli ponownie użyjesz obiektu „Coś” w nowej aplikacji, tak naprawdę nie musisz się martwić o wynik.
Oczywiście możesz napisać „coś” nieco lepiej, aby zachować konserwację:
#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;
}
I nieco rozszerzony test cout vs. printf, dodano test „double”, jeśli ktoś chce zrobić więcej testów (Visual Studio 2008, wydanie wersji pliku wykonywalnego):
#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);
}
}
Wynik to:
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
o wiele mniej wydajny niż '\n'
?
endl
opróżnia bufor i \n
nie robi tego, chociaż nie jestem pewien, czy jest to ostatecznie powód.
Chciałbym zaznaczyć, że jeśli chcesz grać wątkami w C ++, jeśli używasz cout
, możesz uzyskać ciekawe wyniki.
Rozważ ten kod:
#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
Teraz dane wyjściowe są tasowane. Może także dawać różne wyniki, spróbuj wykonać kilka razy:
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
Możesz użyć, printf
aby zrobić to dobrze, lub możesz użyć mutex
.
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
Baw się dobrze!
thread
nie powodują, że dane wyjściowe są szalone. Właśnie odtworzyłem i znalazłem jedno xyz
i drugie ABC
. Nie było manglingu b / w ABC
as ABABAB
.
cout
działa z wątkami, ale wiem na pewno, że wyświetlany kod nie jest tym, którego użyłeś do uzyskania tych danych wyjściowych. Kod przekazuje ciąg "ABC"
dla wątku 1 i "xyz"
wątku 2, ale dane wyjściowe pokazują AAA
i BBB
. Napraw to, bo w tej chwili jest to mylące.
cout<< "Hello";
printf("%s", "Hello");
Oba są używane do drukowania wartości. Mają zupełnie inną składnię. C ++ ma oba, C ma tylko printf.
Chciałbym powiedzieć, że brak rozszerzalności nie printf
jest do końca prawdą: w
C jest to prawda. Ale w C nie ma prawdziwych klas.
W C ++ możliwe jest przeciążenie operatora rzutowania, więc przeciążenie char*
operatora i użycie w printf
ten sposób:
Foo bar;
...;
printf("%s",bar);
może być możliwe, jeśli Foo przeładuje dobrego operatora. Lub jeśli zrobiłeś dobrą metodę. Krótko mówiąc, printf
jest tak rozszerzalny jakcout
dla mnie.
Argument techniczny, który widzę dla strumieni C ++ (ogólnie ... nie tylko cout.) To:
Typesafety. (A tak przy okazji, jeśli chcę wydrukować taki, '\n'
którego używamputchar('\n')
... nie użyję bomby nuklearnej do zabicia owada.).
Łatwiej się uczyć. (żadnych „skomplikowanych” parametrów do nauczenia, wystarczy użyć <<
i>>
operatorów)
Pracuj natywnie z std::string
(bo printf
jest std::string::c_str()
, ale dla scanf
?)
Bo printf
widzę:
Łatwiejsze lub co najmniej krótsze (pod względem zapisanych znaków) złożone formatowanie. O wiele bardziej czytelne dla mnie (chyba kwestia gustu).
Lepsza kontrola nad tym, co zrobiła funkcja (Zwróć ile znaków zostało napisanych, a jest %n
formatyzator: „Nic nie wydrukowano. Argument musi być wskaźnikiem do podpisanej liczby wewnętrznej, w której przechowywana jest liczba zapisanych do tej pory znaków.” (Z printf - C ++ Reference )
Lepsze możliwości debugowania. Z tego samego powodu, co ostatni argument.
Moje osobiste preferencje dotyczą funkcji printf
(i scanf
), głównie dlatego, że uwielbiam krótkie wiersze i ponieważ nie sądzę, że trudno jest uniknąć problemów z drukowaniem tekstu. Jedyne, co wdrażam za pomocą funkcji w stylu C, to to, że std::string
nie jest obsługiwane. Musimy przejść char*
przed, zanim go oddasz printf
(z tym, std::string::c_str()
jeśli chcemy czytać, ale jak pisać?)
char*
nie będzie używana.
char*
życia i jak długo, a także niebezpieczeństwa zdefiniowane przez użytkownika niejawne obsady.
Więcej różnic: „printf” zwraca wartość całkowitą (równą liczbie drukowanych znaków), a „cout” nic nie zwraca
I.
cout << "y = " << 7;
nie jest atomowy.
printf("%s = %d", "y", 7);
jest atomowy.
cout wykonuje kontrolę typu, printf nie.
Nie ma odpowiednika iostream "% d"
cout
nic nie zwraca, ponieważ jest to obiekt, a nie funkcja. operator<<
zwraca coś (zwykle jest to lewy operand, ale w przypadku błędu występuje wartość fałszywa). I w jakim sensie jest to printf
wezwanie „atomowy”?
printf("%s\n",7);
%s
jest ?
printf
% s argument musi mieć ważny wskaźnik do NUL łańcuch. Zakres pamięci „7” (wskaźnik) zwykle nie jest prawidłowy; błąd segmentacji może mieć szczęście. W niektórych systemach „7” może wydrukować dużo śmieci na konsolę i trzeba by na nie spojrzeć przez jeden dzień, zanim program się zatrzyma. Innymi słowy, jest to zła rzecz printf
. Narzędzia analizy statycznej mogą wychwycić wiele z tych problemów.
printf
nie sprawdza on typów, nigdy nie korzystałem z kompilatora, który nie ostrzegałby mnie przed błędami podczas pisania printf
...
TL; DR: Zawsze ufaj wygenerowanemu kodowi maszynowemu , wydajności , czytelności i czasowi kodowania, zanim zaufasz losowym komentarzom online, w tym również temu.
Nie jestem ekspertem. Właśnie słyszałem dwóch współpracowników rozmawiających o tym, jak powinniśmy unikać używania C ++ w systemach wbudowanych z powodu problemów z wydajnością. Co ciekawe, zrobiłem test porównawczy oparty na prawdziwym zadaniu projektowym.
W tym zadaniu musieliśmy zapisać konfigurację do pamięci RAM. Coś jak:
kawa = gorący
cukier = brak
mleka = pierś
mac = AA: BB: CC: DD: EE: FF
Oto moje programy testowe (tak, wiem, że OP pytał o printf (), a nie fprintf (). Spróbuj uchwycić istotę, a przy okazji, link OP wskazuje na fprintf ().)
Program 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);
Program 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();
Zrobiłem co w mojej mocy, aby je wypolerować, zanim zapętliłem je oba 100 000 razy. Oto wyniki:
Program C:
real 0m 8.01s
user 0m 2.37s
sys 0m 5.58s
Program C ++:
real 0m 6.07s
user 0m 3.18s
sys 0m 2.84s
Rozmiar pliku obiektu:
C - 2,092 bytes
C++ - 3,272 bytes
Wniosek: na mojej bardzo specyficznej platformie , z bardzo specyficznym procesorem , z bardzo specyficzną wersją jądra Linuksa , aby uruchomić program, który jest skompilowany z bardzo konkretną wersją GCC , w celu wykonania bardzo konkretnego zadania , powiedziałbym podejście C ++ jest bardziej odpowiednie, ponieważ działa znacznie szybciej i zapewnia znacznie lepszą czytelność. Z drugiej strony C oferuje niewielką powierzchnię, moim zdaniem nie znaczy prawie nic, ponieważ rozmiar programu nie jest naszym problemem.
Pamiętaj, YMMV.
Nie jestem programistą, ale byłem inżynierem czynników ludzkich. Uważam, że język programowania powinien być łatwy do nauczenia się, rozumienia i używania, a to wymaga prostej i spójnej struktury językowej. Chociaż wszystkie języki są symboliczne, a zatem u ich podstaw są arbitralne, istnieją konwencje, a ich przestrzeganie ułatwia naukę i używanie języka.
Istnieje ogromna liczba funkcji w C ++ i innych językach zapisanych jako funkcja (parametr), składnia, która pierwotnie była używana do relacji funkcjonalnych w matematyce w epoce przed komputerem. printf()
postępuje zgodnie z tą składnią i jeśli autorzy C ++ chcieli stworzyć inną logicznie odmienną metodę odczytu i zapisu plików, mogliby po prostu stworzyć inną funkcję przy użyciu podobnej składni.
W Pythonie możemy oczywiście drukować przy użyciu również dość standardowej object.method
składni, tj. Variablename.print, ponieważ zmienne są obiektami, ale w C ++ nimi nie są.
Nie przepadam za składnią cout, ponieważ operator << nie przestrzega żadnych reguł. Jest to metoda lub funkcja, tzn. Bierze parametr i coś z nim robi. Jest jednak napisany tak, jakby był operatorem porównania matematycznego. Jest to złe podejście z ludzkiego punktu widzenia.
printf
jest funkcją, podczas gdy cout
jest zmienną.
printf
jest funkcją, ale printf()
jest wywołaniem funkcji =)