'printf' vs. 'cout' в C ++


Відповіді:


332

Я здивований, що всі в цьому питанні стверджують, що 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 .

Ви не можете роздрукувати байт NUL, \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);
     ^

18
Ви кажете, що I / O - це вузьке місце. Очевидно, ви ніколи не перевіряли це припущення. Я цитую себе: "З іншого боку, версія iostreams зі швидкістю 75,3 Мб / с не може забезпечити буфер даних досить швидко, щоб не відставати від жорсткого диска. Це погано, і він навіть не робить жодної реальної роботи. "Я думаю, що у мене занадто великі очікування, коли я кажу, що моя бібліотека вводу / виводу повинна мати можливість насичувати дисковий контролер".
Бен Войгт

4
@BenVoigt: Я визнаю, я намагаюся уникати C ++, коли це можливо. Я багато намагався використовувати його, але це було більше дратівливим і менш досяжним, ніж інша мова програмування, яку я використовував. Це ще одна причина для мене, щоб уникати C ++ - це навіть не швидко (це навіть не iostream - вся бібліотека C ++ повільна в більшості реалізацій, можливо, за винятком std::sort, що якось дивно швидко порівняно з qsort(у 2 рази), на вартість виконавчого розміру).
Конрад Боровський

3
Ніхто тут не згадав про проблеми в паралельному середовищі під час використання cout.
Ніколас Гамільтон

9
Ваш аргумент щодо ефективності не має сенсу. Більше збірка у вашій програмі не означає, що програма буде повільнішою, оскільки ви не враховуєте весь код, який створює функцію printf, а це багато коду. На мою думку, можна оптимізувати cout з оператором << набагато краще, ніж printf, тому що компілятор може краще розуміти змінні та форматування.
Ignas2526

18
Мені подобається багато речей щодо цієї відповіді, але, можливо, моя улюблена частина - «Усі думають, що вони дбають про продуктивність, але ніхто не заважає її вимірювати».
Кайл Странд

203

З 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 /)


2
З іншого боку, є бібліотека FastFormat ( fastformat.org ), яка пропонує безпеку типу, виразність та продуктивність одночасно. (Не те, що я ще пробував це ...)
xtofl

3
@Marcelo, ймовірно, тому, що це хороший підсумок, з усім цитованим. Форматування ... так, це дуже погано. Я мав би це виправити, але, схоже, інші (включаючи себе) подбали про це, що, звичайно, є більш конструктивним, ніж просто скуголити.
Майкаж

2
Станом на пізнє printf()також передбачається розширення. Дивіться "гачки друку
Максим

4
@MaximYegorushkin: Стандарт printfне має такої здатності. Непереносні бібліотечні механізми навряд чи знаходяться на такому рівні, як повністю стандартизована розширюваність іостримів.
Бен Войгт

4
"З іншого боку, printf значно швидше" printf також більш чистий і простий у використанні, тому я уникаю cout, коли це можливо.
FluorescentGreen5

43

Люди часто стверджують, що 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- майже так само швидко, а то й швидше. Більш детально можна ознайомитися в моєму блозі .

Щоб було зрозуміло, я не намагаюся сказати, що iostreams завжди краще, ніж 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);
    }
}

5
У ваших партитурах printf б'є кут легко (більшість випадків). Цікаво, чому ви рекомендуєте використовувати cout, якщо мова йде про perf. Хоча я згоден, парф не надто відрізняється в реалістичних випадках ..
mishal153

3
@ mishal153: Я просто намагаюся сказати, що продуктивність не надто різна, тому загальнозвучна порада "ніколи не використовуй cout, тому що це waaay повільно" - це просто нерозумно. Зауважте, що cout має очевидну перевагу безпеки типу, а також часто і читабельності. (Форматування з плаваючою комою в iostreams - жахливе ...)
Томас

35
Важлива відмінність між printf()і std::ostreamполягає в тому, що перший виводить усі аргументи в один єдиний виклик, тоді як std::ostreamдля кожного відбувається окремий виклик <<. Тест виводить лише один аргумент і новий рядок, тому різниці ви не бачите.
Максим Єгорушкін

12
Компілятор повинен мати можливість вбудовувати ці виклики. Крім того, printfможе бути зроблено багато дзвінків під кришками до допоміжних функцій для різних специфікаторів форматування ... що, або це жахлива монолітна функція. І знову ж таки, через вкраплення, вона взагалі не повинна змінювати швидкість.
Томас

4
Ви приурочили свій термінал. Використовувати sprintfабо fprintfта stringstreamабо fstream.
Бен Войгт

41

І я цитую :

У високому рівні основні відмінності полягають у безпеці типів (у cstdio цього немає), продуктивності (більшість реалізацій iostreams повільніші, ніж у cstdio) та розширюваності (iostreams дозволяє користувацьким вихідним цілям та безперешкодному виведенню визначених користувачем типів).


