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 doqsort
(2 razy), w koszt rozmiaru pliku wykonywalnego).Z C ++ FAQ :
Z drugiej strony
printf
jest znacznie szybszy, co może uzasadniać korzystanie z niego zamiastcout
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 /)źródło
printf()
ma być również rozszerzalny. Zobacz „haczyki printf” na stronie udrepper.livejournal.com/20948.htmlprintf
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:Wniosek: jeśli chcesz tylko nowe wiersze, użyj
printf
; w przeciwnym raziecout
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).źródło
printf()
istd::ostream
polega na tym, że poprzednie generuje wszystkie argumenty w jednym wywołaniu, podczas gdystd::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
lubfprintf
istringstream
lubfstream
.I cytuję :
źródło
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.źródło
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 :)
3) Uważam, że jest bardziej czytelny, szczególnie gdy mamy wiele parametrów.
Jednym z problemów
cout
są opcje formatowania. Formatowanie danych (precyzja, uzasadnienie itp.)printf
Jest łatwiejsze.źródło
printf
plik, zastępując gofprintf
...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
(istring
), jeśli już używam STL. W przeciwnym razie staram się tego unikać.źródło
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,
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.
źródło
Oczywiście możesz napisać „coś” nieco lepiej, aby zachować konserwację:
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):
Wynik to:
źródło
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:
Teraz dane wyjściowe są tasowane. Może także dawać różne wyniki, spróbuj wykonać kilka razy:
Możesz użyć,
printf
aby zrobić to dobrze, lub możesz użyćmutex
.Baw się dobrze!
źródło
thread
nie powodują, że dane wyjściowe są szalone. Właśnie odtworzyłem i znalazłem jednoxyz
i drugieABC
. Nie było manglingu b / wABC
asABABAB
.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
iBBB
. Napraw to, bo w tej chwili jest to mylące.Oba są używane do drukowania wartości. Mają zupełnie inną składnię. C ++ ma oba, C ma tylko printf.
źródło
Chciałbym powiedzieć, że brak rozszerzalności nie
printf
jest do końca prawdą: wC 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 wprintf
ten sposób: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
(boprintf
jeststd::string::c_str()
, ale dlascanf
?)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
(iscanf
), 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, żestd::string
nie jest obsługiwane. Musimy przejśćchar*
przed, zanim go oddaszprintf
(z tym,std::string::c_str()
jeśli chcemy czytać, ale jak pisać?)źródło
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"
źródło
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 toprintf
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 rzeczprintf
. 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 pisaniaprintf
...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:
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:
Program C ++:
Zrobiłem co w mojej mocy, aby je wypolerować, zanim zapętliłem je oba 100 000 razy. Oto wyniki:
Program C:
Program C ++:
Rozmiar pliku obiektu:
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.
źródło
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.
źródło
printf
jest funkcją, podczas gdycout
jest zmienną.źródło
printf
jest funkcją, aleprintf()
jest wywołaniem funkcji =)