'printf' vs. 'cout' di C ++


Jawaban:


333

Saya terkejut bahwa semua orang dalam pertanyaan ini mengklaim bahwa std::coutitu jauh lebih baik daripada printf, bahkan jika pertanyaannya hanya menanyakan perbedaan. Sekarang, ada perbedaan - std::coutadalah C ++, dan printfC (namun, Anda dapat menggunakannya dalam C ++, seperti hampir semua hal lain dari C). Sekarang, saya akan jujur ​​di sini; keduanya printfdan std::coutmemiliki kelebihan mereka.

Perbedaan nyata

Kemungkinan diperpanjang

std::coutdapat diperpanjang. Saya tahu bahwa orang-orang akan mengatakan bahwa printfitu juga dapat diperluas, tetapi ekstensi tersebut tidak disebutkan dalam standar C (jadi Anda harus menggunakan fitur-fitur non-standar - tetapi bahkan fitur non-standar yang umum ada), dan ekstensi tersebut adalah satu huruf (jadi mudah untuk konflik dengan format yang sudah ada).

Tidak seperti itu printf, std::coutsepenuhnya bergantung pada overloading operator, jadi tidak ada masalah dengan format khusus - yang Anda lakukan hanyalah mendefinisikan pengambilan subrutin std::ostreamsebagai argumen pertama dan tipe Anda sebagai yang kedua. Dengan demikian, tidak ada masalah namespace - selama Anda memiliki kelas (yang tidak terbatas pada satu karakter), Anda dapat melakukan std::ostreamoverloading untuk itu.

Namun, saya ragu bahwa banyak orang ingin memperpanjang ostream(jujur ​​saja, saya jarang melihat ekstensi seperti itu, meskipun mudah dibuat). Namun, ada di sini jika Anda membutuhkannya.

Sintaksis

Karena dapat dengan mudah diperhatikan, keduanya printfdan std::coutmenggunakan sintaks yang berbeda. printfmenggunakan sintaks fungsi standar menggunakan string pola dan daftar argumen panjang variabel. Sebenarnya, printfadalah alasan mengapa C memilikinya - printfformat terlalu kompleks untuk dapat digunakan tanpa mereka. Namun, std::coutgunakan API yang berbeda - operator <<API yang mengembalikan sendiri.

Secara umum, itu berarti versi C akan lebih pendek, tetapi dalam kebanyakan kasus itu tidak masalah. Perbedaannya terlihat ketika Anda mencetak banyak argumen. Jika Anda harus menulis sesuatu seperti Error 2: File not found., dengan asumsi nomor kesalahan, dan deskripsinya adalah placeholder, kode akan terlihat seperti ini. Kedua contoh berfungsi secara identik (well, semacam, std::endlsebenarnya mem-flush buffer).

printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;

Meskipun ini tidak terlihat terlalu gila (hanya dua kali lebih lama), banyak hal menjadi lebih gila ketika Anda benar-benar memformat argumen, bukan hanya mencetaknya. Sebagai contoh, mencetak sesuatu seperti 0x0424itu gila. Ini disebabkan oleh std::coutpencampuran nilai keadaan dan aktual. Saya tidak pernah melihat bahasa di mana sesuatu seperti std::setfillakan menjadi tipe (selain C ++, tentu saja). printfjelas memisahkan argumen dan tipe aktual. Saya benar-benar lebih suka mempertahankan printfversi itu (bahkan jika itu terlihat agak samar) dibandingkan dengan iostreamversi itu (karena mengandung terlalu banyak suara).

printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;

Terjemahan

Di sinilah keunggulan nyata printfkebohongan. The printfformat string baik ... string. Itu membuatnya sangat mudah untuk diterjemahkan, dibandingkan dengan operator <<penyalahgunaan iostream. Dengan asumsi bahwa gettext()fungsi menerjemahkan, dan Anda ingin menunjukkan Error 2: File not found., kode untuk mendapatkan terjemahan dari string format yang ditampilkan sebelumnya akan terlihat seperti ini:

printf(gettext("Error %d: %s.\n"), id, errors[id]);

