Yanıtlar:
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.
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.
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;
İş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.
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 .
\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.
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 ) .
Neden miras almak istediğini bilmiyorum ostream, ama umrumda değil. Bu da mümkün FILE.
class MyFile : public FILE {}
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);
^
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).
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 vescanf()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 kullanarakscanf()) 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ünprintf()vescanf()?!- Devralınabilen: C ++
<iostream>mekanizmasıstd::ostreamve gibi gerçek sınıflardan oluşturulmuşturstd::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 /)
printf() de genişletilebilir olması gerekiyor. Kısmındaki "printf kancaları" Bkz udrepper.livejournal.com/20948.html
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.
İ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);
}
}
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.
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.
sprintfveya fprintfve stringstreamyafstream .
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).
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.
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.
printfile değiştirerek de bir dosyaya fprintf...
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.
İ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.
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
endldaha az verimli '\n'?
endltamponu temizler çünkü inanıyorum , ve \ndeğil, her ne kadar bunun kesin nedeni olduğundan emin değilim.
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!
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.
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ı.
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.
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?)
char*kullanılmayacak.
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.
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"
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?
printf("%s\n",7);
%sdır-dir ?
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.
printftypechecking yapmazken, ben asla tür hataları hakkında beni uyarmayan bir derleyici kullanmadım printf...
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.
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.
printfbir işlevken coutbir değişkendir.
printfbir işlevdir, ancak printf()bir işlev çağrısıdır =)