C ++ 'da' printf 've' cout '


Yanıtlar:


333

Bu sorudaki herkesin , soru sadece farklar sorsa bile, std::coutbundan daha iyi olduğunu iddia ettiğine şaşırdım printf. Şimdi, bir fark var - std::coutC ++ ve printfC (ancak, C ++ 'da hemen hemen her şey gibi kullanabilirsiniz ). Şimdi, burada dürüst olacağım; ikisi de printfvestd::cout avantajları var.

Gerçek farklılıklar

uzayabilirlik

std::coutgenişletilebilir. İnsanların şunu söyleyeceğini biliyorumprintf genişletilebilir , ancak bu tür bir uzantı C standardında belirtilmedi (bu nedenle standart olmayan özellikler kullanmak zorunda kalacaksınız - ancak standart olmayan ortak özellik bile mevcut değil) ve bu uzantılar bir harftir (bu nedenle mevcut bir biçimle çakışmak kolaydır).

Bunun aksine printf, std::couttamamen operatörün aşırı yüklenmesine bağlıdır, bu nedenle özel formatlarla ilgili bir sorun yoktur - tek yapmanız gereken std::ostreamilk argüman olarak bir alt program ve ikinci olarak da türünüzü tanımlamaktır . Bu nedenle, ad alanı sorunları yoktur - bir sınıfınız olduğu sürece (bir karakterle sınırlı değildir), çalışabilirsiniz.std::ostream bunun için aşırı yükleme .

Ancak, birçok insanın uzatmak isteyeceğinden şüphe duyuyorum ostream(dürüst olmak gerekirse, bu uzantıları yapmak kolay olsa bile nadiren gördüm). Ancak, ihtiyacınız varsa burada.

Sözdizimi

Kolayca fark edileceği üzere; hem printfve std::coutfarklı bir sözdizimi kullanın. printfdesen dizesi ve değişken uzunluklu bağımsız değişken listelerini kullanarak standart işlev sözdizimini kullanır. Aslında, printfC'nin onlara sahip olmasının bir nedeni - printfformatlar onlar olmadan kullanılamayacak kadar karmaşık. Ancak, std::coutfarklı bir API kullanır -operator << kendisini döndüren API.

Genellikle bu, C versiyonunun daha kısa olacağı anlamına gelir, ancak çoğu durumda önemli olmayacaktır. Çok sayıda argüman yazdırdığınızda fark fark edilir. Error 2: File not found.Hata numarası varsayarak ve açıklamasının yer tutucu olduğunu varsayarak, böyle bir şey yazmanız gerekiyorsa , kod şöyle görünür. Her iki örnek de aynı şekilde çalışır ( std::endltamponu temizler).

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

Bu çok çılgın görünmese de (sadece iki kat daha uzun), argümanları gerçekten yazdırdığınızda, yalnızca yazdırmak yerine, daha da çılgınlaşıyor. Örneğin, böyle bir şeyin basılması 0x0424çılgınca. Bu, std::coutkarıştırma durumu ve gerçek değerlerden kaynaklanır. Hiç std::setfillbir tür bir tür (tabii C ++ dışında) olacağını bir dil görmedim . printfargümanları ve gerçek türü açıkça ayırır. Gerçekten (çok fazla gürültü içerdiği printfiçin) iostreamsürümüne kıyasla (şifreli görünüyor olsa bile) sürümünü korumayı tercih ederim .

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

Tercüme

İşte burada printfyalanların gerçek avantajı . printfBiçim dizesi ... iyi bir dizedir. Bu, operator <<istismarına kıyasla tercüme edilmeyi gerçekten kolaylaştırır iostream. gettext()İşlevin çevirdiğini ve göstermek istediğinizi varsayarsak Error 2: File not found., daha önce gösterilen biçim dizesinin çevirisini almak için kod şöyle görünür:

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

Şimdi, hata numarasının açıklamadan sonra olduğu Fictionish diline çevirdiğimizi varsayalım. Çevrilen dize şöyle görünür %2$s oru %1$d.\n. Şimdi, C ++ ile nasıl yapılır? Hiçbir fikrim yok. Çeviri amacıyla geçebileceğiniz iostreamyapıları ya da bir şeyi sahte printfyapabilirsiniz gettext. Tabii ki, $C standardı değil, ama bence kullanmak o kadar yaygın ki.

Belirli tamsayı tipi sözdizimini hatırlamak / aramak zorunda kalmamak

C'nin çok sayıda tamsayı türü vardır ve C ++ da öyle. std::coutsize kolları her türlü iken, printfbir tam sayı tipine bağlı olarak, belirli bir sözdizimi gerektirir (tamsayı olmayan türleri vardır, ancak, pratikte kullanımı yalnızca tamsayı olmayan türü printfolan const char *(Cı-dize, kullanılarak elde edilebilir to_cyöntemin std::string)). Örneğin, yazdırmak için size_tsize kullanımına ihtiyaç %zdiken, int64_tkullanarak gerektirecektir %"PRId64". Tablolara http://en.cppreference.com/w/cpp/io/c/fprintf ve http://en.cppreference.com/w/cpp/types/integer adresinden ulaşılabilir .

NUL baytını yazdıramazsınız, \0

Çünkü printfC ++ dizeleri aksine kullanımları C dizeleri, belirli hileler olmadan boş karakter baskı yapamaz. Bazı durumlarda kullanmak mümkündür %cile'\0' açıkça bir hack olsa, bir argüman olarak.

Kimsenin umurunda olmadığı farklılıklar

Verim

Güncelleme: O iostreamkadar yavaş çıkıyor ki , genellikle sabit sürücünüzden daha yavaştır (programınızı dosyaya yönlendirirseniz). stdioÇok sayıda veri çıkarmanız gerekirse ile senkronizasyonu devre dışı bırakmak yardımcı olabilir. Performans gerçek bir endişe ise (STDOUT'a birkaç satır yazmak yerine),printf .

Herkes performansı önemsediğini düşünüyor, ancak kimse bunu ölçmek için rahatsız etmiyor. Cevabım, G / Ç'nin her ne şekilde olursa olsun darboğaz olduğu printfya da iostream. Bu derleme hızlı bir bakış ( derleyici seçeneğini kullanarak clang ile derlenmiş) daha hızlı printf olabileceğini düşünüyorum -O3. Hata örneğimi varsayarsak, örnek, printförnekten daha az çağrı yapar cout. Bu int mainşununla 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:

İki dizenin ve 2(sayı) printfargüman olarak aktarıldığını kolayca fark edebilirsiniz . Bu kadar; başka bir şey yok. Karşılaştırma için, bu iostreammontaj için derlenmiştir. Hayır, satır içi yoktur; her operator <<çağrı, başka bir argüman kümesiyle başka bir çağrı anlamına gelir.

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:

Ancak, dürüst olmak gerekirse, G / Ç zaten darboğaz olduğu için bu hiçbir şey ifade etmiyor. Daha iostreamhızlı olmadığını göstermek istedim çünkü “güvenli”. Çoğu C uygulaması printf, hesaplanmış goto kullanarak formatlar uygular , bu nedenle printfderleyicinin farkında olmasa bile olabildiğince hızlıdır printf(bazı olmadıklarından değil - bazı derleyiciler printfbelirli durumlarda optimize edebilir - sabit dize bitişi \ngenellikleputs ) .

miras

Neden miras almak istediğini bilmiyorum ostream, ama umrumda değil. Bu da mümkün FILE.

class MyFile : public FILE {}

Tip güvenliği

Doğru, değişken uzunluklu bağımsız değişken listelerinin güvenliği yoktur, ancak bu önemli değildir, çünkü popüler C derleyicileri printfuyarıları etkinleştirirseniz biçim dizesiyle ilgili sorunları algılayabilir . Aslında, Clang bunu uyarıları etkinleştirmeden yapabilir.

$ 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
G / Ç'nin zaten darboğaz olduğunu söylüyorsunuz. Açıkçası bu varsayımı hiç test etmediniz. Kendime alıntı yapıyorum: "Öte yandan, iostreams sürümü, 75.3 MB / s'de, bir sabit diske ayak uyduracak kadar hızlı bir şekilde arabelleğe alınamıyor. Bu kötü ve henüz gerçek bir iş bile yapmıyor. G / Ç kütüphanemin disk denetleyicimi doyurabilmesi gerektiğini söylediğimde çok yüksek beklentilerimin olduğunu düşünmüyorum. "
Ben Voigt

4
@BenVoigt: Kabul ediyorum, mümkünse C ++ 'dan kaçınmaya çalışıyorum. Çok kullanmayı denedim, ancak kullandığım diğer programlama dillerinden daha can sıkıcı ve daha az bakım gerektiriyordu. Bu, C ++ 'dan kaçınmamın bir başka nedeni - bu bile hızlı değil (hatta iostream değil - tüm C ++ kütüphanesi, belki de istisna olmak üzere çoğu uygulamada yavaştır std::sort, bu da qsort(2 kez) ile karşılaştırıldığında bir şekilde şaşırtıcı derecede hızlıdır . yürütülebilir boyutun maliyeti).
Konrad Borowski

3
Burada kimse cout kullanırken paralel ortamdaki sorunlardan bahsetmedi.
Nicholas Hamilton

9
Performans argümanınız hiçbir anlam ifade etmiyor. Eğer çünkü Daha montaj program anlamına gelmez bu program yavaş olacaktır değil kodun bir sürü printf fonksiyonunu yapar tüm kodu için muhasebe. Bence, << operatör ile cout'u printf'den çok daha iyi optimize etmek mümkündür, çünkü derleyici değişkenleri ve biçimlendirmeyi daha iyi anlayabilir.
Ignas2526

18
Bu cevapla ilgili pek çok şeyi seviyorum, ama belki de en sevdiğim kısım "Herkes performansı önemsediğini düşünüyor, ancak kimse bunu ölçmek için rahatsız etmiyor."
Kyle Strand

203

Gönderen C ++ SSS :

[15.1] <iostream> Geleneksel yerine neden kullanmalıyım <cstdio>?

Tip güvenliğini artırın, hataları azaltın, genişletilebilirliğe izin verin ve kalıtım sağlayın.

printf()tartışmasız bir şekilde kırılmaz ve scanf()hataya eğilimli olmasına rağmen yaşanabilir, ancak her ikisi de C ++ I / O'nun yapabilecekleri ile sınırlıdır. C ++ I / O ( <<ve kullanarak >>), C'ye ( printf()ve kullanarak scanf()) göre:

  • Daha güvenli tip: <iostream>I / O'd olan nesnenin türü derleyici tarafından statik olarak bilinir. Buna karşılık, <cstdio>türleri dinamik olarak anlamak için "%" alanlarını kullanır.
  • Daha az hata eğilimi: İle <iostream> Gerçek nesnelerin I / O'd ile tutarlı olması gereken gereksiz "%" belirteçleri yoktur. Fazlalık kaldırıldığında bir sınıf hata kaldırılır.
  • Genişletilebilir: C ++ <iostream>mekanizması, yeni kullanıcı tanımlı türlerin mevcut kodu bozmadan I / O'd olmasını sağlar. Herkesin aynı anda yeni uyumsuz "%" alanlarını ekleyerek olsaydı kaos düşünün printf()ve scanf()?!
  • Devralınabilen: C ++ <iostream>mekanizması std::ostreamve gibi gerçek sınıflardan oluşturulmuştur std::istream. Aksine <cstdio>s' FILE*, bu gerçek sınıfları ve dolayısıyla devredilebilir. Bu, akış gibi görünen ve hareket eden, ancak istediğiniz tuhaf ve harika şeyleri yapan, kullanıcı tanımlı başka şeylere sahip olabileceğiniz anlamına gelir. Otomatik olarak tanımadığınız kullanıcılar tarafından yazılmış G / Ç kodu satırlarını kullanırsınız ve "genişletilmiş akış" sınıfınızı bilmeleri gerekmez.

Öte yandan, printftercih kullanmadan haklı olabilir, önemli ölçüde daha hızlı olduğu coutiçinde çok özel ve sınırlı durumlarda. Daima önce profil yapın. (Örneğin, bkz. Http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)


2
Öte yandan, aynı anda tip güvenliği, ifade ve performans sunan FastFormat kütüphanesi ( fastformat.org ) var. (Henüz denediğimden değil ...)
xtofl

3
@Marcelo muhtemelen çünkü her şey iyi bir özet. Biçimlendirme ... evet, bu oldukça kötü. Bunu kendim düzeltmeliydim, ama görünüşe göre başkalarının (kendiniz de dahil) ona baktığı anlaşılıyor, ki bu tabii ki sızlanmaktan daha yapıcı.
Mikeage

2
Geç itibariyle printf() de genişletilebilir olması gerekiyor. Kısmındaki "printf kancaları" Bkz udrepper.livejournal.com/20948.html
Maxim Egorushkin

4
@MaximYegorushkin: Standart printf böyle bir yeteneği yok. Taşınabilir olmayan kütüphane mekanizmaları, iostreams'in tamamen standartlaştırılmış genişletilebilirliğiyle neredeyse aynı seviyede değildir.
Ben Voigt

4
"Öte yandan, printf önemli ölçüde daha hızlıdır" printf de daha temiz ve kullanımı daha kolaydır, bu yüzden mümkün olduğunda cout'u önlerim.
FloresanYeşil5

43

İnsanlar genellikle bunun printfçok daha hızlı olduğunu iddia eder . Bu büyük ölçüde bir efsanedir. Aşağıdaki sonuçlarla test ettim:

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

Sonuç: yalnızca yeni satırlar istiyorsanız printf; aksi takdirde coutneredeyse hızlı veya daha hızlıdır. Daha fazla detay bulunabilir bloguma .

Açıkça söylemek gerekirse, iostreams'nin her zamankinden daha iyi olduğunu söylemeye çalışmıyorum printf; Sadece bazı yaygın, yanıltıcı varsayımlara dayanan vahşi bir tahmin değil, gerçek verilere dayalı bilinçli bir karar vermeniz gerektiğini söylemeye çalışıyorum.

Güncelleme: İşte test için kullandığım kodun tamamı. g++Herhangi bir ek seçenek olmadan derlenmiştir ( -lrtzamanlama hariç ).

#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
Sizin puanlarınızda printf kolayca cout atmaktadır (çoğunluk vakaları). Merak ettiğimde neden cout kullanılmasını öneriyorsun acaba? Gerçi perf gerçekçi durumlarda çok farklı değil kabul ediyorum ..
mishal153

3
@ mishal153: Ben sadece performansın çok farklı olmadığını söylemeye çalışıyorum, bu yüzden "asla cout kullanmadığı için cout'u asla kullanma" nın yaygın olarak duyulan tavsiyesi basit aptalca. Cout'un belirgin bir tür güvenlik ve genellikle okunabilirlik avantajına sahip olduğunu unutmayın. (İostreams ile kayan nokta biçimlendirme korkunç ...)
Thomas

35
Arasındaki önemli fark printf() ve std::ostreamolduğu eski çıkış tek bir çağrı tüm bağımsız değişkenler ise std::ostreamdoğurur her biri için ayrı bir arama <<. Test sadece bir argüman ve yeni bir satır çıkarır, bu yüzden farkı göremezsiniz.
Maxim Egorushkin

12
Derleyici bu çağrıları satır içine alabilmelidir. Ayrıca,printf çeşitli biçimlendirme belirteçleri için yardımcı işlevlere kapak altında çok fazla çağrı yapabilir ... ya da bu korkunç bir monolitik işlevdir. Ve yine, inlining nedeniyle, hızda hiç bir fark yaratmamalıdır.
Thomas

4
Terminalinizi zamanladınız. Kullanın sprintfveya fprintfve stringstreamyafstream .
Ben Voigt

41

Ve alıntı yapıyorum :

Yüksek düzeyde terimlerle, ana farklar tip güvenliği (cstdio'da yoktur), performans (çoğu iostreams uygulaması cstdio'dan daha yavaştır) ve genişletilebilirliktir (iostreams, özel çıktı hedeflerine ve kullanıcı tanımlı türlerin kesintisiz çıktısına izin verir).


Özellikle POSIX ile, typedef'lerden birinin gerçekte ne büyüklükte olduğunu asla bilemeyeceğiniz, bu nedenle çok fazla oyuncuya ihtiyacınız var veya programların% 99'u olarak% d ile riske atıyorsunuz. % Z C99 ile gelmeden çok uzun zaman aldı. Ancak time_t / off_t için doğru format talimatı arayışı devam eder.
Lothar

30

Bunlardan biri stdout'a baskı yapan bir fonksiyondur. Diğeri, çeşitli üye işlevlerini ve operator<<bu baskının stdout'a aşırı yüklenmesini sağlayan bir nesnedir . Numaralandırabileceğim çok daha fazla fark var, ama neyin peşinde olduğundan emin değilim.


12

Benim için, 'printf' yerine 'cout' yapmamı sağlayacak gerçek farklılıklar:

1) << operatörü sınıflarım için aşırı yüklenebilir.

2) Cout için çıkış akışı kolayca bir dosyaya değiştirilebilir: (: kopyala yapıştır :)