Sekarang, mari kita asumsikan bahwa kita menerjemahkan ke Fictionish, di mana nomor kesalahannya setelah deskripsi. String yang diterjemahkan akan terlihat seperti %2$s oru %1$d.\n. Sekarang, bagaimana cara melakukannya di C ++? Yah, saya tidak tahu. Saya kira Anda dapat membuat palsu iostreamyang membangun printfAnda dapat lulus gettext, atau sesuatu, untuk keperluan terjemahan. Tentu saja, $ini bukan standar C, tapi itu sangat umum sehingga aman digunakan menurut saya.

Tidak harus mengingat / mencari sintaks tipe integer spesifik

C memiliki banyak tipe integer, dan begitu juga C ++. std::coutmenangani semua jenis untuk Anda, sementara printfmemerlukan sintaksis khusus tergantung pada tipe integer (ada tipe non-integer, tetapi satu-satunya tipe non-integer yang akan Anda gunakan dalam praktiknya printfadalah const char *(string C, dapat diperoleh dengan menggunakan to_cmetode std::string)). Misalnya, untuk mencetak size_t, Anda harus menggunakan %zd, sementara int64_takan membutuhkan penggunaan %"PRId64". Tabel tersedia di http://en.cppreference.com/w/cpp/io/c/fprintf dan http://en.cppreference.com/w/cpp/types/integer .

Anda tidak dapat mencetak byte NUL, \0

Karena printfmenggunakan string C yang bertentangan dengan string C ++, ia tidak dapat mencetak byte NUL tanpa trik khusus. Dalam kasus-kasus tertentu itu mungkin untuk menggunakan %cdengan '\0'sebagai argumen, walaupun itu jelas hack.

Perbedaan tidak ada yang peduli

Performa

Pembaruan: Ternyata iostreamsangat lambat sehingga biasanya lebih lambat dari hard drive Anda (jika Anda mengarahkan program Anda ke file). Menonaktifkan sinkronisasi dengan stdiodapat membantu, jika Anda perlu menampilkan banyak data. Jika kinerja adalah masalah nyata (bukan menulis beberapa baris untuk STDOUT), gunakan saja printf.

Semua orang berpikir bahwa mereka peduli dengan kinerja, tetapi tidak ada yang peduli untuk mengukurnya. Jawaban saya adalah bahwa I / O adalah bottleneck, tidak peduli apakah Anda menggunakan printfatau iostream. Saya pikir itu printf bisa lebih cepat dari melihat cepat ke perakitan (dikompilasi dengan dentang menggunakan -O3opsi kompilator). Dengan asumsi contoh kesalahan saya, printfcontoh melakukan panggilan jauh lebih sedikit daripada coutcontoh. Ini int maindengan 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:

Anda dapat dengan mudah melihat bahwa dua string, dan 2(angka) didorong sebagai printfargumen. Itu saja; tidak ada yang lain. Sebagai perbandingan, ini iostreamdikompilasi untuk perakitan. Tidak, tidak ada inlining; setiap operator <<panggilan berarti panggilan lain dengan serangkaian argumen lain.

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:

Namun, jujur ​​saja, ini tidak ada artinya, karena I / O adalah hambatannya. Saya hanya ingin menunjukkan bahwa iostreamitu tidak lebih cepat karena "ketik aman". Sebagian besar implementasi C mengimplementasikan printfformat menggunakan goto yang dikomputasi, jadi printfini secepat mungkin, bahkan tanpa diketahui oleh kompiler printf(bukan tidak - beberapa kompiler dapat mengoptimalkan printfdalam kasus-kasus tertentu - string akhir konstan \nbiasanya dioptimalkan untuk puts) .

Warisan

Saya tidak tahu mengapa Anda ingin mewarisi ostream, tetapi saya tidak peduli. Mungkin FILEjuga dengan itu.

class MyFile : public FILE {}

Ketik keamanan

Benar, daftar argumen panjang variabel tidak memiliki keamanan, tetapi itu tidak masalah, karena kompiler C populer dapat mendeteksi masalah dengan printfstring format jika Anda mengaktifkan peringatan. Bahkan, Dentang bisa melakukan itu tanpa mengaktifkan peringatan.

$ 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);
     ^

18
Anda mengatakan I / O adalah hambatannya. Jelas Anda tidak pernah menguji asumsi itu. Saya mengutip diri saya sendiri: "Di sisi lain, versi iostreams, pada 75,3 MB / s, tidak dapat buffer data cukup cepat untuk bersaing dengan hard disk. Itu buruk, dan bahkan belum melakukan pekerjaan nyata. Saya tidak Saya kira harapan saya terlalu tinggi ketika saya mengatakan bahwa perpustakaan I / O saya harus dapat memenuhi kontroler disk saya. "
Ben Voigt

