Должен ли я использовать 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 заявление?

678   19  

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++. Кроме того, это боль в а...натомия в любом случае.

производительность

  1. вы измерили фактическое значение производительности printf? Являются ли ваши приложения узким местом серьезно настолько ленивыми, что выход результаты вычислений-это узкое место? Вы уверены, что вам вообще нужен C++?
  2. страшный производительности, чтобы удовлетворить тех из вас, кто не хочет использовать 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.

Я использую printf, потому что я ненавижу уродливый <<cout<< синтаксис.

использовать 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

    Ничего не найдено.