#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) Özellikle birçok parametremiz olduğunda cout'u daha okunabilir buluyorum.

Bir sorun ile coutbiçimlendirme seçenekleri olduğunu. Verileri (hassasiyet, gerekçe vb.) Biçimlendirmek printfdaha kolaydır.


1
bu iyi. Kimse bazı yabancı kütüphane iş parçacığında küresel cout bu şekilde değiştiremezsiniz nasıl?
vp_arth

1
Kolayca değiştirebilir printfile değiştirerek de bir dosyaya fprintf...
CoffeeTableEspresso

5

Burada aksi belirtilmedikçe önemli bulduğum iki nokta:

1) coutSTL'yi kullanmıyorsanız çok fazla bagaj taşır. Nesne dosyanıza iki kat daha fazla kod ekler printf. Bu da geçerlidir stringve kendi string kütüphanemi kullanma eğilimimin ana sebebi budur.

2) talihsiz bulduğum coutaşırı yüklenmiş <<operatörleri kullanıyor . Ayrıca,<<Operatörün amacına uygun olarak sola kayma olabilir (sola kaydırma). Şahsen, amaçlanan kullanımlarına teğet amaçlarla operatörleri aşırı yüklemeyi sevmiyorum.

Alt satır: Eğer zaten STL kullanıyorsanız cout(ve string) kullanacağım . Aksi takdirde bundan kaçınma eğilimindeyim.