4
@ BenVoigt: Saya akui, saya mencoba menghindari C ++ jika memungkinkan. Saya mencoba banyak menggunakannya, tetapi itu lebih menjengkelkan, dan kurang terpelihara daripada bahasa pemrograman lain yang saya gunakan. Ini adalah alasan lain bagi saya untuk menghindari C ++ - ini bahkan tidak cepat (bahkan bukan iostream - seluruh perpustakaan C ++ lambat dalam sebagian besar implementasi, mungkin dengan pengecualian untuk std::sort, yang entah bagaimana sangat cepat dibandingkan dengan qsort(2 kali), di biaya ukuran yang dapat dieksekusi).
Konrad Borowski

3
Tidak ada seorang pun di sini yang menyebutkan masalah di lingkungan paralel saat menggunakan cout.
Nicholas Hamilton

9
Argumen kinerja Anda tidak masuk akal sama sekali. Semakin banyak perakitan dalam program Anda tidak berarti program itu akan lebih lambat, karena Anda tidak menghitung semua kode yang membuat fungsi printf, yang merupakan banyak kode. Menurut pendapat saya, dimungkinkan untuk mengoptimalkan cout dengan << operator jauh lebih baik daripada printf, karena kompiler dapat membuat variabel dan format yang lebih baik.
Ignas2526

18
Saya suka banyak hal tentang jawaban ini, tetapi mungkin bagian favorit saya adalah "Semua orang berpikir bahwa mereka peduli dengan kinerja, tetapi tidak ada yang mengganggu untuk mengukurnya."
Kyle Strand

203

Dari FAQ C ++ :

[15.1] Mengapa saya harus menggunakan <iostream> daripada tradisional <cstdio>?

Tingkatkan keamanan jenis, kurangi kesalahan, memungkinkan ekstensibilitas, dan memberikan warisan.

printf()mungkin tidak rusak, dan scanf()mungkin layak huni meskipun rentan kesalahan, namun keduanya terbatas sehubungan dengan apa yang dapat dilakukan C ++ I / O. C ++ I / O (menggunakan <<dan >>) adalah, relatif terhadap C (menggunakan printf()dan scanf()):

  • Lebih banyak tipe-aman: Dengan <iostream>, jenis objek yang saya / O akan dikenal secara statis oleh kompiler. Sebaliknya, <cstdio>gunakan bidang "%" untuk mengetahui jenis secara dinamis.
  • Kurang rawan kesalahan: Dengan <iostream>, tidak ada token "%" yang berlebihan yang harus konsisten dengan objek aktual yang saya / O miliki. Menghapus redundansi menghapus kelas kesalahan.
  • Extensible: <iostream>Mekanisme C ++ memungkinkan tipe baru yang ditentukan pengguna menjadi I / O'd tanpa melanggar kode yang ada. Bayangkan kekacauan jika semua orang secara bersamaan menambahkan bidang "%" yang tidak kompatibel ke printf()dan scanf()?!
  • Inheritable: <iostream>Mekanisme C ++ dibangun dari kelas nyata seperti std::ostreamdan std::istream. Tidak seperti <cstdio>itu FILE*, ini adalah kelas nyata dan karenanya dapat diwariskan. Ini berarti Anda dapat memiliki hal-hal lain yang ditetapkan pengguna yang terlihat dan bertindak seperti aliran, namun itu melakukan hal-hal aneh dan indah apa pun yang Anda inginkan. Anda secara otomatis dapat menggunakan zillions baris kode I / O yang ditulis oleh pengguna yang bahkan tidak Anda kenal, dan mereka tidak perlu tahu tentang kelas "aliran diperluas" Anda.

Di sisi lain, printfsecara signifikan lebih cepat, yang dapat membenarkan menggunakannya dalam preferensi untuk coutdi sangat kasus-kasus tertentu dan terbatas. Selalu profil dulu. (Lihat, misalnya, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)


2
Di sisi lain, ada perpustakaan FastFormat ( fastformat.org ), yang menawarkan tipe-safety, ekspresivitas, dan kinerja sekaligus. (Bukan berarti saya belum mencobanya ...)
xtofl

