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

236

В чем разница между printf() и cout в С++?

Теги:
printf
cout

16 ответов

175

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

Отказ от ответственности: я более опытен с C, чем С++, поэтому, если есть проблема с моим ответом, не стесняйтесь редактировать или комментировать.

Реальные различия

расширяемость

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

В отличие от printf, std::cout полностью зависит от перегрузки оператора, поэтому нет проблем с пользовательскими форматами - все, что вы делаете, это определение подпрограммы, принимающей std::ostream как первый аргумент, а ваш тип - второй. Таким образом, нет проблем с пространством имен, поскольку у вас есть класс (который не ограничивается одним символом), вы можете иметь для него перегрузку std::ostream.

Однако я сомневаюсь, что многие люди захотят расширить ostream (честно говоря, я редко видел такие расширения, даже если их легко сделать). Однако, здесь, если вам это нужно.

Синтаксис

Как легко заметить, оба printf и std::cout используют разные синтаксисы. printf использует стандартный синтаксис функций, используя строки шаблонов и списки аргументов переменной длины. На самом деле, printf - причина, по которой C имеет их - форматы printf слишком сложны, чтобы их можно было использовать без них. Однако std::cout использует другой API - API operator <<, который возвращает себя.

Как правило, это означает, что версия 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 было бы типом (отличным от С++, конечно). 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. Теперь, как это сделать на С++? Ну, я понятия не имею. Думаю, вы можете сделать фальшивый iostream, который строит printf, который вы можете передать в gettext или что-то еще для перевода. Конечно, $ не является стандартом C, но он настолько распространен, что он безопасен для использования по моему мнению.

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

C имеет множество целочисленных типов, а также С++. std::cout обрабатывает все типы для вас, а printf требует определенного синтаксиса в зависимости от целочисленного типа (существуют нецелые типы, но единственным нецеловым типом, который вы будете использовать на практике с printf, является const char * ( C, можно получить с помощью метода to_c std::string)). Например, для печати size_t вам нужно использовать %zd, а для int64_t потребуется использовать %"PRIu64"d. Таблицы доступны в http://en.cppreference.com/w/cpp/io/c/fprintf и http://en.cppreference.com/w/cpp/types/integer.

Вы не можете напечатать байт NUL, \0

Поскольку printf использует строки 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:

Однако, честно говоря, это ничего не значит, так как I/O является узким местом в любом случае. Я просто хотел показать, что iostream не быстрее, потому что он "type safe". Большинство реализаций 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);
     ^
$ 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);
     ^
  • 11
    Вы говорите, что I / O является узким местом в любом случае. Очевидно, вы никогда не проверяли это предположение. Я цитирую себя: «С другой стороны, версия iostreams с пропускной способностью 75,3 МБ / с не может достаточно быстро буферизовать данные, чтобы не отставать от жесткого диска. Это плохо, и даже пока не выполняет никакой реальной работы. не думаю, что у меня слишком большие ожидания, когда я говорю, что моя библиотека ввода-вывода должна быть способна насытить мой контроллер диска ».
  • 1
    @ BenVoigt: Признаюсь, я стараюсь избегать C ++, когда это возможно. Я много пытался его использовать, но это было более раздражающим и менее понятным, чем другие языки программирования, которые я использовал. Это еще одна причина для меня избегать C ++ - это даже не быстро (это даже не iostream - вся библиотека C ++ медленна в большинстве реализаций, возможно, за исключением std::sort , который почему-то удивительно быстр по сравнению с qsort ( 2 раза), за счет размера исполняемого файла).
Показать ещё 11 комментариев
169

Из Часто задаваемые вопросы по С++:

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

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

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

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

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

  • 1
    С другой стороны, есть библиотека FastFormat ( fastformat.org ), предлагающая безопасность типов, выразительность и производительность одновременно. (Не то, чтобы я попробовал это еще ...)
  • 0
    Как такой плохо представленный фрагмент вставки копии, без учета побега (отсюда и множество необъяснимых упущений в тексте), получает девять голосов?
Показать ещё 9 комментариев
37

И я quote:

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

  • 0
    Особенно в Unix, где с POSIX вы никогда не знаете, какого размера действительно имеет одна из typedefs, поэтому вам нужно много приведений или в 99% программ вы просто рискуете с% d. Прошло даже много времени, прежде чем% z пришел с C99. Но для time_t / off_t поиск правильной инструкции формата продолжается.
34

Люди часто утверждают, что 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);
    }
}
  • 0
    "подробнее в вашем блоге"? Едва. В вашем блоге отсутствуют те же данные, что и в этом посте: в нем не говорится о том, что вы тестировали или как вы его тестировали.
  • 0
    @jalf: Думаешь? Я написал ссылку вместо того, чтобы публиковать полный код здесь, потому что он немного длинный и повторяющийся. Я определенно написал там, что я тестировал и как ... текста в моем блоге должно быть достаточно для воспроизведения кода. Но я отредактировал это в своем посте ... этого достаточно?
Показать ещё 16 комментариев
30

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

11

Для меня настоящие различия, которые заставили бы меня перейти на "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 таким образом в каком-то другом потоке библиотеки?
5

Две точки, не упомянутые здесь иначе, я нахожу значительными:

1) cout несет много багажа, если вы еще не используете STL. Он добавляет в ваш объектный файл более чем вдвое больше кода printf. Это также верно для string, и это основная причина, по которой я склонен использовать свою собственную библиотеку строк.

2) cout использует перегруженные операторы <<, которые я нахожу неудачными. Это может добавить путаницу, если вы также используете оператор << по своему назначению (сдвиг влево). Мне лично не нравится перегружать операторов для целей, которые касаются их предполагаемого использования.

Итог: я буду использовать coutstring), если я уже использую STL. В противном случае я стараюсь избегать этого.

