Должен ли я использовать printf в моем коде C++?
Я обычно использую cout и cerr для записи текста на консоль. Однако иногда мне легче использовать старый добрый printf заявление. Я использую его, когда мне нужно отформатировать вывод.
один пример, где я хотел бы использовать это:
// Lets assume that I'm printing coordinates...
printf("(%d,%d)n", x, y);
// To do the same thing as above using cout....
cout << "(" << x << "," << y << ")" << endl;
Я знаю, что могу форматировать вывод с помощью cout но я уже знаю, как использовать printf. Есть ли причина, по которой я не должен использовать printf заявление?
19 ответов:
мои ученики, которые учатся
cinиcoutво-первых, а затем узнатьprintfпозднее, в подавляющем большинстве предпочитаютprintf(или более обычноfprintf). Я сам нашелprintfмодель достаточно читабельна, чтобы я портировал ее на другие языки программирования. Так же как и Оливье Danvy, который даже сделал его тип-безопасный.если у вас есть компилятор, который способен проверять типы вызовов
printf, Я не вижу причин не использоватьfprintfи друзей в С.++отказ от ответственности: я ужасный программист на C++.
Если вы когда-нибудь надеетесь i18n вашей программы, держаться подальше от iostreams. Проблема в том, что невозможно правильно локализовать ваши строки, если предложение состоит из нескольких фрагментов, как это делается с iostream.
помимо вопроса о фрагментах сообщений, у вас также есть проблема заказа. Рассмотрим отчет, который печатает имя студента и их средний балл:
std::cout << name << " has a GPA of " << gpa << std::endl;когда вы перевести это на другой язык, другой язык грамматика может понадобиться вам, чтобы показать средний балл перед именем. Насколько мне известно, iostreams с не способ переупорядочить интерполированных значений.
Если вы хотите лучшее из обоих миров (тип безопасности и возможность i18n), используйте импульс.Формат.
адаптивность
любая попытка
printfне-POD приводит к неопределенному поведению:struct Foo { virtual ~Foo() {} operator float() const { return 0.f; } }; printf ("%f", Foo()); std::string foo; printf ("%s", foo);вышеуказанные функции printf звонки доходность неопределенного поведения. Ваш компилятор может действительно предупредить вас, но эти предупреждения не требуются по стандартам и не возможны для строк формата, известных только во время выполнения.
IO-Streams:
std::cout << Foo(); std::string foo; std::cout << foo;судья себе.
расширения
struct Person { string first_name; string second_name; }; std::ostream& operator<< (std::ostream &os, Person const& p) { return os << p.first_name << ", " << p.second_name; } cout << p; cout << p; some_file << p;C:
// inline everywhere printf ("%s, %s", p.first_name, p.second_name); printf ("%s, %s", p.first_name, p.second_name); fprintf (some_file, "%s, %s", p.first_name, p.second_name);или:
// re-usable (not common in my experience) int person_fprint(FILE *f, const Person *p) { return fprintf(f, "%s, %s", p->first_name, p->second_name); } int person_print(const Person *p) { return person_fprint(stdout, p); } Person p; .... person_print(&p);обратите внимание, как вы должны заботиться об использовании правильных аргументов/подписей вызова в C (например,
person_fprint(stderr, ...,person_fprint(myfile, ...), где в C++"FILE-аргумент "автоматически" выводится " из выражения. Более точный эквивалент этого вывода на самом деле больше похож на это:FILE *fout = stdout; ... fprintf(fout, "Hello World!\n"); person_fprint(fout, ...); fprintf(fout, "\n");как i18n
мы повторно используем нашу персону определение:cout << boost::format("Hello %1%") % p; cout << boost::format("Na %1%, sei gegrüßt!") % p; printf ("Hello %1$s, %2$s", p.first_name.c_str(), p.second_name.c_str()); printf ("Na %1$s, %2$s, sei gegrüßt!", p.first_name.c_str(), p.second_name.c_str());судите сами.
я нахожу это менее актуально на сегодняшний день (2017). Может быть, просто чувство кишки, но I18N-это не то, что делается ежедневно вашим средним программистом на C или c++. Кроме того, это боль в а...натомия в любом случае.
производительность
- вы измерили фактическое значение производительности printf? Являются ли ваши приложения узким местом серьезно настолько ленивыми, что выход результаты вычислений-это узкое место? Вы уверены, что вам вообще нужен C++?
- страшный производительности, чтобы удовлетворить тех из вас, кто не хочет использовать printf и cout. Это особенность, а не ошибка!
если вы используете iostreams последовательно, вы можете
std::ios::sync_with_stdio(false);и reap равное время выполнения с хорошим компилятором:
#include <cstdio> #include <iostream> #include <ctime> #include <fstream> void ios_test (int n) { for (int i=0; i<n; ++i) { std::cout << "foobarfrob" << i; } } void c_test (int n) { for (int i=0; i<n; ++i) { printf ("foobarfrob%d", i); } } int main () { const clock_t a_start = clock(); ios_test (10024*1024); const double a = (clock() - a_start) / double(CLOCKS_PER_SEC); const clock_t p_start = clock(); c_test (10024*1024); const double p = (clock() - p_start) / double(CLOCKS_PER_SEC); std::ios::sync_with_stdio(false); const clock_t b_start = clock(); ios_test (10024*1024); const double b = (clock() - b_start) / double(CLOCKS_PER_SEC); std::ofstream res ("RESULTS"); res << "C ..............: " << p << " sec\n" << "C++, sync with C: " << a << " sec\n" << "C++, non-sync ..: " << b << " sec\n"; }результаты (
g++ -O3 synced-unsynced-printf.cc,./a.out > /dev/null,cat RESULTS):C ..............: 1.1 sec C++, sync with C: 1.76 sec C++, non-sync ..: 1.01 secсудьи ... себе.
нет. Вы не запретите мне мой printf.
вы можете haz typesafe, i18n friendly printf в C++11, благодаря вариативным шаблонам. И вы сможете иметь их очень, очень производительными, используя пользовательские литералы, т. е. можно будет написать полностью статическое воплощение.
у меня есть доказательство концепции. Тогда поддержка C++11 была не такой зрелой, как сейчас, но вы получите идею.
временные Адаптивность
// foo.h ... struct Frob { unsigned int x; }; ... // alpha.cpp ... printf ("%u", frob.x); ... // bravo.cpp ... printf ("%u", frob.x); ... // charlie.cpp ... printf ("%u", frob.x); ... // delta.cpp ... printf ("%u", frob.x); ...позже ваши данные становятся настолько большими, что вы должны сделать
// foo.h ... unsigned long long x; ...это интересное упражнение, поддерживающее это и делающее его без ошибок. Особенно когда другие, несвязанные проекты используют фу.h.
другие.
Потенциальная Ошибка: есть много места для совершения ошибок с printf, особенно когда вы бросаете строки базы ввода пользователя в миксе (подумайте о вашем I18N команда.) Вы должны позаботиться о том, чтобы правильно экранировать каждую такую строку формата, вы должны быть уверены, что передаете правильные аргументы и т. д. так далее..
IO-потоки делают мой двоичный файл больше: если это более важная проблема, чем ремонтопригодность, качество кода, возможность повторного использования, то (после проверки проблемы!) использовать функции printf.
использовать boost::формат. Вы получаете безопасность типа, поддержку std:: string, интерфейс printf, возможность использования cout и множество других хороших вещей. Ты не вернешься.
нет причин вообще. Я думаю, что это просто какая-то странная идеология, которая заставляет людей использовать только библиотеки C++, хотя старые добрые библиотеки C все еще действительны. Я парень C++, и я использую функции C тоже много. Никогда не было никаких проблем с ними.
потоки являются каноническим способом. Попробуйте заставить этот код работать с
printf:template <typename T> void output(const T& pX) { std::cout << pX << std::endl; }удачи.
Я имею в виду, что вы можете сделать операторы, чтобы ваши типы выводились в
ostream' s, и без хлопот использовать его так же, как и любой другой тип.printfне соответствует общности C++, или более конкретно шаблонов.там больше, чем юзабилити. Есть также последовательность. Во всех моих проектах у меня есть cout (и
cerrиclog) te'D также выводить в файл. Если вы используетеprintf, вы пропустите все это. Кроме того, сама консистенция-это хорошо; смешиваниеcoutиprintf, в то время как совершенно справедливо, уродливо.если у вас есть объект, и вы хотите сделать его выходным, самый чистый способ сделать это-перегрузка
operator<<для этого класса. Как вы собираетесь использоватьprintfпотом? Вы собираетесь в конечном итоге с кодом перемешаны сcoutиprintf' s.если вы действительно хотите форматировать, использовать Повышение.Форматирование при сохранении интерфейса потока. Последовательность и форматирование.
используйте printf. Не используйте потоки C++. printf дает вам гораздо лучший контроль (например, точность поплавка и т. д.). Код также обычно короче и читабельнее.
руководство по стилю Google C++ согласен.
не использовать потоки, за исключением тех случаев, когда требуется-интерфейс. Использовать вместо этого используются printf-подобные процедуры.
есть различные плюсы и минусы использование потоков, но в данном случае, как в многие другие случаи, последовательность козыри дебаты. Не используйте потоки в вашем код.
в целом я согласен (ненавижу
но я должен указать на аспекты безопасности.
printf("%x",2.0f) printf("%x %x",2) printf("%x",2,2)вероятно, компилятор не заметит, но может привести к сбою вашего приложения.
используйте все, что соответствует вашим потребностям и предпочтениям. Если вам удобно с printf, то обязательно используйте его. Если вы счастливы с iostreams придерживаться их. Смешивать и сочетать, как лучше всего соответствует вашим требованиям. В конце концов, это программное обеспечение - есть лучшие способы и худшие способы, но редко есть только один способ.
поделиться и наслаждаться.
Мне не нравится printf. Отсутствие безопасности типа делает его опасным для использования, плюс нужно помнить спецификаторы формата-это боль. Шаблонные операторы, которые умно делают правильные вещи, намного лучше. Поэтому я всегда использую потоки C++ в C++.
конечно, многие люди предпочитают printf, для другое причины, перечисленные в другом месте.
Я часто "возвращаюсь" к использованию
printf(), но чащеsnprintf()для облегчения форматирования вывода. При программировании на C++ я использую эту оболочку, которую я написал некоторое время назад, называемую так (чтобы использовать ваш пример, как указано выше):cout << format("(%d,%d)\n", x, y);здесь заголовок (
stdiomm.h):#pragma once #include <cstdarg> #include <string> template <typename T> std::basic_string<T> format(T const *format, ...); template <typename T> std::basic_string<T> vformat(T const *format, va_list args);и источник (
stdiomm.cpp):#include "stdiomm.h" #include <boost/scoped_array.hpp> #include <cstdio> template <> std::wstring vformat(wchar_t const *format, va_list arguments) { #if defined(_WIN32) int required(_vscwprintf(format, arguments)); assert(required >= 0); boost::scoped_array<wchar_t> buffer(new wchar_t[required + 1]); int written(vswprintf(buffer.get(), required + 1, format, arguments)); assert(written == required); return std::wstring(buffer.get(), written); #else # error "No implementation yet" #endif } template <> std::string vformat(char const *format, va_list arguments) { #if defined(_WIN32) int required(_vscprintf(format, arguments)); assert(required >= 0); boost::scoped_array<char> buffer(new char[required + 1]); int written(vsnprintf(buffer.get(), required + 1, format, arguments)); assert(written == required); return std::string(buffer.get(), written); #else char *buffer; int printed = vasprintf(&buffer, format, arguments); assert(printed != -1); std::string retval(buffer, printed); free(buffer); return retval; #endif } template <typename T> std::basic_string<T> format(T const *format, ...) { va_list ap; va_start(ap, format); std::basic_string<T> retval(vformat(format, ap)); va_end(ap); return retval; } template std::wstring format(wchar_t const *format, ...); template std::string format(char const *format, ...);обновление
после прочтения некоторых других ответов мне, возможно, придется переключиться на
boost::format()сам!
хотя вопрос довольно старый, я хочу добавить свои два цента.
печать пользовательских объектов с помощью printf ()
Это довольно просто, если вы думаете об этом - вы можете stringify свой тип и отправил строку в printf:
std::string to_string(const MyClass &x) { return to_string(x.first)+" "+to_string(x.second); } //... printf("%s is awesome", to_string(my_object).c_str()); //more or lessобидно не было (есть с++11 to_string()) стандартизированный интерфейс C++, чтобы преобразовать в строки объектов...
printf () pitfall
один флаг - %n
единственный выходной параметр - он ожидает указатель на int. Он записывает количество успешно написанных символов в место, указанное этим указателем. Умелое использование его может вызвать переполнение, что является уязвимостью безопасности (см. printf() format string attack).
Вы можете получить лучшее из обоих миров с библиотека fmt который сочетает в себе безопасность и расширяемость iostreams с удобством использования и производительностью
(s)printf. Пример:std::string = fmt::format("The answer is {}", 42);библиотека поддерживает синтаксис строк формата Python и printf.
отказ от ответственности: Я автор библиотека fmt.
Я почти всегда использую printf для временных операторов отладки. Для более постоянного кода я предпочитаю потоки "c", как они есть Путь C++. Хотя boost:: format выглядит многообещающе и может заменить мое использование потока (особенно для сложно отформатированного вывода), вероятно, ничто не заменит printf для меня в течение длительного времени.
C++ потоки - это перебор, ведь они на самом деле просто классы с перегруженным оператором
<<.
Я много раз читал, что потоки-это C++ way как printf является C путь, но они являются функциями библиотеки в C++, так что вы должны использовать то, что подходит лучше всего.
Я в основном предпочитаю printf, но я также использовал потоки, которые обеспечивают более чистый код и не позволяют вам сопоставлять заполнители % с аргументами.
Это зависит от ситуации. Нет ничего совершенного. Я использую оба. Потоки хороши для пользовательских типов, поскольку вы можете перегрузить оператор >> в ostream. Но когда дело доходит до интервалов и т. д., Лучше использовать printf(). stringstream и like лучше, чем стиль c strcat(). Поэтому используйте тот, который подходит для данной ситуации.
Я читал предупреждения о том, что cout и cerr небезопасны для многопоточности. Если правда, то это хорошая причина, чтобы избежать их использования. Примечание: Я использую GNU g++ с openMP.
потоки предпочтительны в cpp, поскольку они придерживаются объектно-ориентированной парадигмы cpp, кроме того, чтобы быть безопасным типом.
printf, с другой стороны, является более функциональным подходом.
единственная причина не использовать printf в коде cpp, о которой я могу думать, не является объектно-ориентированным.
Это скорее личный выбор.
Comments