3
@ Marscelo mungkin karena ringkasan yang bagus, dengan semua yang dikutip. Formatnya ... ya, itu sangat buruk. Saya seharusnya memperbaikinya sendiri, tetapi tampaknya orang lain (termasuk Anda sendiri) yang merawatnya, yang, tentu saja, lebih konstruktif daripada sekadar merengek.
Mikeage

2
Pada akhir-akhir printf()ini juga seharusnya bisa diperluas. Lihat "printf hooks" di udrepper.livejournal.com/20948.html
Maxim Egorushkin

4
@ MaximYegorushkin: Standar printftidak memiliki kemampuan seperti itu. Mekanisme pustaka non-portabel hampir tidak pada tingkat yang sama dengan diperpanjangnya iostreams sepenuhnya standar.
Ben Voigt

4
"Di sisi lain, printf secara signifikan lebih cepat" printf juga lebih bersih dan lebih mudah digunakan, itulah sebabnya saya menghindari cout jika memungkinkan.
FluorescentGreen5

43

Orang sering mengklaim itu printfjauh lebih cepat. Ini sebagian besar adalah mitos. Saya baru saja mengujinya, dengan hasil sebagai berikut:

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

Kesimpulan: jika Anda hanya menginginkan baris baru, gunakan printf; jika tidak, couthampir secepat, atau bahkan lebih cepat. Rincian lebih lanjut dapat ditemukan di blog saya .

Untuk lebih jelasnya, saya tidak berusaha mengatakan bahwa iostreamselalu lebih baik daripada printf; Saya hanya mencoba untuk mengatakan bahwa Anda harus membuat keputusan berdasarkan data nyata, bukan tebakan liar berdasarkan beberapa asumsi umum yang menyesatkan.

Pembaruan: Ini kode lengkap yang saya gunakan untuk pengujian. Dikompilasi dengan g++tanpa opsi tambahan (selain dari -lrtwaktunya).

#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);
    }
}

5
Dalam skor Anda, printf mengalahkan ketukan dengan mudah (sebagian besar kasus). Saya bertanya-tanya mengapa Anda merekomendasikan menggunakan cout ketika datang ke perf. Meskipun saya setuju perf tidak terlalu berbeda dalam kasus realistis ..
mishal153

3
@ mishal153: Saya hanya mencoba untuk mengatakan bahwa kinerjanya tidak terlalu berbeda, jadi saran yang sering didengar dari "jangan pernah menggunakan cout karena ini sangat lambat" benar-benar bodoh. Perhatikan bahwa cout memiliki keuntungan yang jelas dari keamanan jenis, dan sering dibaca juga. (Pemformatan titik-mengambang dengan iostreams mengerikan ...)
Thomas

35
Perbedaan penting antara printf()dan std::ostreamadalah bahwa mantan menampilkan semua argumen dalam satu panggilan tunggal sedangkan std::ostreampanggilan terpisah untuk masing-masing <<. Tes hanya menghasilkan satu argumen dan baris baru, itu sebabnya Anda tidak dapat melihat perbedaannya.
Maxim Egorushkin

12
Kompiler harus dapat menyatukan panggilan-panggilan ini. Juga, printfmungkin membuat banyak panggilan di bawah penutup untuk membantu fungsi berbagai penentu format ... itu, atau itu fungsi monolitik mengerikan. Dan lagi, karena inlining, seharusnya tidak membuat perbedaan dalam kecepatan sama sekali.
Thomas

4
Anda mengatur waktu terminal Anda. Gunakan sprintfatau fprintfdan stringstreamatau fstream.
Ben Voigt

41

Dan saya kutip :

Dalam istilah tingkat tinggi, perbedaan utama adalah keselamatan jenis (cstdio tidak memilikinya), kinerja (sebagian besar implementasi iostreams lebih lambat daripada yang cstdio) dan ekstensibilitas (iostreams memungkinkan target output khusus dan output mulus dari tipe yang ditentukan pengguna).


Terutama pada unix di mana dengan POSIX Anda tidak pernah tahu ukuran apa yang dimiliki salah satu typedef sehingga Anda membutuhkan banyak gips atau 99% dari program Anda hanya mengambil risiko dengan% d. Butuh waktu lama bahkan sebelum% z datang dengan C99. Tetapi untuk time_t / off_t pencarian untuk instruksi format yang benar berlanjut.
Lothar