3

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

Например, если у вас есть класс,

#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, вы можете сократить много раз, потраченные на обслуживание вашего кода, а не только на то, что если вы повторно используете объект "Что-то" в новом приложении, вам действительно не нужно беспокоиться о выходе.

  • 0
    Кроме того, если говорить о производительности, я бы сказал, что вы вообще ничего не должны выводить, если ваше приложение создано для повышения производительности. Любой вывод в std довольно дорогой и медленный. Я говорю, что вы должны избегать этого и выводить только тогда, когда это абсолютно необходимо.
  • 0
    имейте в виду, что в вашем классе могут быть частные члены, к которым вы не можете получить доступ извне. С оператором вывода у вас есть ровно одно местоположение, которое должно быть дружелюбным вашему классу, и теперь вы можете вывести его в любом месте, даже в коде, о котором вы не знали.
1

TL; DR: всегда выполняйте собственные исследования в отношении кода машинного кода , производительности, читабельности и . время, прежде чем доверять случайным комментариям в Интернете, включая этот.

Я не эксперт. Мне просто пришлось подслушать двух сотрудников, говорящих о том, как избежать использования С++ во встроенных системах из-за проблем с производительностью. Ну, достаточно интересно, я сделал тест, основанный на реальной проектной задаче.

В указанной задаче нам пришлось написать некоторый конфиг в ОЗУ. Что-то вроде:

кофе = горячий
нет сахара = нет
молоко = грудь
Mac = 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);

Программа на С++:

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

Программа на С++:

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

Размер файла объекта:

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

Заключение: на моей конкретной платформе с очень специфическим процессором, работающим с очень конкретной версией Linux kernel, для запуска которая скомпилирована с очень конкретной версией GCC, чтобы выполнить очень специфическую задачу, я бы сказал, что подход на С++ более подходит, потому что он работает значительно быстрее и обеспечивают гораздо лучшую читаемость. С другой стороны, C предлагает небольшую площадь, на мой взгляд, почти ничего не значит, потому что размер программы не касается нас.

Память, YMMV.

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

Я хотел бы указать, что если вы хотите играть с потоками на С++, если вы используете 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 thread не делает вывод с ума. Я только что воспроизвел и нашел и xyz и ABC на выходе. Там не было калечат ч / б ABC как ABABAB .
  • 1
    Я не знаю, как cout работает с потоками, но я точно знаю, что код, который вы показываете, не тот, который вы использовали для получения этих результатов. Ваш код передает строку "ABC" для потока 1 и "xyz" для потока 2, но ваши выходные данные показывают AAA и BBB . Пожалуйста, исправьте это, потому что сейчас это сбивает с толку.
1

Другие отличия: "printf" возвращает целочисленное значение (равное количеству напечатанных символов), а "cout" ничего не возвращает

А.

cout << "y = " << 7; не является атомарным.

printf("%s = %d", "y", 7); является атомным.

cout выполняет typechecking, printf не делает.

Нет эквивалента iostream "% d"

  • 2
    cout ничего не возвращает, потому что это объект, а не функция. operator<< возвращает что-то (обычно это левый операнд, но ложное значение, если есть ошибка). И в каком смысле printf называется «атомным»?
  • 8
    Это как атомная бомба. printf("%s\n",7);
Показать ещё 2 комментария
1

Я бы сказал, что отсутствие расширяемости printf не совсем верно:
В C это правда. Но в C нет реальных классов.
В С++ можно перегрузить оператор рассылки, поэтому, перегружая оператор char* и используя printf следующим образом:

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

может быть возможно, если Foo перегрузит хороший оператор. Или, если вы сделали хороший метод. Короче говоря, printf для меня такой же расширяемый, как cout.

Технический аргумент, который я вижу для потоков С++ (в общем... не только cout.):

  • типобезопасность. (И, кстати, если я хочу напечатать один '\n', я использую putchar('\n')... Я не буду использовать ядерную бомбу, чтобы убить насекомое.).

  • Проще учиться. (нет "сложных" параметров для изучения, просто для использования операторов << и >>)

  • Работайте с std::string (для printf есть std::string::c_str(), но для scanf?)

Для printf я вижу:

  • Легче или, по крайней мере, короче (с точки зрения написания символов) сложное форматирование. Гораздо читаем, для меня (вопрос вкуса, я думаю).

  • Лучший контроль над тем, что сделала функция (верните, сколько написанных символов, а также форматировщик %n: "Ничего не напечатано. Аргумент должен быть указателем на подписанный int, где количество написанных символов до сих пор хранится." (from printf - С++ Reference)

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

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

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

Конечно, вы можете написать "что-то" немного лучше, чтобы поддерживать обслуживание:

#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, добавлен тест "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
  • 0
    Вау, почему endl намного менее эффективен, чем '\n' ?
  • 0
    Я полагаю, это потому, что endl очищает буфер, а \n - нет, хотя я не уверен, что это определенно причина.
Показать ещё 1 комментарий
0
cout<< "Hello";
printf("%s", "Hello"); 

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

  • 18
    ... какие? ты что-то перепутал?
  • 1
    Исправлена проблема. -1 потому что требовала исправления, а ответ оставляет желать лучшего.
Показать ещё 10 комментариев
-4

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

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

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

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

-5

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

  • 4
    Я сделал откат, потому что, хотя сам ответ может быть неправильным, он все же является подлинным ответом. Если вы (правильно) считаете, что ответ неправильный, у вас есть два варианта: 1) добавить комментарий или 2) добавить новый ответ (или сделать оба). Не меняйте чей-либо ответ на такой, что он говорит что-то совершенно не то, что задумал автор.
  • 0
    printf это функция, но printf() это вызов функции =)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню