«printf» против «cout» в C ++


Ответы:


333

Я удивлен, что все в этом вопросе утверждают, что std::coutэто намного лучше, чем printf, даже если вопрос только что спросил различия. Теперь есть разница - std::coutэто C ++ и printfC (однако вы можете использовать его в 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 - operator <<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]);

Теперь давайте предположим, что мы переводим на Fictionish, где номер ошибки после описания. Переведенная строка будет выглядеть так %2$s oru %1$d.\n. Теперь, как это сделать в C ++? Ну, я понятия не имею. Я предполагаю, что вы можете создать фальшивку, iostreamкоторая будет конструкцией, printfкоторую вы можете передать gettextили что-то еще, для целей перевода. Конечно, $это не стандарт C, но он настолько распространен, что, на мой взгляд, безопасен для использования.

Не нужно запоминать / искать определенный синтаксис целочисленных типов

C имеет много целочисленных типов, как и C ++. std::coutобрабатывает все типы для вас, в то время как printfтребует определенного синтаксиса в зависимости от целочисленного типа (существуют нецелочисленные типы, но единственный нецелочисленный тип, с которым вы будете использовать на практике, printfэто const char *(строка C, может быть получена с использованием to_cметода of 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.

Все думают, что они заботятся о производительности, но никто не мешает ее измерить. Мой ответ таков, что ввод-вывод в любом случае является узким местом, независимо от того, используете вы 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строкой формата, если вы включите предупреждения. На самом деле, Clang может сделать это без включения предупреждений.

$ 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

Из C ++ FAQ :

[15.1] Почему я должен использовать <iostream> вместо традиционного <cstdio>?

Повысить безопасность типов, уменьшить количество ошибок, расширить возможности и обеспечить наследуемость.

printf()возможно, не сломан и scanf(), возможно, пригоден для жизни, несмотря на склонность к ошибкам, однако оба ограничены в отношении того, что может делать ввод / вывод C ++. Ввод-вывод C ++ (с использованием <<и >>) относительно C (с использованием printf()и scanf()):

  • Более безопасный тип: С <iostream>, тип объекта, являющегося вводом / выводом, статически известен компилятору. Напротив, <cstdio>использует поля "%" для динамического определения типов.
  • Менее подвержен ошибкам: при <iostream>этом отсутствуют избыточные токены «%», которые должны соответствовать фактическим объектам ввода-вывода. Удаление избыточности удаляет класс ошибок.
  • Расширяемый: <iostream>механизм C ++ позволяет вводить-выводить новые пользовательские типы, не нарушая существующий код. Представьте себе хаос, если все одновременно добавляли новые несовместимые поля "%" в printf()и scanf()?!
  • Наследуемо: <iostream>механизм C ++ построен из реальных классов, таких как std::ostreamи std::istream. В отличие от <cstdio>s FILE*, это реальные классы и, следовательно, наследуемые. Это означает, что у вас могут быть другие определяемые пользователем вещи, которые выглядят и действуют как потоки, но в то же время делают все, что угодно и удивительно. Вы автоматически получаете возможность использовать миллионы строк кода ввода-вывода, написанных пользователями, которых вы даже не знаете, и им не нужно знать о вашем классе «расширенного потока».

С другой стороны, printfэто значительно быстрее, что может оправдать его использование coutв очень специфических и ограниченных случаях. Всегда профиль первым. (См., Например, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)


2
С другой стороны, есть библиотека FastFormat ( fastformat.org ), предлагающая безопасность типов, выразительность и производительность одновременно. (Не то, чтобы я попробовал это еще ...)
xtofl

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

2
В последнее время printf()также предполагается быть расширяемым. См. « Printf
Максим Егорушкин

4
@MaximYegorushkin: Стандарт printfне имеет такой способности. Механизмы непереносимых библиотек едва ли находятся на том же уровне, что и полностью стандартизированная расширяемость iostreams.
Бен Фойгт

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это почти так же быстро, или даже быстрее. Более подробную информацию можно найти в моем блоге .

Чтобы быть ясным, я не пытаюсь сказать, что iostreamс всегда лучше, чем 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, когда дело доходит до перфорации? Хотя я согласен, что перфект не слишком отличается в реальных случаях ..
mishal153

3
@ mishal153: Я просто пытаюсь сказать, что производительность не слишком отличается, поэтому общепринятый совет «никогда не используйте cout, потому что он медленный», просто глупый. Обратите внимание, что 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

Одна из них - это функция, которая печатает на стандартный вывод. Другой - это объект, который предоставляет несколько функций-членов и перегружает operator<<эту печать в stdout. Я могу перечислить еще много различий, но я не уверен, что вы ищете.


12

Для меня настоящие различия, которые заставили бы меня пойти на «cout», а не «printf»:

1) Оператор << может быть перегружен для моих классов.

2) Поток вывода для cout может быть легко изменен на файл: (: copy paste :)

#include <iostream>
#include <fstream>
using namespace std;

int main ()
{
    cout << "This is sent to prompt" << endl;
    ofstream file;
    file.open ("test.txt");
    streambuf* sbuf = cout.rdbuf();
    cout.rdbuf(file.rdbuf());
    cout << "This is sent to file" << endl;
    cout.rdbuf(sbuf);
    cout << "This is also sent to prompt" << endl;
    return 0;
}

3) Я считаю 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 против printf, добавлен тест 'double', если кто-то хочет проводить больше тестов (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 заключается в том, что он использует operator <<, что является многословным и уродливым и, возможно, злоупотреблением оператором. :)
Джалф

8
Хотя это, конечно, не лучший ответ, я не понимаю, как scatman наказывается за его ответ только потому, что он был выбран как лучший ответ. xbit имеет гораздо худший ответ IMO, но имеет -1 голос. Я не говорю, что за xbit нужно больше голосовать, но я не считаю, что было бы справедливо понизить голос скатмана за ошибку ФП больше, чем это должно быть ...
Джесси

1

Я хотел бы сказать, что отсутствие расширяемости printfне совсем верно:
в Си это правда. Но в Си нет реальных классов.
В 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форматер: «Ничего не напечатано. Аргумент должен быть указателем на целое число со знаком, где хранится количество написанных символов.» (Из printf - C ++ Reference )

  • Лучшие возможности отладки. По той же причине, что и последний аргумент.

Мои личные предпочтения касаются printfscanf) функций, главным образом потому, что я люблю короткие строки и потому, что я не думаю, что проблемы с типом при печати текста действительно трудно избежать. Единственное, что я сожалею о функциях в стиле C, это то, что они std::stringне поддерживаются. Мы должны пройти через, char*прежде чем дать его printf( std::string::c_str()если мы хотим читать, но как писать?)


3
Компилятор не имеет информации о типе для функций varargs, поэтому он не будет преобразовывать фактический параметр (за исключением продвижений аргументов по умолчанию , например стандартных интегральных продвижений). Смотрите 5.2.2p7. Определяемое пользователем преобразование в char*не будет использовано.
Бен Фойгт

Даже если бы это работало, это не было бы примером расширяемости sprintf, просто умным взломом, чтобы дать sprintf то, что он ожидает, и он игнорирует некоторые серьезные проблемы, такие как, где char*живет и как долго, и опасности, определяемые пользователем неявные приведения.
Марсело Кантос

1

Больше различий: «printf» возвращает целочисленное значение (равное количеству напечатанных символов), а «cout» ничего не возвращает

А также.

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

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

cout выполняет проверку типов, printf - нет.

Там нет iostream эквивалента "% 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 ++ во встроенных системах из-за проблем с производительностью. Что ж, интересно, я сделал тест, основанный на реальной задаче проекта.

В указанной задаче мы должны были записать некоторые конфигурации в RAM. Что-то вроде:

кофе = горячий
сахар = нет
молока =
макинтош = AA: BB: CC: DD: EE: FF

Вот мои тестовые программы (да, я знаю, что OP спрашивал о printf (), а не о fprintf (). Попытайтесь понять суть и, кстати, ссылка OP в любом случае указывает на 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 ++ является более подходящим, поскольку он работает значительно быстрее и обеспечивает лучшую читаемость. С другой стороны, C предлагает небольшую площадь, на мой взгляд, почти ничего не значит, потому что размер программы не имеет значения.

Помните, YMMV.


Я не согласен с тем, что C ++ более читабелен в этом примере, потому что ваш пример упаковывает несколько строк в один вызов printf. Это, естественно, менее читабельно, чем то, как вы делали код на C ++, и редко делается на C, потому что его трудно читать и поддерживать. Справедливое сравнение распространило бы C на отдельные printfs, один для досягаемости.
maharvey67

1
@ maharvey67 Это правда, что ты сказал. Тем не менее, пример, который я привел в C, касался производительности. Упакованный вызов в fprintf уже на две секунды медленнее, чем эквивалентность C ++. Если бы я сделал код на С читаемым, он мог бы быть еще медленнее. Отказ от ответственности: это было год назад, и я помню, что изо всех сил старался полировать как C, так и C ++ код. У меня не было доказательств того, что отдельные вызовы fprintf будут быстрее, чем один вызов, но причина, по которой я сделал это, вероятно, указывает на то, что это не так.
Уэсли

0

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

Существует огромное количество функций в C ++ и других языках, написанных как функция (параметр), синтаксис, который первоначально использовался для функциональных отношений в математике в докомпьютерную эпоху. printf()следует этому синтаксису, и если авторы C ++ хотели создать какой-либо логически другой метод для чтения и записи файлов, они могли бы просто создать другую функцию, используя похожий синтаксис.

В Python мы, конечно, можем печатать, используя также достаточно стандартный object.methodсинтаксис, то есть variablename.print, поскольку переменные являются объектами, а в C ++ - нет.

Мне не нравится синтаксис cout, потому что оператор << не следует никаким правилам. Это метод или функция, то есть он принимает параметр и что-то с ним делает. Однако это написано так, как будто это оператор математического сравнения. Это плохой подход с точки зрения человеческого фактора.


-1

printfявляется функцией, тогда как coutявляется переменной.


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

1
printfявляется функцией, но printf()является вызовом функции =)
vp_arth

cout - это объект, а не переменная.
Лин
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.