30

Salah satunya adalah fungsi yang mencetak ke stdout. Yang lain adalah objek yang menyediakan beberapa fungsi anggota dan kelebihan operator<<cetak itu untuk stdout. Ada banyak lagi perbedaan yang bisa saya sebutkan, tetapi saya tidak yakin apa yang Anda cari.


12

Bagi saya, perbedaan nyata yang akan membuat saya memilih 'cout' daripada 'printf' adalah:

1) << operator dapat kelebihan beban untuk kelas saya.

2) Aliran keluaran untuk cout dapat dengan mudah diubah menjadi file: (: copy paste :)

#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) Saya menemukan cout lebih mudah dibaca, terutama ketika kita memiliki banyak parameter.

Satu masalah dengan coutadalah opsi format. Memformat data (presisi, justifikasi, dll.) Lebih printfmudah.


1
ini bagus. Bagaimana saya tahu tidak ada orang yang memodifikasi cout global dengan cara ini di beberapa utas perpustakaan asing?
vp_arth

1
Anda dapat dengan mudah mengubah printfke file juga dengan menggantinya dengan fprintf...
CoffeeTableEspresso

5

Dua poin yang tidak disebutkan di sini yang saya temukan signifikan:

1) coutmembawa banyak bagasi jika Anda belum menggunakan STL. Itu menambahkan lebih dari dua kali lebih banyak kode ke file objek Anda printf. Ini juga berlaku untuk string, dan ini adalah alasan utama saya cenderung menggunakan perpustakaan string saya sendiri.

2) coutmenggunakan <<operator yang kelebihan beban , yang menurut saya sangat disayangkan. Ini dapat menambah kebingungan jika Anda juga menggunakan <<operator untuk tujuan yang dimaksud (bergeser ke kiri). Saya pribadi tidak suka membebani operator untuk keperluan yang tidak jelas dengan tujuan penggunaannya.

Intinya: Saya akan menggunakan cout(dan string) jika saya sudah menggunakan STL. Kalau tidak, saya cenderung menghindarinya.


4

Dengan primitif, mungkin tidak masalah sepenuhnya yang mana yang Anda gunakan. Saya katakan di mana manfaatnya adalah ketika Anda ingin menampilkan objek yang kompleks.

Misalnya, jika Anda memiliki kelas,

#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;
}

Sekarang hal di atas mungkin tidak terlalu bagus, tetapi anggap Anda harus menampilkan ini di beberapa tempat dalam kode Anda. Tidak hanya itu, katakanlah Anda menambahkan bidang "int d." Dengan cout, Anda hanya perlu mengubahnya di satu tempat. Namun, dengan printf, Anda harus mengubahnya di banyak tempat dan tidak hanya itu, Anda harus mengingatkan diri sendiri mana yang akan ditampilkan.

Dengan itu, dengan cout, Anda dapat mengurangi banyak waktu yang dihabiskan dengan pemeliharaan kode Anda dan tidak hanya itu jika Anda menggunakan kembali objek "Sesuatu" dalam aplikasi baru, Anda tidak benar-benar perlu khawatir tentang output.


Juga, untuk menambahkan tentang hal kinerja, saya akan mengatakan bahwa Anda tidak boleh mengeluarkan apa pun jika aplikasi Anda dibuat untuk kinerja. Segala jenis output ke std agak mahal dan lambat. Saya katakan Anda harus menghindarinya dan hanya menghasilkan ketika benar-benar diperlukan untuk melakukannya.
Daniel

Ingatlah bahwa kelas Anda mungkin memiliki anggota pribadi yang tidak dapat Anda akses dengan mudah dari luar. Dengan operator keluaran, Anda memiliki tepat satu lokasi yang harus menjadi teman kelas Anda, dan sekarang Anda dapat menampilkannya di mana saja, bahkan dalam kode yang tidak Anda ketahui.
Hochl

2

Tentu saja Anda dapat menulis "sesuatu" sedikit lebih baik untuk menjaga pemeliharaan:

#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;
}

Dan sedikit ujian cout vs printf, menambahkan tes 'ganda', jika ada yang ingin melakukan lebih banyak pengujian (Visual Studio 2008, rilis versi yang dapat dieksekusi):

#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);
    }
}

