Ответы:
Я удивлен, что все в этом вопросе утверждают, что 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 :
[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>
sFILE*
, это реальные классы и, следовательно, наследуемые. Это означает, что у вас могут быть другие определяемые пользователем вещи, которые выглядят и действуют как потоки, но в то же время делают все, что угодно и удивительно. Вы автоматически получаете возможность использовать миллионы строк кода ввода-вывода, написанных пользователями, которых вы даже не знаете, и им не нужно знать о вашем классе «расширенного потока».
С другой стороны, printf
это значительно быстрее, что может оправдать его использование cout
в очень специфических и ограниченных случаях. Всегда профиль первым. (См., Например, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)
printf()
также предполагается быть расширяемым. См. « Printf
printf
не имеет такой способности. Механизмы непереносимых библиотек едва ли находятся на том же уровне, что и полностью стандартизированная расширяемость iostreams.
Люди часто утверждают, что 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);
}
}
printf()
и std::ostream
заключается в том, что первый выводит все аргументы в одном вызове, тогда std::ostream
как для каждого из них выполняется отдельный вызов <<
. Тест выводит только один аргумент и новую строку, поэтому вы не видите разницу.
printf
может быть много вызовов под прикрытием вспомогательных функций для различных спецификаторов форматирования ... или это чудовищная монолитная функция. И опять же, из-за наклона, это не должно иметь никакого значения в скорости вообще.
sprintf
или fprintf
и stringstream
или fstream
.
И я цитирую :
В терминах высокого уровня основными различиями являются безопасность типов (cstdio не имеет их), производительность (большинство реализаций iostreams медленнее, чем реализации cstdio) и расширяемость (iostreams позволяет настраивать конечные цели вывода и беспрепятственный вывод пользовательских типов).
Одна из них - это функция, которая печатает на стандартный вывод. Другой - это объект, который предоставляет несколько функций-членов и перегружает operator<<
эту печать в stdout. Я могу перечислить еще много различий, но я не уверен, что вы ищете.
Для меня настоящие различия, которые заставили бы меня пойти на «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
Упрощается.
printf
файл, заменив его на fprintf
...
Два момента, не упомянутых здесь иным, которые я считаю существенными:
1) cout
перевозит много багажа, если вы еще не используете STL. Он добавляет вдвое больше кода в ваш объектный файл, чем printf
. Это также верно string
, и это главная причина, по которой я склонен использовать свою собственную библиотеку строк.
2) cout
использует перегруженные <<
операторы, что я считаю неудачным. Это может привести к путанице, если вы также используете <<
оператор по назначению (сдвиг влево). Лично я не люблю перегружать операторов в целях, которые касаются их предполагаемого использования.
Итог: я буду использовать cout
(и string
), если я уже использую STL. В противном случае, я склонен избегать этого.
С примитивами, вероятно, не имеет значения, какой из них вы используете. Я говорю, где это полезно, когда вы хотите выводить сложные объекты.
Например, если у вас есть класс,
#include <iostream>
#include <cstdlib>
using namespace std;
class Something
{
public:
Something(int x, int y, int z) : a(x), b(y), c(z) { }
int a;
int b;
int c;
friend ostream& operator<<(ostream&, const Something&);
};
ostream& operator<<(ostream& o, const Something& s)
{
o << s.a << ", " << s.b << ", " << s.c;
return o;
}
int main(void)
{
Something s(3, 2, 1);
// output with printf
printf("%i, %i, %i\n", s.a, s.b, s.c);
// output with cout
cout << s << endl;
return 0;
}
Теперь вышеприведенное может показаться не таким уж замечательным, но давайте предположим, что вы должны вывести это в нескольких местах в вашем коде. Кроме того, допустим, вы добавили поле «int d». С Cout вам нужно только изменить его в одном месте. Тем не менее, с printf вам придется менять его, возможно, во многих местах, и не только это, вы должны напоминать себе, какие из них выводить.
С учетом сказанного, с помощью cout вы можете сократить много времени, затрачиваемого на обслуживание кода, и не только то, что если вы повторно используете объект «Нечто» в новом приложении, вам не нужно беспокоиться о выводе.
Конечно, вы можете написать «кое-что» немного лучше, чтобы сохранить обслуживание:
#include <iostream>
#include <cstdlib>
using namespace std;
class Something
{
public:
Something(int x, int y, int z) : a(x), b(y), c(z) { }
int a;
int b;
int c;
friend ostream& operator<<(ostream&, const Something&);
void print() const { printf("%i, %i, %i\n", a, b, c); }
};
ostream& operator<<(ostream& o, const Something& s)
{
o << s.a << ", " << s.b << ", " << s.c;
return o;
}
int main(void)
{
Something s(3, 2, 1);
// Output with printf
s.print(); // Simple as well, isn't it?
// Output with cout
cout << s << endl;
return 0;
}
И немного расширенный тест cout против 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'
?
endl
очищает буфер, а \n
не делает, хотя я не уверен, что это определенно причина.
Я хотел бы отметить, что если вы хотите играть с потоками в C ++, если вы используете, cout
вы можете получить некоторые интересные результаты.
Рассмотрим этот код:
#include <string>
#include <iostream>
#include <thread>
using namespace std;
void task(int taskNum, string msg) {
for (int i = 0; i < 5; ++i) {
cout << "#" << taskNum << ": " << msg << endl;
}
}
int main() {
thread t1(task, 1, "AAA");
thread t2(task, 2, "BBB");
t1.join();
t2.join();
return 0;
}
// g++ ./thread.cpp -o thread.out -ansi -pedantic -pthread -std=c++0x
Теперь на выходе получается все перемешано. Это также может дать разные результаты, попробуйте выполнить несколько раз:
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
Вы можете использовать, printf
чтобы сделать это правильно, или вы можете использовать mutex
.
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
Радоваться, веселиться!
thread
s не делает вывод сходить с ума. Я просто воспроизвел и нашел оба xyz
и ABC
в выходной. Там не было калечат ч / б ABC
как ABABAB
.
cout
работает с потоками, но я точно знаю, что код, который вы показываете, не тот, который вы использовали для получения этих результатов. Ваш код передает строку "ABC"
для потока 1 и "xyz"
для потока 2, но ваш вывод показывает AAA
и BBB
. Пожалуйста, исправьте это, потому что сейчас это сбивает с толку.
cout<< "Hello";
printf("%s", "Hello");
Оба используются для печати значений. У них совершенно другой синтаксис. C ++ имеет оба, C имеет только printf.
Я хотел бы сказать, что отсутствие расширяемости printf
не совсем верно:
в Си это правда. Но в Си нет реальных классов.
В C ++ возможно перегрузить оператор приведения, поэтому перегрузить 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 )
Лучшие возможности отладки. По той же причине, что и последний аргумент.
Мои личные предпочтения касаются 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. Что-то вроде:
кофе = горячий
сахар = нет
молока =
макинтош = 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 ++ хотели создать какой-либо логически другой метод для чтения и записи файлов, они могли бы просто создать другую функцию, используя похожий синтаксис.
В Python мы, конечно, можем печатать, используя также достаточно стандартный object.method
синтаксис, то есть variablename.print, поскольку переменные являются объектами, а в C ++ - нет.
Мне не нравится синтаксис cout, потому что оператор << не следует никаким правилам. Это метод или функция, то есть он принимает параметр и что-то с ним делает. Однако это написано так, как будто это оператор математического сравнения. Это плохой подход с точки зрения человеческого фактора.
printf
является функцией, тогда как cout
является переменной.
printf
является функцией, но printf()
является вызовом функции =)