Особливо в unix, де з POSIX ви ніколи не знаєте, якого розміру насправді має typedefs, тому вам потрібно багато кастингу, або як 99% програм ви просто ризикуєте з% d. Був навіть задовго до того, як% z прийшов із C99. Але для time_t / off_t пошуки правильної інструкції формату тривають.
Лотар

30

Один - це функція, яка друкує до stdout. Інший - це об'єкт, який забезпечує кілька функцій-членів і перевантажує operator<<цей друк для stdout. Є ще багато відмінностей, які я міг би перерахувати, але я не впевнений, що ти хочеш.


12

Для мене реальні відмінності, які змусили б мене перейти на '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них простіше.


1
Це гарно. Як я можу знати, що ніхто не модифікує глобальну cout таким чином у якійсь іноземній бібліотечній нитці?
vp_arth

1
Ви також можете легко змінити printfфайл, замінивши його на fprintf...
CoffeeTableEspresso

5

Два моменти, які не згадуються інакше, які я вважаю важливими:

1) coutперевозить багато багажу, якщо ви вже не використовуєте STL. Він додає у ваш об’єктний файл понад два рази більше коду, ніж printf. Це також стосується string, і це головна причина, що я схильний використовувати власну бібліотеку струн.

2) coutвикористовує перевантажених <<операторів, що мені здається невдалим. Це може додати плутанину, якщо ви також використовуєте<< оператора за призначенням (зсув вліво). Я особисто не люблю перевантажувати операторів для цілей, дотичних до їх призначення.

Підсумок: я буду використовувати coutstring), якщо я вже використовую STL. Інакше я схильний уникати цього.


4

Для примітивів це, мабуть, не має значення повністю, яким саме ви користуєтесь. Я кажу, де це стає корисним, коли потрібно виводити складні об'єкти.

Наприклад, якщо у вас є клас,

#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 ви можете скоротити багато разів, витрачені на підтримку коду, і не тільки те, що якщо ви повторно використовуєте об’єкт "Щось" у новому додатку, вам не доведеться турбуватися про вихід.


Крім того, щоб додати інформацію про продуктивність, я б сказав, що вам взагалі нічого не слід виводити, якщо ваша заявка зроблена для продуктивності. Будь-який вихід на std досить дорогий і повільний. Я кажу, що вам слід уникати цього і виводити його лише тоді, коли це абсолютно необхідно.
Даніель

майте на увазі, що у вашому класі можуть бути приватні члени, до яких ви не можете настільки легко отримати доступ ззовні. У оператора виводу у вас є саме одне місцеположення, яке має бути другом вашого класу, і тепер ви можете виводити його будь-де, навіть у коді, про який ви не знали.
hochl

2

Звичайно, ви можете написати "щось" трохи краще, щоб зберегти технічне обслуговування:

#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'?
Ніколас Гамільтон

1
Я вважаю, що це endlзмиває буфер, а \nні, хоча я не впевнений, що це остаточно причина.
Калеб Сю

Це не відповідає на питання, це більше як відповідь на відповідь Даниїла та Томаса .
Фабіо каже: Відновити Моніку

2

Я хотів би зазначити, що якщо ви хочете грати з нитками в 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

Веселіться!


2
wtf threads не роблять висновок. Я просто відтворив і знайшов і те, xyzі ABCу виході. Не було маніпулювання б / у ABCяк ABABAB.
Абхінав Гауніял

1
Я не знаю, як coutпрацює з потоками, але я точно знаю, що код, який ви показуєте, не той, який ви використовували для отримання цих виходів. Ваш код передає рядок "ABC"для потоку 1 і "xyz"для потоку 2, але ваш результат показує AAAі BBB. Виправте це, бо зараз це заплутано.
Фабіо каже: Відновити Моніку

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

Обидва використовуються для друку значень. Вони мають зовсім інший синтаксис. У C ++ є обидва, у C є тільки printf.


19
... що? ти щось змішував?
xtofl

1
Виправлена ​​проблема. -1 тому, що вона потребувала фіксації, і відповідь залишає бажати кращого.
Якобі

3
Імена функцій були змінені: cout використовувався з синтаксисом printf, а printf використовувався з синтаксисом cout. Не повинні були навіть бути прийняті!
Махмуд Аль-Кудсі

2
і головним недоліком cout є те, що він використовує оператор <<, який є багатослівним і некрасивим і, можливо, зловживає оператором. :)
jalf

8
Хоча ця впевненість і не найкраща відповідь, я не розумію, як розстріляного карають за його відповідь лише за те, що його обрали як найкращу відповідь. xbit має гірший відповідь на ІМО, але має -1 голос. Я не кажу, що за Xbit більше не слід голосувати, але я не вважаю, що було б справедливо відкидати розсіювача голосів за помилку ОП більше, ніж це має бути ...
Джессі

1

Я хотів би сказати, що відсутність розширюваності 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 ++ )

  • Кращі можливості налагодження. З тієї ж причини, що і останній аргумент.