Hasilnya adalah:

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

Wow, mengapa endljauh lebih efisien daripada '\n'?
Nicholas Hamilton

1
Saya percaya itu karena endlflush buffer, dan \ntidak, meskipun saya tidak yakin ini alasan mengapa.
Caleb Xu

Ini bukan jawaban atas pertanyaan, ini lebih seperti jawaban untuk pertanyaan Daniel dan Thomas .
Fabio mengatakan Reinstate Monica

2

Saya ingin menunjukkan bahwa jika Anda ingin bermain dengan utas di C ++, jika Anda menggunakan coutAnda bisa mendapatkan beberapa hasil yang menarik.

Pertimbangkan kode ini:

#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

Sekarang, hasilnya datang semua dikocok. Itu dapat menghasilkan hasil yang berbeda juga, coba jalankan beberapa kali:

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

Anda dapat menggunakannya printfuntuk memperbaikinya, atau Anda dapat menggunakannya mutex.

#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB

Selamat bersenang-senang!


2
wtf threads tidak membuat output menjadi gila. Saya baru saja mereproduksi dan menemukan keduanya xyzdan ABCdalam output. Tidak ada mangling b / w ABCsebagai ABABAB.
Abhinav Gauniyal

1
Saya tidak tahu cara coutkerjanya dengan utas, tetapi saya tahu pasti bahwa kode yang Anda tampilkan bukan yang Anda gunakan untuk mendapatkan hasil tersebut. Kode Anda melewati string "ABC"untuk utas 1 dan "xyz"utas 2, tetapi output Anda menunjukkan AAAdan BBB. Tolong perbaiki, karena sekarang ini membingungkan.
Fabio mengatakan Reinstate Monica

1
cout<< "Hello";
printf("%s", "Hello"); 

Keduanya digunakan untuk mencetak nilai. Mereka memiliki sintaks yang sama sekali berbeda. C ++ memiliki keduanya, C hanya memiliki printf.


19
... apa? apakah Anda mencampuradukkan sesuatu?
xtofl

1
Memperbaiki masalah. -1 karena itu perlu diperbaiki dan jawabannya meninggalkan banyak yang diinginkan.
Yacoby

3
Nama fungsi telah dibalik: cout digunakan dengan sintaks printf, dan printf digunakan dengan sintaks cout. Seharusnya tidak diterima!
Mahmoud Al-Qudsi

2
dan kelemahan utama dari cout adalah menggunakan cout operator << yang verbose dan jelek dan bisa dibilang penyalahgunaan operator. :)
jalf

8
Meskipun ini pasti bukan jawaban terbaik, saya tidak mengerti bagaimana scatman dihukum karena jawabannya hanya karena dipilih sebagai jawaban terbaik. xbit memiliki jawaban IMO yang jauh lebih buruk tetapi memiliki -1 suara. Saya tidak mengatakan xbit seharusnya tidak lagi dipilih, tetapi saya tidak melihat adil untuk menurunkan pemilih sebagai kesalahan OP lagi daripada yang seharusnya ...
Jesse

1

Saya ingin mengatakan bahwa kurangnya ekstensibilitas printftidak sepenuhnya benar:
Dalam C, itu benar. Tetapi di C, tidak ada kelas nyata.
Di C ++, dimungkinkan untuk membebani operator transmisi, jadi, membebani char*operator dan menggunakan printfseperti ini:

Foo bar;
...;
printf("%s",bar);

bisa dimungkinkan, jika Foo membebani operator yang baik. Atau jika Anda membuat metode yang bagus. Singkatnya, printfsama mudahnya dengan coutsaya.

Argumen teknis yang dapat saya lihat untuk aliran C ++ (secara umum ... tidak hanya cout.) Adalah:

  • Keamanan jenis. (Dan, omong-omong, jika saya ingin mencetak satu '\n'saya gunakan putchar('\n')... Saya tidak akan menggunakan bom nuklir untuk membunuh serangga.).

  • Lebih mudah dipelajari. (tidak ada parameter "rumit" untuk dipelajari, hanya untuk digunakan <<dan >>operator)

  • Bekerja secara native dengan std::string(karena printfada std::string::c_str(), tetapi untuk scanf?)

