Saya menggunakan banyak printf
untuk melacak / mencatat tujuan dalam kode saya, saya telah menemukan bahwa itu adalah sumber kesalahan pemrograman. Saya selalu menemukan operator penyisipan ( <<
) agak aneh tapi saya mulai berpikir bahwa dengan menggunakannya, saya bisa menghindari beberapa bug ini.
Adakah yang pernah memiliki wahyu yang sama atau saya hanya memahami sedotan di sini?
Beberapa mengambil poin
- Garis pemikiran saya saat ini adalah bahwa keamanan jenis lebih penting daripada manfaat menggunakan printf. Masalah sebenarnya adalah string format dan penggunaan fungsi variadic yang tidak aman.
- Mungkin saya tidak akan menggunakan
<<
dan varian aliran keluaran stl tapi saya pasti akan melihat ke dalam menggunakan mekanisme tipe-aman yang sangat mirip. - Banyak tracing / logging bersyarat tetapi saya ingin selalu menjalankan kode untuk tidak ketinggalan bug dalam pengujian hanya karena itu cabang yang jarang diambil.
printf
di dunia C ++? Saya melewatkan sesuatu di sini?printf
dalam C ++. (Apakah itu ide yang bagus adalah pertanyaan lain.)printf
memang memiliki beberapa kelebihan; lihat jawaban saya.Jawaban:
printf, terutama dalam kasus di mana Anda mungkin peduli dengan kinerja (seperti sprintf dan fprintf) adalah hack yang sangat aneh. Itu terus-menerus membuat saya kagum bahwa orang-orang yang menggebrak C ++ karena kinerja miniscule overhead yang terkait dengan fungsi virtual kemudian akan pergi untuk mempertahankan io C.
Ya, untuk mengetahui format output kami, sesuatu yang dapat kita ketahui 100% pada waktu kompilasi, mari kita parsing string format fricken saat runtime di dalam tabel lompatan besar yang aneh menggunakan kode format yang tidak dapat dipahami!
Tentu saja kode format ini tidak dapat dibuat untuk mencocokkan jenis yang diwakilinya, itu akan terlalu mudah ... dan Anda diingatkan setiap kali Anda mencari apakah itu% llg atau% lg bahwa bahasa (diketik) ini membuat Anda mencari tipe secara manual untuk mencetak / memindai sesuatu, DAN dirancang untuk prosesor pra-32bit.
Saya akui bahwa penanganan lebar dan presisi format C ++ sangat besar dan bisa menggunakan sedikit gula sintaksis, tetapi itu tidak berarti Anda harus mempertahankan peretasan aneh yang merupakan sistem io utama C. Dasar-dasar absolut cukup mudah dalam kedua bahasa (walaupun Anda mungkin harus menggunakan sesuatu seperti fungsi kesalahan / aliran kesalahan kustom untuk kode debug), kasus-kasus moderat seperti regex seperti dalam C (mudah untuk menulis, sulit untuk mengurai / debug ), dan kasus kompleks tidak mungkin dalam C.
(Jika Anda menggunakan kontainer standar sama sekali, tulis sendiri beberapa operator templated cepat << kelebihan yang memungkinkan Anda melakukan hal-hal seperti
std::cout << my_list << "\n";
untuk debug, di mana my_list adalah tipelist<vector<pair<int,string> > >
.)sumber
operator<<(ostream&, T)
dengan memanggil ... yahsprintf
,! Kinerjasprintf
tidak optimal, tetapi karena ini, kinerja iostreams secara umum bahkan lebih buruk.Mencampur keluaran gaya-C
printf()
(atauputs()
atauputchar()
...) denganstd::cout << ...
keluaran gaya C ++ bisa tidak aman. Jika saya ingat dengan benar, mereka dapat memiliki mekanisme buffering yang terpisah, sehingga output mungkin tidak muncul dalam urutan yang dimaksud. (Seperti yang disebutkan oleh Pemrogram AP dalam komentar, atasisync_with_stdio
ini).printf()
pada dasarnya tipe-tidak aman. Jenis yang diharapkan untuk suatu argumen ditentukan oleh string format ("%d"
memerlukanint
atau sesuatu yang dipromosikanint
,"%s"
membutuhkanchar*
yang harus menunjuk ke string gaya-C yang dihentikan dengan benar, dll.), Tetapi meneruskan jenis argumen yang salah menghasilkan perilaku yang tidak terdefinisi , bukan kesalahan yang dapat didiagnosis. Beberapa kompiler, seperti gcc, melakukan pekerjaan peringatan yang cukup baik tentang ketidakcocokan jenis, tetapi mereka dapat melakukannya hanya jika format string adalah literal atau dikenal pada waktu kompilasi (yang merupakan kasus paling umum) - dan semacamnya peringatan tidak dibutuhkan oleh bahasa. Jika Anda melewati jenis argumen yang salah, hal-hal buruk yang sewenang-wenang dapat terjadi.Aliran C ++ I / O, di sisi lain, jauh lebih aman untuk jenis, karena
<<
operator kelebihan beban untuk berbagai jenis.std::cout << x
tidak harus menentukan jenisx
; kompiler akan menghasilkan kode yang tepat untuk tipe apa punx
.Di sisi lain,
printf
opsi pemformatan adalah IMHO jauh lebih nyaman. Jika saya ingin mencetak nilai titik-mengambang dengan 3 digit setelah titik desimal, saya dapat menggunakan"%.3f"
- dan itu tidak berpengaruh pada argumen lain, bahkan dalamprintf
panggilan yang sama .setprecision
Sebaliknya, C ++ mempengaruhi kondisi aliran, dan dapat mengacaukan keluaran nanti jika Anda tidak terlalu berhati-hati untuk mengembalikan aliran ke keadaan sebelumnya. (Ini adalah kencing hewan peliharaan pribadi saya; jika saya kehilangan beberapa cara bersih untuk menghindarinya, silakan berkomentar.)Keduanya memiliki kelebihan dan kekurangan. Ketersediaan
printf
sangat berguna jika Anda memiliki latar belakang C dan Anda lebih mengenalnya, atau jika Anda mengimpor kode sumber C ke dalam program C ++.std::cout << ...
lebih idiomatis untuk C ++, dan tidak memerlukan banyak perawatan untuk menghindari ketidakcocokan tipe. Keduanya valid C ++ (standar C ++ mencakup sebagian besar pustaka standar C dengan referensi).Ini mungkin terbaik untuk menggunakan
std::cout << ...
untuk kepentingan C ++ programmer lain yang dapat bekerja pada kode Anda, tetapi Anda dapat menggunakan salah satu - terutama dalam kode jejak bahwa Anda akan membuang.Dan tentu saja ada baiknya menghabiskan waktu belajar bagaimana menggunakan debugger (tapi itu mungkin tidak layak di beberapa lingkungan).
sumber
printf
.std::ios_base::sync_with_stdio
.std::cout
menggunakan panggilan terpisah untuk setiap item yang dicetak? Anda dapat mengatasinya dengan mengumpulkan garis keluaran ke dalam string sebelum mencetaknya. Dan tentu saja Anda juga dapat mencetak satu item sekaligus denganprintf
; lebih mudah mencetak satu baris (atau lebih) dalam satu panggilan.Masalah Anda kemungkinan besar berasal dari pencampuran dua manajer keluaran standar yang sangat berbeda, yang masing-masing memiliki agenda sendiri untuk STDOUT kecil yang malang itu. Anda tidak mendapatkan jaminan tentang bagaimana mereka diimplementasikan, dan sangat mungkin bahwa mereka mengatur opsi deskriptor file yang saling bertentangan, keduanya mencoba melakukan hal-hal yang berbeda untuk itu, dll. Juga, operator penyisipan memiliki satu lebih besar
printf
:printf
akan membiarkan Anda melakukan ini:Padahal
<<
tidak akan.Catatan: Untuk debugging, Anda tidak menggunakan
printf
ataucout
. Anda menggunakanfprintf(stderr, ...)
dancerr
.sumber
printf
tidak aman untuk tipe dan saya pikir saat ini adalah keselamatan jenis melebihi manfaat apa pun dari penggunaanprintf
. Masalahnya adalah format string dan fungsi variadic tidak aman.SomeObject
bukan penunjuk? Anda akan mendapatkan data biner acak yang diputuskan oleh kompilatorSomeObject
.Ada banyak grup - misalnya google - yang tidak suka stream.
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Streams
(Pop membuka segitiga thingy sehingga Anda dapat melihat diskusi.) Saya pikir panduan gaya google C ++ memiliki BANYAK saran yang sangat masuk akal.
Saya pikir tradeoffnya adalah stream lebih aman tetapi printf lebih jelas untuk dibaca (dan lebih mudah untuk mendapatkan format yang Anda inginkan).
sumber
printf
dapat menyebabkan bug karena kurangnya keamanan jenis. Ada beberapa cara untuk mengatasi itu tanpa beralih keiostream
's<<
operator dan lebih rumit format:printf
string format Anda terhadapprintf
argumen dan dapat menampilkan peringatan seperti berikut jika tidak cocok.printf
panggilan -gaya untuk membuat mereka mengetik-aman.printf
string format-like (Boost.Format's hampir identik denganprintf
) sambil menjagaiostreams
keamanan jenis dan jenis ekstensibilitas.sumber
Sintaks printf pada dasarnya baik-baik saja, dikurangi beberapa pengetikan yang tidak jelas. Jika Anda pikir itu salah mengapa C #, Python dan bahasa lain menggunakan konstruksi yang sangat mirip? Masalah dalam C atau C ++: itu bukan bagian dari bahasa dan karenanya tidak diperiksa oleh kompiler untuk sintaks yang benar (*) dan tidak terurai menjadi serangkaian panggilan asli jika mengoptimalkan kecepatan. Perhatikan bahwa jika mengoptimalkan ukuran, panggilan printf mungkin menjadi lebih efisien! Sintaks streaming C ++ juga bagus. Berhasil, jenis-keamanan ada, tetapi sintaks verbose ... bleh. Maksudku, aku menggunakannya, tapi tanpa sukacita.
(*) beberapa kompiler MELAKUKAN pemeriksaan ini dan hampir semua alat analisis statis (saya menggunakan Lint dan tidak pernah memiliki masalah dengan printf sejak itu).
sumber
format("fmt") % arg1 % arg2 ...;
) dengan tipe-safety. Dengan biaya lebih banyak kinerja, karena menghasilkan panggilan stringstream yang secara internal menghasilkan panggilan sprintf di banyak implementasi.printf
menurut pendapat saya sendiri, alat output yang jauh lebih fleksibel untuk menangani variabel daripada output stream CPP. Sebagai contoh:Namun, di mana Anda mungkin ingin menggunakan
<<
operator CPP adalah ketika Anda membebani itu untuk metode tertentu ... misalnya untuk mendapatkan dump objek yang menyimpan data orang tertentu,PersonData
....Untuk itu, akan jauh lebih efektif untuk mengatakan (dengan asumsi
a
objek objek PersonData)dari:
Yang pertama jauh lebih sejalan dengan prinsip enkapsulasi (Tidak perlu mengetahui secara spesifik, variabel anggota pribadi), dan juga lebih mudah dibaca.
sumber
Anda tidak seharusnya menggunakan
printf
C ++. Pernah. Alasannya adalah, seperti yang Anda catat dengan benar, bahwa itu adalah sumber bug dan fakta bahwa mencetak jenis khusus, dan di C ++ hampir semuanya harus jenis khusus, adalah rasa sakit. Solusi C ++ adalah stream.Namun ada masalah kritis yang membuat stream tidak cocok untuk output apa pun dan yang terlihat pengguna! Masalahnya adalah terjemahan. Contoh meminjam dari manual gettext mengatakan Anda ingin menulis:
Sekarang penerjemah Jerman datang dan berkata: Oke, dalam bahasa Jerman, pesannya seharusnya
Dan sekarang Anda berada dalam masalah, karena dia membutuhkan potongan-potongan di sekitar. Harus dikatakan, bahwa bahkan banyak implementasi
printf
memiliki masalah dengan ini. Kecuali jika mereka mendukung ekstensi sehingga Anda dapat menggunakannyaThe Boost.Format mendukung format printf-gaya dan memiliki fitur ini. Jadi, Anda menulis:
Sayangnya itu membawa sedikit penalti kinerja, karena secara internal ia menciptakan stringstream dan menggunakan
<<
operator untuk memformat setiap bit dan dalam banyak implementasi<<
operator panggilan internalsprintf
. Saya menduga implementasi yang lebih efisien akan mungkin jika benar-benar diinginkan.sumber
Anda melakukan banyak pekerjaan yang tidak berguna, selain fakta bahwa
stl
itu jahat atau tidak, debug kode Anda dengan serangkaianprintf
hanya menambah 1 tingkat kegagalan yang mungkin.Cukup gunakan debugger dan baca sesuatu tentang Pengecualian dan cara menangkap dan melemparnya; cobalah untuk tidak menjadi lebih bertele-tele daripada yang sebenarnya Anda butuhkan.
PS
printf
digunakan dalam C, untuk C ++ yang Anda milikistd::cout
sumber