4

İlkellerde muhtemelen hangisini kullandığınız önemli değildir. Yararlı hale geldiğini söylüyorum, karmaşık nesneler çıkarmak istediğinizde.

Örneğin, bir sınıfınız varsa,

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

Şimdi yukarıdakiler o kadar da harika görünmeyebilir, ancak diyelim ki bunu kodunuzda birden fazla yerde çıkarmanız gerekiyor. Sadece bu değil, diyelim ki bir alan eklediniz "int d". Cout ile, sadece bir yerde değiştirmeniz gerekir. Bununla birlikte, printf ile, muhtemelen birçok yerde değiştirmek zorunda kalacaksınız ve sadece bunu değil, kendinize hangilerinin çıktı vereceğini hatırlatmalısınız.

Bununla birlikte, cout ile, kodunuzun bakımı ile harcanan birçok zamanı azaltabilirsiniz ve sadece "Bir şey" nesnesini yeni bir uygulamada yeniden kullanırsanız, çıktı için gerçekten endişelenmenize gerek yoktur.


Ayrıca, performans şeyini eklemek için, uygulamanız performans için yapılmışsa hiçbir şey çıkarmamanız gerektiğini söyleyebilirim. STD'ye her türlü çıktı oldukça pahalı ve yavaştır. Bundan kaçınmanız ve sadece mutlaka yapılması gerektiğinde çıktı almanız gerektiğini söylüyorum.
Daniel