Karena printfsaya melihat:

  • Lebih mudah, atau setidaknya lebih pendek (dalam hal karakter ditulis) pemformatan kompleks. Jauh lebih mudah dibaca, untuk saya (soal selera saya kira).

  • Kontrol yang lebih baik atas fungsi yang dibuat (Kembalikan jumlah karakter yang ditulis dan ada %nformatter: "Tidak ada yang dicetak. Argumen harus menjadi penunjuk ke int yang ditandatangani, di mana jumlah karakter yang ditulis sejauh ini disimpan." (Dari printf - Referensi C ++ )

  • Kemungkinan debug yang lebih baik. Untuk alasan yang sama dengan argumen terakhir.

Preferensi pribadi saya pergi ke printf(dan scanf) fungsi, terutama karena saya suka garis pendek, dan karena saya tidak berpikir masalah mengetik pada teks sangat sulit untuk dihindari. Satu-satunya hal yang saya sesalkan dengan fungsi C-style adalah yang std::stringtidak didukung. Kita harus melalui char*sebelum memberikannya kepada printf(dengan std::string::c_str()jika kita ingin membaca, tetapi bagaimana menulis?)


3
Kompiler tidak memiliki informasi tipe untuk fungsi varargs, sehingga tidak akan mengonversi parameter aktual (kecuali promosi argumen default , seperti promosi integral standar). Lihat 5.2.2p7. Konversi yang ditentukan pengguna untuk char*tidak akan digunakan.
Ben Voigt

Bahkan jika ini berhasil, itu tidak akan menjadi contoh dari sprintf extensibility, hanya hacking pintar untuk memberikan sprintf apa yang diharapkan, dan mengabaikan beberapa masalah serius seperti di mana char*kehidupan dan untuk berapa lama, dan bahaya yang ditentukan pengguna gips implisit.
Marcelo Cantos

1

Lebih banyak perbedaan: "printf" mengembalikan nilai integer (sama dengan jumlah karakter yang dicetak) dan "cout" tidak mengembalikan apa pun

Dan.

cout << "y = " << 7; bukan atom.

printf("%s = %d", "y", 7); adalah atom.

cout melakukan pemeriksaan ketik, printf tidak.

Tidak ada yang setara dengan iostream "% d"


3
couttidak mengembalikan apa-apa karena itu objek, bukan fungsi. operator<<mengembalikan sesuatu (biasanya operan kirinya, tetapi nilai palsu jika ada kesalahan). Dan dalam arti apakah printfpanggilan "atomik"?
Keith Thompson

9
Itu seperti bom atom. printf("%s\n",7);
kebisingan seni

@artlessnoise, tunggu mengapa kesalahan segmentasi? %sadalah ?
Abhinav Gauniyal

1
Itulah inti dari pernyataan 'bom atom'. Sebuah printf % s argumen harus memiliki pointer yang valid untuk null string diakhiri. Rentang memori '7' (sebuah pointer) biasanya tidak valid; kesalahan segmentasi bisa beruntung. Pada beberapa sistem, '7' mungkin mencetak banyak sampah ke konsol dan Anda harus melihatnya satu hari sebelum program berhenti. Dengan kata lain, ini adalah hal yang buruk printf. Alat analisis statis dapat menangkap banyak masalah ini.
kebisingan tanpa suara

Walaupun secara teknis printftidak melakukan pengetikan, saya tidak pernah menggunakan kompiler yang tidak memperingatkan saya tentang kesalahan ketik dengan printf...
CoffeeTableEspresso

1

TL; DR: Selalu lakukan riset Anda sendiri, berkenaan dengan ukuran kode mesin yang dihasilkan , kinerja , keterbacaan , dan waktu pengkodean sebelum mempercayai komentar acak daring, termasuk yang ini.

Saya bukan ahli. Saya kebetulan mendengar dua rekan kerja berbicara tentang bagaimana kita harus menghindari penggunaan C ++ di sistem embedded karena masalah kinerja. Yah, cukup menarik, saya melakukan benchmark berdasarkan tugas proyek nyata.

Dalam tugas tersebut, kami harus menulis beberapa konfigurasi ke RAM. Sesuatu seperti:

kopi =
gula panas = tidak ada
susu = payudara
mac = AA: BB: CC: DD: EE: FF

Inilah program benchmark saya (Ya, saya tahu OP bertanya tentang printf (), bukan fprintf (). Coba tangkap esensinya dan omong-omong, tautan OP menunjuk ke 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();

Saya melakukan yang terbaik untuk memoles mereka sebelum saya memutarkan keduanya 100.000 kali. Inilah hasilnya:

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

Ukuran file objek:

C   - 2,092 bytes
C++ - 3,272 bytes

Kesimpulan: Pada platform saya yang sangat spesifik , dengan prosesor yang sangat spesifik , menjalankan versi Linux kernel yang sangat spesifik , untuk menjalankan program yang dikompilasi dengan versi GCC yang sangat spesifik , untuk menyelesaikan tugas yang sangat spesifik , saya akan mengatakan pendekatan C ++ lebih cocok karena berjalan secara signifikan lebih cepat dan memberikan keterbacaan yang jauh lebih baik. Di sisi lain, C menawarkan jejak kecil, menurut pendapat saya, hampir tidak berarti karena ukuran program tidak menjadi perhatian kami.

Ingat, YMMV.


Saya tidak setuju bahwa C ++ lebih mudah dibaca dalam contoh ini, karena contoh Anda mengemas beberapa baris menjadi satu panggilan printf. Itu secara alami kurang dapat dibaca daripada cara Anda melakukan kode C ++, dan jarang dilakukan dalam C karena sulit dibaca dan sulit untuk dipelihara. Perbandingan yang adil akan menyebar C ke printfs terpisah, satu untuk garis jangkauan.
maharvey67

1
@ Maharvey67 Memang benar apa yang Anda katakan. Namun, contoh yang saya berikan di C adalah pertimbangan kinerja. Panggilan packed-in-one ke fprintf sudah dua detik lebih lambat dari pada kesetaraan C ++. Jika saya membuat kode C dapat dibaca maka mungkin lebih lambat. Penafian: Ini adalah satu tahun yang lalu dan saya ingat saya mencoba yang terbaik untuk memoles kode C dan C ++. Saya tidak punya bukti panggilan terpisah ke fprintf akan lebih cepat dari satu panggilan tunggal, tetapi alasan saya melakukannya dengan cara ini mungkin menunjukkan bahwa itu bukan.
Wesley

0

Saya bukan seorang programmer, tetapi saya telah menjadi seorang insinyur faktor manusia. Saya merasa bahasa pemrograman harus mudah dipelajari, dipahami, dan digunakan, dan ini menuntut bahasa pemrograman yang sederhana dan konsisten. Meskipun semua bahasa adalah simbolis dan karenanya, pada intinya, sewenang-wenang, ada konvensi dan mengikuti mereka membuat bahasa lebih mudah untuk dipelajari dan digunakan.

Ada sejumlah besar fungsi dalam C ++ dan bahasa lain yang ditulis sebagai fungsi (parameter), sebuah sintaks yang awalnya digunakan untuk hubungan fungsional dalam matematika di era pra-komputer. printf()mengikuti sintaks ini dan jika penulis C ++ ingin membuat metode yang berbeda secara logis untuk membaca dan menulis file mereka bisa saja membuat fungsi yang berbeda menggunakan sintaksis yang sama.

Dalam Python kita tentu saja dapat mencetak menggunakan object.methodsintaks yang juga cukup standar , yaitu variablename.print, karena variabel adalah objek, tetapi dalam C ++ mereka tidak.

Saya tidak menyukai sintaksis cout karena operator << tidak mengikuti aturan apa pun. Ini adalah metode atau fungsi, yaitu mengambil parameter dan melakukan sesuatu untuk itu. Namun itu ditulis seolah-olah itu adalah operator perbandingan matematis. Ini adalah pendekatan yang buruk dari sudut pandang faktor manusia.


-1

printfadalah fungsi sedangkan coutvariabel.


6
Saya melakukan roll-back karena, meskipun jawabannya sendiri mungkin salah, itu masih merupakan jawaban yang tulus. Jika Anda (benar) berpikir jawabannya salah, Anda memiliki dua opsi: 1) menambahkan komentar atau 2) menambahkan jawaban baru (atau melakukan keduanya). Jangan mengubah jawaban seseorang untuk mengatakan sesuatu yang sama sekali berbeda dari apa yang dimaksudkan oleh penulis.
Markus

1
printfadalah fungsi, tetapi printf()merupakan panggilan fungsi =)
vp_arth

Cout adalah objek, bukan variabel.
Lin
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.