Я удивлен, что все в этом вопросе утверждают, что 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 - 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 .
\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);
^
std::sort
, что несколько удивительно быстро по сравнению сqsort
(2 раза), в стоимость исполняемого размера).Из C ++ FAQ :
С другой стороны,
printf
это значительно быстрее, что может оправдать его использованиеcout
в очень специфических и ограниченных случаях. Всегда профиль первым. (См., Например, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)источник
printf()
также предполагается быть расширяемым. См. « Printfprintf
не имеет такой способности. Механизмы непереносимых библиотек едва ли находятся на том же уровне, что и полностью стандартизированная расширяемость iostreams.Люди часто утверждают, что
printf
это намного быстрее. Это во многом миф. Я только что проверил это, со следующими результатами:Вывод: если вам нужны только новые строки, используйте
printf
; в противном случаеcout
это почти так же быстро, или даже быстрее. Более подробную информацию можно найти в моем блоге .Чтобы быть ясным, я не пытаюсь сказать, что
iostream
с всегда лучше, чемprintf
; Я просто пытаюсь сказать, что вы должны принять обоснованное решение, основанное на реальных данных, а не дикие предположения, основанные на некоторых распространенных, вводящих в заблуждение предположениях.Обновление: вот полный код, который я использовал для тестирования. Составлено
g++
без каких-либо дополнительных опций (кроме-lrt
сроков).источник
printf()
иstd::ostream
заключается в том, что первый выводит все аргументы в одном вызове, тогдаstd::ostream
как для каждого из них выполняется отдельный вызов<<
. Тест выводит только один аргумент и новую строку, поэтому вы не видите разницу.printf
может быть много вызовов под прикрытием вспомогательных функций для различных спецификаторов форматирования ... или это чудовищная монолитная функция. И опять же, из-за наклона, это не должно иметь никакого значения в скорости вообще.sprintf
илиfprintf
иstringstream
илиfstream
.И я цитирую :
источник
Одна из них - это функция, которая печатает на стандартный вывод. Другой - это объект, который предоставляет несколько функций-членов и перегружает
operator<<
эту печать в stdout. Я могу перечислить еще много различий, но я не уверен, что вы ищете.источник
Для меня настоящие различия, которые заставили бы меня пойти на «cout», а не «printf»:
1) Оператор << может быть перегружен для моих классов.
2) Поток вывода для cout может быть легко изменен на файл: (: copy paste :)
3) Я считаю cout более читабельным, особенно когда у нас много параметров.
Одна проблемы с
cout
этими параметрами форматирования. Форматирование данных (точность, выравнивание и т. Д.)printf
Упрощается.источник
printf
файл, заменив его наfprintf
...Два момента, не упомянутых здесь иным, которые я считаю существенными:
1)
cout
перевозит много багажа, если вы еще не используете STL. Он добавляет вдвое больше кода в ваш объектный файл, чемprintf
. Это также верноstring
, и это главная причина, по которой я склонен использовать свою собственную библиотеку строк.2)
cout
использует перегруженные<<
операторы, что я считаю неудачным. Это может привести к путанице, если вы также используете<<
оператор по назначению (сдвиг влево). Лично я не люблю перегружать операторов в целях, которые касаются их предполагаемого использования.Итог: я буду использовать
cout
(иstring
), если я уже использую STL. В противном случае, я склонен избегать этого.источник
С примитивами, вероятно, не имеет значения, какой из них вы используете. Я говорю, где это полезно, когда вы хотите выводить сложные объекты.
Например, если у вас есть класс,
Теперь вышеприведенное может показаться не таким уж замечательным, но давайте предположим, что вы должны вывести это в нескольких местах в вашем коде. Кроме того, допустим, вы добавили поле «int d». С Cout вам нужно только изменить его в одном месте. Тем не менее, с printf вам придется менять его, возможно, во многих местах, и не только это, вы должны напоминать себе, какие из них выводить.
С учетом сказанного, с помощью cout вы можете сократить много времени, затрачиваемого на обслуживание кода, и не только то, что если вы повторно используете объект «Нечто» в новом приложении, вам не нужно беспокоиться о выводе.
источник
Конечно, вы можете написать «кое-что» немного лучше, чтобы сохранить обслуживание:
И немного расширенный тест cout против printf, добавлен тест 'double', если кто-то хочет проводить больше тестов (Visual Studio 2008, выпуск исполняемого файла):
Результат:
источник
endl
так менее эффективно'\n'
?endl
очищает буфер, а\n
не делает, хотя я не уверен, что это определенно причина.Я хотел бы отметить, что если вы хотите играть с потоками в C ++, если вы используете,
cout
вы можете получить некоторые интересные результаты.Рассмотрим этот код:
Теперь на выходе получается все перемешано. Это также может дать разные результаты, попробуйте выполнить несколько раз:
Вы можете использовать,
printf
чтобы сделать это правильно, или вы можете использоватьmutex
.Радоваться, веселиться!
источник
thread
s не делает вывод сходить с ума. Я просто воспроизвел и нашел обаxyz
иABC
в выходной. Там не было калечат ч / бABC
какABABAB
.cout
работает с потоками, но я точно знаю, что код, который вы показываете, не тот, который вы использовали для получения этих результатов. Ваш код передает строку"ABC"
для потока 1 и"xyz"
для потока 2, но ваш вывод показываетAAA
иBBB
. Пожалуйста, исправьте это, потому что сейчас это сбивает с толку.Оба используются для печати значений. У них совершенно другой синтаксис. C ++ имеет оба, C имеет только printf.
источник
Я хотел бы сказать, что отсутствие расширяемости
printf
не совсем верно:в Си это правда. Но в Си нет реальных классов.
В C ++ возможно перегрузить оператор приведения, поэтому перегрузить
char*
оператор и использоватьprintf
так:Это может быть возможно, если Foo перегрузит хороший оператор. Или если вы сделали хороший метод. Короче говоря,
printf
это так же расширяемо, как иcout
для меня.Технический аргумент, который я вижу для потоков C ++ (в общем ... не только cout.):
Типобезопасность. (И, между прочим, если я хочу напечатать один, который
'\n'
я используюputchar('\n')
... Я не буду использовать ядерную бомбу, чтобы убить насекомое.).Проще учиться. (нет «сложных» параметров для изучения, просто для использования
<<
и>>
операторов)Работа изначально с
std::string
(дляprintf
естьstd::string::c_str()
, но дляscanf
?)Ибо
printf
я вижу:Более простое или, по крайней мере, более короткое (с точки зрения написания символов) сложное форматирование. Гораздо более читабельный, для меня (вопрос вкуса, я думаю).
Лучший контроль над тем, что сделала функция (Возвращает, сколько символов было написано и есть
%n
форматер: «Ничего не напечатано. Аргумент должен быть указателем на целое число со знаком, где хранится количество написанных символов.» (Из printf - C ++ Reference )Лучшие возможности отладки. По той же причине, что и последний аргумент.
Мои личные предпочтения касаются
printf
(иscanf
) функций, главным образом потому, что я люблю короткие строки и потому, что я не думаю, что проблемы с типом при печати текста действительно трудно избежать. Единственное, что я сожалею о функциях в стиле C, это то, что ониstd::string
не поддерживаются. Мы должны пройти через,char*
прежде чем дать егоprintf
(std::string::c_str()
если мы хотим читать, но как писать?)источник
char*
не будет использовано.char*
живет и как долго, и опасности, определяемые пользователем неявные приведения.Больше различий: «printf» возвращает целочисленное значение (равное количеству напечатанных символов), а «cout» ничего не возвращает
А также.
cout << "y = " << 7;
не атомно.printf("%s = %d", "y", 7);
атомно.cout выполняет проверку типов, printf - нет.
Там нет iostream эквивалента
"% d"
источник
cout
ничего не возвращает, потому что это объект, а не функция.operator<<
возвращает что-то (обычно его левый операнд, но ложное значение, если есть ошибка). И в каком смысле этоprintf
называется «атомным»?printf("%s\n",7);
%s
является ?printf
% S аргумент должен иметь действительный указатель на нулевой завершающий нуль. Диапазон памяти «7» (указатель) обычно недействителен; ошибка сегментации может быть удачной. В некоторых системах «7» может выводить много мусора на консоль, и вам придется посмотреть на нее за день до остановки программы. Другими словами, это плохоprintf
. Инструменты статического анализа могут решить многие из этих проблем.printf
он не выполняет проверку типов, я никогда не использовал компилятор, который не предупреждал меня об ошибках типов сprintf
...TL; DR. Всегда проводите собственные исследования в отношении размера сгенерированного машинного кода , производительности , читаемости и времени кодирования, прежде чем доверять случайным комментариям в Интернете, включая этот.
Я не эксперт. Я случайно услышал, как два коллеги говорили о том, как мы должны избегать использования C ++ во встроенных системах из-за проблем с производительностью. Что ж, интересно, я сделал тест, основанный на реальной задаче проекта.
В указанной задаче мы должны были записать некоторые конфигурации в RAM. Что-то вроде:
Вот мои тестовые программы (да, я знаю, что OP спрашивал о printf (), а не о fprintf (). Попытайтесь понять суть и, кстати, ссылка OP в любом случае указывает на fprintf ().)
C программа:
C ++ программа:
Я сделал все возможное, чтобы отполировать их, прежде чем я зациклил их обоих 100 000 раз. Вот результаты:
C программа:
C ++ программа:
Размер файла объекта:
Вывод: На моей очень специфической платформе с очень специфическим процессором , работающим с очень специфической версией ядра Linux , для запуска программы, которая компилируется с очень специфической версией GCC , чтобы выполнить очень специфическую задачу , я бы сказал, подход C ++ является более подходящим, поскольку он работает значительно быстрее и обеспечивает лучшую читаемость. С другой стороны, C предлагает небольшую площадь, на мой взгляд, почти ничего не значит, потому что размер программы не имеет значения.
Помните, YMMV.
источник
Я не программист, но я был инженером по человеческим факторам. Я считаю, что язык программирования должен быть легким в изучении, понимании и использовании, и это требует, чтобы он имел простую и последовательную лингвистическую структуру. Хотя все языки являются символическими и, следовательно, по своей сути произвольными, существуют соглашения, и следование им делает язык более легким для изучения и использования.
Существует огромное количество функций в C ++ и других языках, написанных как функция (параметр), синтаксис, который первоначально использовался для функциональных отношений в математике в докомпьютерную эпоху.
printf()
следует этому синтаксису, и если авторы C ++ хотели создать какой-либо логически другой метод для чтения и записи файлов, они могли бы просто создать другую функцию, используя похожий синтаксис.В Python мы, конечно, можем печатать, используя также достаточно стандартный
object.method
синтаксис, то есть variablename.print, поскольку переменные являются объектами, а в C ++ - нет.Мне не нравится синтаксис cout, потому что оператор << не следует никаким правилам. Это метод или функция, то есть он принимает параметр и что-то с ним делает. Однако это написано так, как будто это оператор математического сравнения. Это плохой подход с точки зрения человеческого фактора.
источник
printf
является функцией, тогда какcout
является переменной.источник
printf
является функцией, ноprintf()
является вызовом функции =)