sınıfınızın dışarıdan bu kadar kolay erişemeyeceğiniz özel üyeler olabileceğini unutmayın. Çıktı operatörü ile, sınıfınızla arkadaş olmanız gereken tam bir konumunuz var ve şimdi bilmediğiniz kodlarda bile her yerden çıktı alabilirsiniz.
hochl

2

Tabii ki bakım tutmak için biraz daha iyi bir şey yazabilirsiniz:

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

Ve biraz daha fazla cout ve printf testi, daha fazla test yapmak istiyorsa (Visual Studio 2008, yürütülebilir sürüm): 'double' testi ekledi:

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

Sonuç:

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

Vay, neden endldaha az verimli '\n'?
Nicholas Hamilton

1
Ben bunun endltamponu temizler çünkü inanıyorum , ve \ndeğil, her ne kadar bunun kesin nedeni olduğundan emin değilim.
Caleb Xu

Bu soruya bir cevap değil, daha çok Daniel'in ve Thomas'ın cevabına benziyor .
Fabio, Reinstate Monica'nın

2

C ++ 'da konu ile oynamak istiyorsanız, kullanırsanız coutbazı ilginç sonuçlar elde edebileceğinizi belirtmek isterim .

Bu kodu düşünün:

#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

Şimdi, çıktı hepsi karışık. Farklı sonuçlar da verebilir, birkaç kez çalıştırmayı deneyin:

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

Doğru yapmak için kullanabilirsiniz printfveya kullanabilirsiniz mutex.

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

İyi eğlenceler!


2
wtf threads çıkış fındık gitmek yapmayın. Ben sadece çoğaltılabilir ve her ikisi de bulundu xyzve ABCçıktıda. S / b ABCgibi mangling yoktu ABABAB.
Abhinav Gauniyal

1
coutKonuları ile nasıl çalıştığını bilmiyorum , ama gösterdiğiniz kodun bu çıktıları almak için kullandığınız biri olmadığından eminim. Kodunuz "ABC", iş parçacığı 1 ve "xyz"iş parçacığı 2 için dizeyi geçirir , ancak çıktınız AAAve öğelerini gösterir BBB. Lütfen düzeltin, çünkü şu anda kafa karıştırıcı.
Fabio, Reinstate Monica'nın

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

Her ikisi de değerleri yazdırmak için kullanılır. Tamamen farklı bir sözdizimine sahipler. C ++ her ikisine de sahiptir, C yalnızca printf'ye sahiptir.


19
... ne? Bir şey karıştırdın mı?
xtofl

1
Sorun düzeltildi. -1 çünkü düzeltme gerekiyordu ve cevap arzulanan bir çok şey bırakıyor.
Yacoby

3
İşlev adları ters çevrilmişti: printf sözdiziminde cout, cout sözdiziminde printf kullanılmıştır. Kabul edilmemeliydi bile!
Mahmoud Al-Qudsi