Мої особисті вподобання стосуються printfscanf) функцій, головним чином тому, що я люблю короткі рядки, і тому, що я не думаю, що проблем з типом друку тексту насправді важко уникнути. Єдине, що я заперечую за допомогою функцій у стилі С, це те, що std::stringвони не підтримуються. Ми повинні пройти через a, char*перш ніж давати це printf(з, std::string::c_str()якщо ми хочемо читати, але як писати?)


3
У компілятора немає інформації про тип для функцій varargs, тому він не буде конвертувати фактичний параметр (за винятком промоції аргументів за замовчуванням , наприклад стандартних інтегральних промоцій). Див. 5.2.2p7. Конверсія, визначена користувачем char*, не використовуватиметься.
Бен Войгт

Навіть якби це спрацювало, це не було б прикладом розширюваності спринту, а лише хитрим хаком, щоб дати спринту те, що він очікує, і ігнорує деякі серйозні питання, такі як, де char*живе і на скільки часу, і небезпеки, визначені користувачем неявні касти.
Марсело Кантос

1

Більше відмінностей: "printf" повертає ціле значення (рівне кількості друкованих символів), а "cout" нічого не повертає

І.

cout << "y = " << 7; не є атомним.

printf("%s = %d", "y", 7); є атомним.

cout виконує перевірку типу, printf не робить.

Немає жодного потокового еквівалента "% d"


3
coutне повертає нічого, тому що це об'єкт, а не функція. operator<<повертає щось (як правило, його лівий операнд, але помилкове значення, якщо є помилка). І в якому сенсі printfдзвінок «атомний»?
Кіт Томпсон

9
Це як атомна бомба. printf("%s\n",7);
бездушний шум

@artlessnoise зачекайте, чому виникла сегментація? %sє?
Абхінав Гауніял

1
У цьому суть заяви "атомної бомби". A printf % S аргумент повинен мати дійсний покажчик на нульовий завершальний нуль. Діапазон пам’яті «7» (покажчик) зазвичай не діє; помилка сегментації може пощастити. У деяких системах "7" може надрукувати багато сміття на консолі, і вам доведеться переглянути це протягом доби, перш ніж програма зупиняється. Іншими словами, це погано printf. Засоби статистичного аналізу можуть наздогнати багато з цих питань.
невгамовний шум

Хоча технічно printfне робиться перевірка набору тексту, я ніколи не використовував компілятор, який не попереджав мене про помилки типу з printf...
CoffeeTableEspresso

1

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 ++, і це рідко робиться в C, оскільки його важко читати і важко підтримувати. Справедливе порівняння поширило б С на окремі друковані шрифти, один за лінією охоплення.
maharvey67

1
@ maharvey67 Це правда, що ти сказав. Однак приклад, який я надавав у С, стосувався ефективності. Запропонований виклик fprintf був на дві секунди повільніше, ніж еквівалентність С ++. Якби я зробив код C читабельним, то він може бути навіть повільніше. Відмова: Це було рік тому, і я пам'ятаю, що я намагався зробити все можливе, щоб відшліфувати і C, і C ++ код. У мене не було доказів, що окремі дзвінки до fprintf були б швидшими, ніж один єдиний дзвінок, але причина, що я робив це таким чином, ймовірно, свідчить про те, що це не так.
Веслі

0

Я не програміст, але я був інженером з людських факторів. Я вважаю, що мову програмування має бути легко вивчати, розуміти та використовувати, і для цього потрібно мати просту і послідовну мовну структуру. Хоча всі мови є символічними і, таким чином, по своїй суті є довільними, існують умовні умови, а слідування за ними полегшує вивчення та використання мови.

Існує велика кількість функцій на C ++ та інших мовах, записаних як функція (параметр), синтаксис, який спочатку використовувався для функціональних зв’язків у математиці в епоху перед комп'ютером. printf()слід за цим синтаксисом, і якби автори C ++ хотіли створити будь-який логічно інший метод для читання та запису файлів, вони могли просто створити іншу функцію, використовуючи аналогічний синтаксис.

У Python ми, звичайно, можемо друкувати, використовуючи також досить стандартний object.method синтаксис, тобто змінний.print, оскільки змінні є об'єктами, але в C ++ вони не є.

Мені не подобається синтаксис cout, оскільки оператор << не дотримується жодних правил. Це метод або функція, тобто він бере параметр і щось робить з ним. Однак він написаний так, ніби це математичний оператор порівняння. Це поганий підхід з точки зору людських факторів.


-1

printfє функцією, тоді як coutє змінною.


6
Я зробив відкат, тому що, хоча сама відповідь може бути помилковою, все-таки справжня відповідь. Якщо ви (правильно) вважаєте, що відповідь неправильна, у вас є два варіанти: 1) додати коментар або 2) додати нову відповідь (або зробити і те й інше). Не змінюйте чиюсь відповідь на таку, що говорить щось зовсім інше, ніж те, що було задумано автором.
Марк

1
printfє функцією, але printf()це функція call =)
vp_arth

cout - це об'єкт, а не змінна.
Лін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.