Відповіді:
Я здивований, що всі в цьому питанні стверджують, що std::cout
це набагато краще printf
, навіть якщо питання просто задало відмінності. Тепер є різниця - std::cout
це C ++ і printf
є C (однак, ви можете використовувати її в C ++, як і майже будь-що інше з C). Тепер я буду чесним тут; і printf
іstd::cout
мають свої переваги.
std::cout
є розширюваним. Я знаю, що люди це скажутьprintf
це також розширюється, але таке розширення не згадується в стандарті C (тому вам доведеться використовувати нестандартні функції - але навіть не існує звичайної нестандартної функції), і такі розширення є однією буквою (тому легко конфліктувати з уже існуючим форматом).
На відміну від цього printf
, std::cout
повністю залежить від перевантаження оператора, тому немає проблем із спеціальними форматами - все, що вам потрібно зробити, це визначити підпрограму, яка бере std::ostream
перший аргумент, а тип - другий. Таким чином, проблем із простором імен немає - якщо у вас є клас (який не обмежується одним символом), ви можете мати std::ostream
перевантаження для нього.
Однак я сумніваюся, що багато людей хотіли б продовжити ostream
(якщо чесно, я рідко бачив такі розширення, навіть якщо їх легко зробити). Однак, це тут, якщо вам це потрібно.
Як це було легко помітити, обидва printf
і std::cout
використовують різний синтаксис. printf
використовує стандартний синтаксис функції, використовуючи рядки шаблону та списки аргументів змінної довжини. Насправді, printf
це є причиною, чому C має їх - printf
формати занадто складні, щоб їх можна було використовувати. Однак std::cout
використовується інший API - theoperator <<
API, який повертається сам.
Як правило, це означає, що версія C буде коротшою, але в більшості випадків це не має значення. Різниця помітна, коли ви друкуєте багато аргументів. Якщо вам доведеться написати щось на зразок Error 2: File not found.
, якщо припустити номер помилки, а його опис - заповнювач, код буде виглядати приблизно так . Обидва приклади працюють однаково (ну, начебто, std::endl
насправді промиває буфер).
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
Хоча це не здається занадто божевільним (це просто в два рази довше), речі стають більш шаленими, коли ви фактично форматуєте аргументи, а не просто друкуєте їх. Наприклад, надрукувати щось подібне 0x0424
просто шалено. Це викликано std::cout
змішуванням стану та фактичних значень. Я ніколи не бачив мови, де щось подібне std::setfill
було б типу (крім C ++, звичайно). printf
чітко розділяє аргументи та фактичний тип. Я дійсно вважав за краще підтримувати його printf
версію (навіть якщо вона виглядає виразною) порівняно з її iostream
версією (оскільки вона містить занадто багато шуму).
printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;
Ось тут справжня перевага printf
брехні. Рядок printf
формату добре ... рядок. Це робить його дуже легким для перекладу, порівняно зі operator <<
зловживанням iostream
. Якщо припустити, що gettext()
функція перекладає, і ви хочете показати Error 2: File not found.
, код для отримання перекладу раніше показаного рядка формату виглядатиме так:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Тепер припустимо, що ми перекладаємо на художню літературу, де номер опису після опису. Перекладений рядок виглядав би так %2$s oru %1$d.\n
. Тепер, як це зробити в C ++? Ну, я поняття не маю. Я думаю, ви можете зробити підроблені, iostream
які конструкції, printf
які ви можете передати gettext
, або щось для цілей перекладу. Звичайно, $
це не стандарт С, але він настільки поширений, що на мою думку це безпечно використовувати.
C має безліч цілих типів, а також C ++. std::cout
обробляє всі типи, тоді як printf
потрібен певний синтаксис залежно від цілого типу (є не цілі типи, але єдиний не цілий тип, який ви будете використовувати на практиці, printf
це const char *
(рядок C, можна отримати за допомогою to_c
методу std::string
)). Наприклад, для друку size_t
вам потрібно використовувати %zd
, в той час як int64_t
потрібно буде використовувати %"PRId64"
. Таблиці доступні на веб-сайті http://en.cppreference.com/w/cpp/io/c/fprintf та http://en.cppreference.com/w/cpp/types/integer .
\0
Оскільки printf
використовує рядки C на відміну від рядків C ++, він не може друкувати байт NUL без конкретних хитрощів. У певних випадках це можливо використовувати %c
з'\0'
якості аргументу, хоча це явно хак.
Оновлення: виявляється, iostream
це настільки повільно, що зазвичай повільніше, ніж ваш жорсткий диск (якщо ви перенаправляєте програму на файл). Відключення синхронізації з stdio
може допомогти, якщо вам потрібно вивести багато даних. Якщо виконання викликає серйозне занепокоєння (на відміну від написання декількох рядків до STDOUT), просто використовуйтеprintf
.
Всі думають, що вони дбають про продуктивність, але ніхто не заважає її вимірювати. Моя відповідь полягає в тому, що I / O - це вузьке місце, незалежно від того, використовуєте ви printf
чи iostream
. Я думаю, що це printf
може бути швидшим від швидкого перегляду в збірку (складеного з clang за допомогою параметра -O3
компілятора). Якщо припустити мій приклад помилок, printf
приклад робить набагато менше викликів, ніж cout
приклад. Це int main
з 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:
Ви можете легко помітити, що два рядки та 2
(число) висуваються як printf
аргументи. Ось про це; більше нічого немає. Для порівняння, це iostream
складається для складання. Ні, немає вкладки; кожен operator <<
виклик означає інший виклик з іншим набором аргументів.
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:
Однак, якщо чесно, це нічого не означає, так як введення / виведення - це вузьке місце. Я просто хотів показати, що iostream
це не швидше, тому що це "тип безпечний". Більшість реалізацій C реалізують printf
формати за допомогою обчислюваного goto, тому вони printf
є настільки швидкими, як це можливо, навіть не знаючи компілятора printf
(не те, що вони не є - деякі компілятори можуть оптимізувати printf
в певних випадках - постійні рядки, що закінчуються \n
, зазвичай оптимізуються доputs
) .
Я не знаю, чому ви хочете успадкувати ostream
, але мені все одно. Це FILE
теж можливо .
class MyFile : public FILE {}
Правда, списки аргументів змінної довжини не мають ніякої безпеки, але це не має значення, оскільки популярні компілятори C можуть виявити проблеми з printf
рядком формату, якщо увімкнути попередження. Насправді, Кланг може це зробити, не вмикаючи попередження.
$ 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
, що якось дивно швидко порівняно з qsort
(у 2 рази), на вартість виконавчого розміру).
З FAQ + :
[15.1] Чому я повинен використовувати
<iostream>
замість традиційного<cstdio>
?Підвищити безпеку типу, зменшити помилки, дозволити розширюваність та забезпечити успадкування.
printf()
Можливо, він не порушений, іscanf()
, можливо, може бути використаний, незважаючи на схильність до помилок, однак обидва обмежені стосовно того, що C ++ введення / виведення може зробити. C ++ введення / виведення (за допомогою<<
та>>
) дорівнює C (за допомогоюprintf()
таscanf()
):
- Більш безпечний для типу: З
<iostream>
, компілятор знає статичний тип об'єкта I / O'd. Навпаки,<cstdio>
використовує поля "%" для динамічного визначення типів.- Менше схильних до помилок: з
<iostream>
, немає зайвих "%" жетонів, які повинні відповідати фактичним об'єктам I / O'd. Видалення надмірності видаляє клас помилок.- Розширювана: Механізм C ++
<iostream>
дозволяє новим визначеним користувачем типам бути введенням / виводу без порушення існуючого коду. Уявіть собі хаос, якби всі одночасно додавали нові та несумісні поля "%" доprintf()
таscanf()
?!- Спадковий: Механізм C ++
<iostream>
побудований з реальних класів, таких якstd::ostream
іstd::istream
. На відміну від<cstdio>
російськихFILE*
, це справжні класи і, отже, спадкові. Це означає, що у вас можуть бути інші визначені користувачем речі, які виглядають і діють як потоки, але вони роблять все, що вам дивно і чудово. Ви автоматично використовуєте мільйони рядків коду вводу-виводу, написані користувачами, яких ви навіть не знаєте, і їм не потрібно знати про клас "розширеного потоку".
З іншого боку, printf
це значно швидше, що може виправдати його використання в перевазі cout
в дуже конкретних і обмежених випадках. Завжди профі вперше. (Див., Наприклад, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)
printf
не має такої здатності. Непереносні бібліотечні механізми навряд чи знаходяться на такому рівні, як повністю стандартизована розширюваність іостримів.
Люди часто стверджують, що printf
це набагато швидше. Це багато в чому міф. Я щойно перевірив це, маючи такі результати:
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
Висновок: якщо ви хочете лише нові рядки, використовуйте printf
; інакше cout
- майже так само швидко, а то й швидше. Більш детально можна ознайомитися в моєму блозі .
Щоб було зрозуміло, я не намагаюся сказати, що iostream
s завжди краще, ніж printf
; Я просто намагаюся сказати, що ви повинні приймати обгрунтоване рішення на основі реальних даних, а не дикої здогадки, заснованої на звичайному, оманливому припущенні.
Оновлення: Ось повний код, який я використав для тестування. Компілюється g++
без додаткових опцій (крім -lrt
термінів).
#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()
і std::ostream
полягає в тому, що перший виводить усі аргументи в один єдиний виклик, тоді як std::ostream
для кожного відбувається окремий виклик <<
. Тест виводить лише один аргумент і новий рядок, тому різниці ви не бачите.
printf
може бути зроблено багато дзвінків під кришками до допоміжних функцій для різних специфікаторів форматування ... що, або це жахлива монолітна функція. І знову ж таки, через вкраплення, вона взагалі не повинна змінювати швидкість.
sprintf
або fprintf
та stringstream
або fstream
.
І я цитую :
У високому рівні основні відмінності полягають у безпеці типів (у cstdio цього немає), продуктивності (більшість реалізацій iostreams повільніші, ніж у cstdio) та розширюваності (iostreams дозволяє користувацьким вихідним цілям та безперешкодному виведенню визначених користувачем типів).
Один - це функція, яка друкує до stdout. Інший - це об'єкт, який забезпечує кілька функцій-членів і перевантажує operator<<
цей друк для stdout. Є ще багато відмінностей, які я міг би перерахувати, але я не впевнений, що ти хочеш.
Для мене реальні відмінності, які змусили б мене перейти на 'cout', а не на 'printf':
1) << оператор може бути перевантажений для моїх занять.
2) Вихідний потік для cout можна легко змінити у файл: (: копіювати пасту :)
#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) Я вважаю, що cout є більш зрозумілим, особливо коли у нас багато параметрів.
Одна проблеми з cout
цими параметрами форматування. Форматування даних (точність, обґрунтованість тощо) у printf
них простіше.
printf
файл, замінивши його на fprintf
...
Два моменти, які не згадуються інакше, які я вважаю важливими:
1) cout
перевозить багато багажу, якщо ви вже не використовуєте STL. Він додає у ваш об’єктний файл понад два рази більше коду, ніж printf
. Це також стосується string
, і це головна причина, що я схильний використовувати власну бібліотеку струн.
2) cout
використовує перевантажених <<
операторів, що мені здається невдалим. Це може додати плутанину, якщо ви також використовуєте<<
оператора за призначенням (зсув вліво). Я особисто не люблю перевантажувати операторів для цілей, дотичних до їх призначення.
Підсумок: я буду використовувати cout
(і string
), якщо я вже використовую STL. Інакше я схильний уникати цього.
Для примітивів це, мабуть, не має значення повністю, яким саме ви користуєтесь. Я кажу, де це стає корисним, коли потрібно виводити складні об'єкти.
Наприклад, якщо у вас є клас,
#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;
}
Тепер вищезгадане може здатися не таким чудовим, але припустимо, що вам доведеться виводити це в декількох місцях вашого коду. Мало того, скажімо, ви додаєте поле "int d". За допомогою cout вам потрібно лише поміняти його за один раз. Однак, з printf, вам доведеться змінити його, можливо, в багатьох місцях, і не тільки це, ви повинні нагадати собі, які з них вивести.
Зважаючи на це, за допомогою cout ви можете скоротити багато разів, витрачені на підтримку коду, і не тільки те, що якщо ви повторно використовуєте об’єкт "Щось" у новому додатку, вам не доведеться турбуватися про вихід.
Звичайно, ви можете написати "щось" трохи краще, щоб зберегти технічне обслуговування:
#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;
}
І трохи розширений тест cout vs. printf, додав тест "подвійний", якщо хтось хоче зробити більше тестування (Visual Studio 2008, версія версії виконуваного файлу):
#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);
}
}
Результат:
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
endl
так набагато менш ефективно, ніж '\n'
?
endl
змиває буфер, а \n
ні, хоча я не впевнений, що це остаточно причина.
Я хотів би зазначити, що якщо ви хочете грати з нитками в C ++, якщо ви використовуєте, cout
ви можете отримати цікаві результати.
Розглянемо цей код:
#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
Тепер вихід приходить весь перетасований. Це також може дати різні результати, спробуйте виконати кілька разів:
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
Ви можете використовувати, printf
щоб правильно це зробити, або ви можете використовувати mutex
.
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
Веселіться!
thread
s не роблять висновок. Я просто відтворив і знайшов і те, xyz
і ABC
у виході. Не було маніпулювання б / у ABC
як ABABAB
.
cout
працює з потоками, але я точно знаю, що код, який ви показуєте, не той, який ви використовували для отримання цих виходів. Ваш код передає рядок "ABC"
для потоку 1 і "xyz"
для потоку 2, але ваш результат показує AAA
і BBB
. Виправте це, бо зараз це заплутано.
cout<< "Hello";
printf("%s", "Hello");
Обидва використовуються для друку значень. Вони мають зовсім інший синтаксис. У C ++ є обидва, у C є тільки printf.
Я хотів би сказати, що відсутність розширюваності printf
не зовсім вірна:
В C це правда. Але в С реальних класів немає.
У C ++ можна перевантажувати оператор литих, так що, перевантажуючи char*
оператора і використовуючи printf
так:
Foo bar;
...;
printf("%s",bar);
можливо, якщо Foo перевантажить хорошого оператора. Або якщо ви зробили хороший метод. Словом, printf
такий же розтяжний, як і cout
для мене.
Технічний аргумент, який я бачу для потоків C ++ (загалом ... не тільки cout.), Є:
Типбезпеки. (І, до речі, якщо я хочу надрукувати сингл, який '\n'
я використовую putchar('\n')
... я не буду використовувати ядерну бомбу для вбивства комахи.).
Простіше в навчанні. (відсутність "складних" параметрів для вивчення, просто для використання <<
та >>
операторів)
Робота з рідною std::string
(для printf
є std::string::c_str()
, але для scanf
?)
Бо printf
я бачу:
Легше або принаймні коротше (в терміні написаних символів) складне форматування. Набагато легше для мене читати (я думаю, питання смаку).
Краще керуйте тим, що зробила функція (Поверніть кількість символів, де написано, і є %n
формат. "Нічого не надруковано. Аргумент повинен бути вказівником на підписаний int, де зберігається кількість написаних символів." ( From printf - Довідка C ++ )
Кращі можливості налагодження. З тієї ж причини, що і останній аргумент.
Мої особисті вподобання стосуються printf
(і scanf
) функцій, головним чином тому, що я люблю короткі рядки, і тому, що я не думаю, що проблем з типом друку тексту насправді важко уникнути. Єдине, що я заперечую за допомогою функцій у стилі С, це те, що std::string
вони не підтримуються. Ми повинні пройти через a, char*
перш ніж давати це printf
(з, std::string::c_str()
якщо ми хочемо читати, але як писати?)
char*
, не використовуватиметься.
char*
живе і на скільки часу, і небезпеки, визначені користувачем неявні касти.
Більше відмінностей: "printf" повертає ціле значення (рівне кількості друкованих символів), а "cout" нічого не повертає
І.
cout << "y = " << 7;
не є атомним.
printf("%s = %d", "y", 7);
є атомним.
cout виконує перевірку типу, printf не робить.
Немає жодного потокового еквівалента "% d"
cout
не повертає нічого, тому що це об'єкт, а не функція. operator<<
повертає щось (як правило, його лівий операнд, але помилкове значення, якщо є помилка). І в якому сенсі printf
дзвінок «атомний»?
printf("%s\n",7);
%s
є?
printf
% S аргумент повинен мати дійсний покажчик на нульовий завершальний нуль. Діапазон пам’яті «7» (покажчик) зазвичай не діє; помилка сегментації може пощастити. У деяких системах "7" може надрукувати багато сміття на консолі, і вам доведеться переглянути це протягом доби, перш ніж програма зупиняється. Іншими словами, це погано printf
. Засоби статистичного аналізу можуть наздогнати багато з цих питань.
printf
не робиться перевірка набору тексту, я ніколи не використовував компілятор, який не попереджав мене про помилки типу з printf
...
TL; DR: Завжди робіть свої власні дослідження щодо розміру , продуктивності , читабельності та часу кодування, згенерованого машинного коду , перш ніж довіряти випадковим коментарям в Інтернеті, включаючи цей.
Я не експерт. Мені просто траплялося, що двоє колег підслуховують, як говорити про те, як нам уникати використання C ++ у вбудованих системах через проблеми з продуктивністю. Ну, що цікаво, я зробив орієнтир, заснований на реальному проектному завданні.
У цьому завданні нам довелося записати деяку конфігурацію в ОЗУ. Щось на зразок:
кава = гарячий
цукор = немає
молока = грудний
мак = AA: BB: CC: DD: EE: FF
Ось мої базові програми (Так, я знаю, що ОП запитував про printf (), а не fprintf (). Спробуйте захопити суть і, до речі, посилання ОП вказує на fprintf () у будь-якому випадку.)
Програма 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);
Програма 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();
Я зробив усе можливе, щоб відполірувати їх, перш ніж обробляти їх обома 100 000 разів. Ось результати:
Програма C:
real 0m 8.01s
user 0m 2.37s
sys 0m 5.58s
Програма C ++:
real 0m 6.07s
user 0m 3.18s
sys 0m 2.84s
Розмір файлу об'єкта:
C - 2,092 bytes
C++ - 3,272 bytes
Висновок: На моїй дуже специфічній платформі з дуже специфічним процесором , що працює в дуже специфічній версії ядра Linux , запускається програма, складена з дуже специфічною версією GCC , для виконання дуже конкретного завдання , я б сказав підхід С ++ є більш підходящим, оскільки він працює значно швидше і забезпечує набагато кращу читабельність. З іншого боку, C пропонує невеликий слід, на мою думку, майже нічого не означає, тому що розмір програми не викликає наших проблем.
Remeber, YMMV.
Я не програміст, але я був інженером з людських факторів. Я вважаю, що мову програмування має бути легко вивчати, розуміти та використовувати, і для цього потрібно мати просту і послідовну мовну структуру. Хоча всі мови є символічними і, таким чином, по своїй суті є довільними, існують умовні умови, а слідування за ними полегшує вивчення та використання мови.
Існує велика кількість функцій на C ++ та інших мовах, записаних як функція (параметр), синтаксис, який спочатку використовувався для функціональних зв’язків у математиці в епоху перед комп'ютером. printf()
слід за цим синтаксисом, і якби автори C ++ хотіли створити будь-який логічно інший метод для читання та запису файлів, вони могли просто створити іншу функцію, використовуючи аналогічний синтаксис.
У Python ми, звичайно, можемо друкувати, використовуючи також досить стандартний object.method
синтаксис, тобто змінний.print, оскільки змінні є об'єктами, але в C ++ вони не є.
Мені не подобається синтаксис cout, оскільки оператор << не дотримується жодних правил. Це метод або функція, тобто він бере параметр і щось робить з ним. Однак він написаний так, ніби це математичний оператор порівняння. Це поганий підхід з точки зору людських факторів.
printf
є функцією, тоді як cout
є змінною.
printf
є функцією, але printf()
це функція call =)