2
ve cout'un ana dezavantajı, ayrıntılı ve çirkin ve tartışmasız operatör kötüye kullanımı olan << operatörünü kullanmasıdır. :)
jalf

8
Bu kesinlikle en iyi cevap olmasa da, scatman'ın cevabı için nasıl cezalandırıldığını anlamıyorum, çünkü en iyi cevap olarak seçildi. xbit'in IMO'ya karşı daha kötü bir yanıtı var ama -1 oyu var. Artık xbit'in aşağı oyu olması gerektiğini söylemiyorum, ama artık OP'nin hatası için scatman'ı oy etmekten daha adil olmayacağını görmüyorum ...
Jesse

1

Genişletilebilirlik eksikliğinin printftamamen doğru olmadığını söylemek istiyorum :
C'de bu doğrudur. Ancak C'de gerçek sınıflar yoktur.
C ++ 'da, döküm operatörünü aşırı yüklemek mümkündür, bu nedenle bir char*operatörü aşırı yüklemek ve şöyle kullanmak printf:

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

Foo iyi operatöre aşırı yüklenirse mümkün olabilir. Veya iyi bir yöntem yaptıysanız. Kısacası, printfbenim kadar genişletilebilir cout.

C ++ akışları için görebildiğim teknik argüman (genel olarak ... sadece cout değil):

  • Typesafety. (Ve bu arada, '\n'kullandığım tek bir baskı yapmak istersem putchar('\n')... Bir böceği öldürmek için nükleer bomba kullanmayacağım.).

  • Öğrenmesi daha basit. (öğrenmek için "karmaşık" parametreler yok, sadece kullanımı <<ve >>operatörler)

  • Doğal Work std::string(için printforada std::string::c_str(), ama için scanf?)

