Должен ли я использовать printf в моем коде C ++? - PullRequest
67 голосов
/ 07 января 2010

Я обычно использую 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 ]

68 голосов
/ 07 января 2010

Мои ученики, которые сначала изучают cin и cout, а затем учат printf позже, в подавляющем большинстве предпочитают printf (или более обычно fprintf).Я сам нашел модель printf достаточно читабельной, чтобы перенести ее на другие языки программирования.Так же, как и Оливье Данви , который даже сделал его безопасным для типов.

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

Отказ от ответственности: Я ужасный программист на C ++.

48 голосов
/ 07 января 2010

Если вы когда-нибудь надеетесь включить свою программу, держитесь подальше от iostreams. Проблема в том, что может быть невозможно правильно локализовать ваши строки, если предложение состоит из нескольких фрагментов, как это делается с iostream.

Помимо вопроса о фрагментах сообщений, у вас также есть проблема заказа. Рассмотрим отчет, в котором напечатано имя учащегося и средний балл:

std::cout << name << " has a GPA of " << gpa << std::endl;

Когда вы переводите это на другой язык, грамматика другого языка может потребовать, чтобы вы указали средний балл перед именем. AFAIK, iostreams не имеет способа изменить порядок интерполированных значений.

Если вы хотите получить лучшее из обоих миров (безопасность и возможность i18n), используйте Boost.Format .

21 голосов
/ 09 февраля 2012

адаптивность

Любая попытка 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

Мы повторно используем наше определение Person:

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

Если вы постоянно используете iostreams, вы можете

std::ios::sync_with_stdio(false);

и получите равное время выполнения с хорошим компилятором:

#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

Судите ... сами.

Нет. Вы не запретите мне мой принт.

Благодаря разнообразным шаблонам вы можете печатать на C ++ 11 в безопасных для печати типах I18N. И вы сможете получить их очень, очень производительно, используя пользовательские литералы, то есть можно будет написать полностью статическое воплощение.

У меня есть подтверждение концепции . Тогда поддержка 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;
...

Это интересное упражнение, поддерживающее это и делающее его без ошибок. Особенно, когда другие несвязанные проекты используют foo.h .

прочее.

  • Потенциал ошибок : Существует много места для ошибок в printf, особенно когда вы добавляете строки пользовательского ввода в микс (подумайте о вашей команде I18N). Вы должны позаботиться о правильном экранировании каждой такой строки формата, вы должны обязательно передать правильные аргументы и т. Д. И т. Д.

  • IO-Streams делает мой двоичный файл больше : Если это более важный вопрос, чем удобство сопровождения, качество кода, возможность повторного использования, тогда (после проверки проблемы!) Используйте printf.

19 голосов
/ 07 января 2010

Используйте boost :: format. Вы получаете безопасность типов, поддержку std :: string, printf-подобный интерфейс, возможность использовать cout и много других полезных вещей. Ты не вернешься.

19 голосов
/ 07 января 2010

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

7 голосов
/ 07 января 2010

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

6 голосов
/ 07 января 2010

Потоки - это канонический путь. Попробуйте заставить этот код работать с printf:

template <typename T>
void output(const T& pX)
{
    std::cout << pX << std::endl;
}

Удачи.

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

Там больше, чем простота использования. Там также последовательность. Во всех моих проектах у меня есть cout (и cerr и clog), которые также выводятся в файл. Если вы используете printf, вы пропустите все это. Кроме того, последовательность сама по себе является хорошей вещью; смешивание cout и printf, хотя совершенно верно, ужасно.

Если у вас есть объект, и вы хотите сделать его пригодным для вывода, самый простой способ сделать это - перегрузка operator<< для этого класса. Как вы собираетесь использовать printf тогда? Вы получите код, перемешанный с cout и printf.

Если вы действительно хотите форматировать, используйте Boost.Format, сохраняя интерфейс потока. Согласованность форматирования и .

5 голосов
/ 07 января 2010

В целом я согласен (ненавижу синтаксис <<, особенно если вам нужно сложное форматирование) </p>

Но я должен отметить аспекты безопасности.

printf("%x",2.0f)
printf("%x %x",2)
printf("%x",2,2)

Вероятно, компилятор не заметит его, но может привести к сбою приложения.

5 голосов
/ 07 января 2010

Используйте printf. Не используйте потоки C ++. printf дает вам гораздо лучший контроль (например, точность с плавающей точкой и т. д.). Код также обычно короче и более читабелен.

Руководство по стилю Google C ++ согласен.

Не использовать потоки, кроме случаев, когда требуется интерфейс регистрации. использование вместо printf-подобных процедур.

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

4 голосов
/ 07 января 2010

Используйте все, что соответствует вашим потребностям и предпочтениям. Если вас устраивает printf, тогда обязательно используйте его. Если вы счастливы с iostreams, придерживайтесь их. Смешивать и сочетать, как лучше всего соответствует вашим требованиям. В конце концов, это программное обеспечение - есть лучшие и худшие пути, но редко есть ОДИН.

Делись и наслаждайся.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...