For printfI bkz:

  • Daha kolay veya en azından daha kısa (yazılı karakterler açısından) karmaşık biçimlendirme. Benim için çok daha okunabilir (sanırım lezzet meselesi).

  • İşlevin ne yaptığının daha iyi kontrolü (Yazılan %nkarakter sayısını ve biçimlendiriciyi döndürün: "Hiçbir şey yazdırılmadı. Bağımsız değişken, şimdiye kadar yazılan karakter sayısının depolandığı imzalı bir int'e bir işaretçi olmalıdır." ( Printf'den - C ++ Referansı )

  • Daha iyi hata ayıklama olanakları. Son argümanla aynı nedenle.

Kişisel tercihlerim printf(ve scanf) işlevlerine gider , çünkü kısa çizgileri severim ve metin yazdırmayla ilgili yazım sorunlarının önlenmesi gerçekten zor değildir. C stili işlevlerle çektiğim tek şey std::stringdesteklenmiyor. Biz geçmek zorunda char*vermeden önce printf(ile std::string::c_str()biz okumak istiyorsanız ama nasıl yazılır?)


3
Derleyicinin varargs işlevleri için tür bilgisi yoktur, bu nedenle gerçek parametreyi dönüştürmez ( standart integral tanıtımları gibi varsayılan bağımsız değişken tanıtımları hariç ). Bkz. 5.2.2p7. Kullanılacak kullanıcı tanımlı bir dönüşüm char*kullanılmayacak.
Ben Voigt

Bu işe yarasa bile, sprintf genişletilebilirliğine bir örnek olmaz, sadece sprintf'e beklediğini vermek için akıllıca bir hack olur ve char*yaşamların nerede ve ne kadar süreyle ve kullanıcı tanımlı tehlikeleri gibi bazı ciddi sorunları göz ardı eder. zımni dökümler.
Marcelo Cantos

1

Diğer farklar: "printf" bir tamsayı değeri (yazdırılan karakter sayısına eşit) döndürür ve "cout" hiçbir şey döndürmez

Ve.

cout << "y = " << 7; atomik değildir.

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

cout yazım denetimi yapar, printf yapmaz.

Hiçbir iostream eşdeğeri yok "% d"


3
couthiçbir şey döndürmez, çünkü bu bir işlev değil, bir nesnedir. operator<<bir şey döndürür (normalde sol işlenen, ancak bir hata varsa yanlış bir değer). Ve hangi anlamda printf"atom" denir?
Keith Thompson

9
Atom bombası gibi. printf("%s\n",7);
artless gürültü 14:13

@artlessnoise bekleyin neden segmentasyon hatası? %sdır-dir ?
Abhinav Gauniyal

1
'Atom bombası' ifadesinin anlamı budur. Bir printf % s argümanı boş sonlandırılmış dize geçerli bir işaretçi olması gerekir. '7' bellek aralığı (işaretçi) genellikle geçerli değildir; bir segmentasyon hatası şanslı olabilir. Bazı sistemlerde, '7' bir konsola çok fazla çöp yazdırabilir ve program durmadan önce bir gün boyunca ona bakmanız gerekir. Başka bir deyişle, bu kötü bir şey printf. Statik analiz araçları bu sorunların çoğunu yakalayabilir.
sanatsız gürültü

Teknik olarak printftypechecking yapmazken, ben asla tür hataları hakkında beni uyarmayan bir derleyici kullanmadım printf...
CoffeeTableEspresso

1

TL; DR: Bu dahil olmak üzere çevrimiçi rastgele yorumlara güvenmeden önce oluşturulan makine kodu boyutu , performans , okunabilirlik ve kodlama süresi ile ilgili olarak her zaman kendi araştırmanızı yapın .

Ben uzman değilim. Performans sorunları nedeniyle gömülü sistemlerde C ++ kullanmaktan nasıl kaçınmamız gerektiğinden bahseden iki iş arkadaşına kulak misafiri oldum. Yeterince ilginç, gerçek bir proje görevine dayanan bir kıyaslama yaptım.

Bahsedilen görevde, RAM'e bir yapılandırma yazmak zorunda kaldık. Gibi bir şey:

kahve = sıcak
şeker =
süt yok = meme
mac = AA: BB: CC: DD: EE: FF

İşte kıyaslama programlarım (Evet, OP'nin fprintf () değil, printf () hakkında sorduğunu biliyorum. Özü yakalamaya çalışın ve bu arada OP'nin fprintf () bağlantısını yine de gösteriyor.)

C programı:

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

C ++ programı:

//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();

Her ikisini de 100.000 kez döngüye sokmadan önce cilalamak için elimden geleni yaptım. Sonuçlar burada:

C programı:

real    0m 8.01s
user    0m 2.37s
sys     0m 5.58s

C ++ programı:

real    0m 6.07s
user    0m 3.18s
sys     0m 2.84s

Nesne dosya boyutu:

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

Sonuç: Benim çok özel olarak platformun çok özel olan, işlemci çok spesifik bir sürümünü çalıştıran, Linux çekirdeğinin bir çok özel sürümüyle derlenmiş bir program çalıştırmak için, GCC çok özel başarmak amacıyla, görevi diyebilirim C ++ yaklaşımı daha uygundur çünkü çok daha hızlı çalışır ve daha iyi okunabilirlik sağlar. Öte yandan, C küçük bir ayak izi sunuyor, bence, program boyutu endişe duymadığımız için neredeyse hiçbir şey ifade etmiyor.

Hatırlayın, YMMV.


Örneğiniz birden çok satırı tek bir printf çağrısında paketlediğinden, bu örnekte C ++ 'ın daha okunabilir olduğunu kabul etmiyorum. Bu, doğal olarak C ++ kodunu yaptığınızdan daha az okunabilir ve C'de nadiren yapılır, çünkü okunması zor ve bakımı zordur. Adil bir karşılaştırma, C'yi erişim hattı için olmak üzere ayrı printfs'ye yayar.
maharvey67

1
@ maharvey67 Söylediklerin doğru. Ancak, C'de verdiğim örnek performans göz önünde bulunduruldu. Fprintf'e paketlenmiş bir çağrı zaten C ++ eşdeğerinden iki saniye daha yavaştı. C kodunu okunabilir yapacak olsaydım, daha da yavaş olabilirdi. Feragatname: Bu bir yıl önceydi ve hem C hem de C ++ kodunu parlatmak için elimden geleni yaptığımı hatırlıyorum. Fprintf'e yapılan ayrı çağrıların tek bir çağrıdan daha hızlı olacağına dair bir kanıtım yoktu, ancak bu şekilde yapmamın nedeni muhtemelen olmadığını gösteriyor.
Wesley

0

Ben programcı değilim ama insan faktörü mühendisi oldum. Bir programlama dilinin öğrenmesi, anlaması ve kullanması kolay olduğunu düşünüyorum, bu da basit ve tutarlı bir dil yapısına sahip olmasını gerektiriyor. Tüm diller sembolik olmasına rağmen, özünde, keyfi olarak, sözleşmeler vardır ve bunları takip etmek dili öğrenmeyi ve kullanmayı kolaylaştırır.

C ++ 'da ve fonksiyon (parametre) olarak yazılan diğer dillerde çok sayıda fonksiyon vardır, başlangıçta bilgisayar öncesi dönemde matematikte fonksiyonel ilişkiler için kullanılan bir sözdizimi. printf()bu sözdizimini izler ve C ++ yazarları dosyaları okumak ve yazmak için mantıksal olarak farklı bir yöntem oluşturmak isteselerdi, benzer bir sözdizimi kullanarak farklı bir işlev oluşturabilirlerdi.

Python'da elbette oldukça standart object.methodsözdizimini, yani variablename.print'i kullanarak yazdırabiliriz , çünkü değişkenler nesnedir, ancak C ++ 'da değildir.

Ben cout sözdizimine düşkün değilim çünkü << operatörü herhangi bir kurala uymuyor. Bir yöntem veya işlevdir, yani bir parametre alır ve ona bir şey yapar. Ancak, matematiksel bir karşılaştırma operatörü gibi yazılmıştır. Bu, insan faktörleri açısından zayıf bir yaklaşımdır.


-1

printfbir işlevken coutbir değişkendir.


6
Geri alma yaptım çünkü cevabın kendisi yanlış olsa da, yine de gerçek bir cevap. Cevabın yanlış olduğunu (doğru) düşünüyorsanız, iki seçeneğiniz vardır: 1) bir yorum ekleyin veya 2) yeni bir cevap ekleyin (veya her ikisini de yapın). Birinin cevabını, yazarın amaçladığından tamamen farklı bir şey söyleyecek şekilde değiştirmeyin.
Mark

1
printfbir işlevdir, ancak printf()bir işlev çağrısıdır =)
vp_arth

cout bir nesnedir, bir değişken değildir.
Lin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.