Я использую много printf
для целей трассировки / регистрации в моем коде, я обнаружил, что это источник ошибки программирования. Я всегда считал, что оператор вставки ( <<
) был чем-то странным, но я начинаю думать, что, используя его, я мог бы избежать некоторых из этих ошибок.
У кого-нибудь было подобное откровение, или я просто хватаюсь за соломинку здесь?
Некоторые забирают очки
- В настоящее время я считаю, что безопасность типов перевешивает любые преимущества использования printf. Настоящая проблема - это строка формата и использование не типизированных переменных функций.
- Может быть, я не буду использовать
<<
и варианты потока вывода stl, но я непременно расскажу об использовании механизма защиты типов, который очень похож. - Большая часть трассировки / ведения журнала является условной, но я бы хотел всегда запускать код, чтобы не пропустить ошибки в тестах только потому, что это редко используемая ветвь.
printf
в мире C ++? Я что-то здесь упускаю?printf
в C ++. (Является ли это хорошей идеей, это другой вопрос.)printf
имеет некоторые преимущества; смотри мой ответ.Ответы:
printf, особенно в тех случаях, когда вы можете заботиться о производительности (например, sprintf и fprintf), является действительно странным хаком. Меня постоянно удивляет, что люди, которые работают на C ++ из-за незначительных накладных расходов на производительность, связанных с виртуальными функциями, будут продолжать защищать C.
Да, чтобы выяснить формат нашего вывода, то, что мы можем знать на 100% во время компиляции, давайте проанализируем измененную строку формата во время выполнения внутри очень странной таблицы переходов, используя непостижимые коды формата!
Конечно, эти коды форматов нельзя было сделать так, чтобы они соответствовали типам, которые они представляют, это было бы слишком просто ... и вам будет напоминаться каждый раз, когда вы просматриваете, является ли% llg или% lg тем, что этот (строго типизированный) язык делает вас вычислять типы вручную, чтобы что-то печатать / сканировать, И был разработан для пред-32-битных процессоров.
Я признаю, что обработка C ++ ширины и точности формата громоздка и может использовать некоторый синтаксический сахар, но это не значит, что вам нужно защищать причудливый хак, который является основной системой ввода-вывода C. Абсолютные основы довольно просты в любом языке (хотя вы, вероятно, должны использовать что-то вроде пользовательской функции ошибок / потока ошибок для отладочного кода в любом случае), умеренные случаи подобны регулярному выражению в C (легко писать, трудно анализировать / отлаживать ), а сложные случаи невозможны в C.
(Если вы вообще используете стандартные контейнеры, напишите себе несколько быстрых шаблонных операторов << overloads, которые позволяют вам делать такие вещи, как
std::cout << my_list << "\n";
для отладки, где my_list имеет типlist<vector<pair<int,string> > >
.)источник
operator<<(ostream&, T)
путем вызова ... нуsprintf
,! Производительностьsprintf
не оптимальна, но из-за этого производительность iostreams, как правило, еще хуже.Смешивание вывода в стиле C
printf()
(илиputs()
илиputchar()
или ...) с выводом в стиле C ++std::cout << ...
может быть небезопасным. Если я правильно помню, у них могут быть отдельные механизмы буферизации, поэтому выходные данные могут не отображаться в намеченном порядке. (Как AProgrammer упоминает в комментарии,sync_with_stdio
обращается к этому).printf()
принципиально небезопасен. Тип, ожидаемый для аргумента, определяется форматной строкой ("%d"
требуется символint
или что-то, что повышаетсяint
,"%s"
требуется элемент,char*
который должен указывать на правильно завершенную строку в стиле C и т. Д.), Но передача неверного типа аргумента приводит к неопределенному поведению , не диагностируемая ошибка. Некоторые компиляторы, такие как gcc, достаточно хорошо предупреждают о несоответствиях типов, но они могут делать это только в том случае, если строка формата является литералом или иным образом известна во время компиляции (что является наиболее распространенным случаем) - и так далее. предупреждения не требуются языком. Если вы передадите неправильный тип аргумента, может произойти сколь угодно плохое.С другой стороны, потоковый ввод / вывод в C ++ намного более безопасен для типов, поскольку
<<
оператор перегружен для множества различных типов.std::cout << x
не нужно указывать типx
; компилятор сгенерирует правильный код для любого типаx
.С другой стороны,
printf
параметры форматирования ИМХО гораздо удобнее. Если я хочу напечатать значение с плавающей точкой с 3 цифрами после десятичной точки, я могу использовать"%.3f"
- и это не влияет на другие аргументы, даже в пределах того же самогоprintf
вызова. С ++setprecision
, с другой стороны, влияет на состояние потока и может испортить последующий вывод, если вы не очень осторожны, чтобы вернуть поток в его предыдущее состояние. (Это моя личная любимая мозоль; если мне не хватает какого-то чистого способа избежать этого, пожалуйста, прокомментируйте.)Оба имеют свои преимущества и недостатки. Доступность
printf
особенно полезна, если у вас есть фон C и вы с ним более знакомы, или если вы импортируете исходный код C в программу C ++.std::cout << ...
более идиоматичен для C ++ и не требует особого внимания, чтобы избежать несоответствия типов. Оба являются действительными C ++ (стандарт C ++ включает в себя большую часть стандартной библиотеки C по ссылке).Вероятно, это лучше всего использовать
std::cout << ...
ради других программистов C ++, которые могут работать над вашим кодом, но вы можете использовать любой из них - особенно в коде трассировки, который вы собираетесь выбросить.И, конечно, стоит потратить некоторое время на изучение того, как использовать отладчики (но в некоторых средах это может оказаться невозможным).
источник
printf
.std::ios_base::sync_with_stdio
.std::cout
использует отдельный вызов для каждого печатного элемента? Вы можете обойти это, собрав строку вывода в строку перед печатью. И, конечно, вы также можете печатать по одному элементу за разprintf
; просто удобнее напечатать строку (или больше) за один звонок.Скорее всего, ваша проблема возникла из-за смешения двух совершенно разных стандартных менеджеров вывода, у каждого из которых есть своя повестка дня для этого бедного маленького STDOUT. Вы не получаете никаких гарантий относительно того, как они реализованы, и вполне возможно, что они устанавливают конфликтующие параметры дескриптора файла, оба пытаются делать с ним разные вещи и т. Д. Кроме того, операторы вставки имеют одну важную особенность
printf
:printf
позволит вам сделать это:Тогда как
<<
не будет.Примечание: для отладки вы не используете
printf
илиcout
. Вы используетеfprintf(stderr, ...)
иcerr
.источник
printf
он не безопасен для типов, и я считаю, что безопасность типов перевешивает любые преимущества использованияprintf
. Проблема на самом деле в строке формата и не типизированной переменной функции.SomeObject
это не указатель? Вы собираетесь получить произвольные двоичные данные, которые решает представлять компиляторSomeObject
.Есть много групп, например Google, которым не нравятся потоки.
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Streams
(Откройте треугольник, чтобы вы могли увидеть обсуждение.) Я думаю, что в руководстве по стилю Google C ++ есть много очень разумных советов.
Я думаю, что компромисс в том, что потоки безопаснее, но printf понятнее (и легче получить именно то форматирование, которое вы хотите).
источник
printf
может вызвать ошибки из-за отсутствия безопасности типа. Есть несколько способов адресации , которые не переключаясь наiostream
«s<<
оператор и более сложное форматирование:printf
строки формата поprintf
аргументам и отображать предупреждения, такие как следующие, если они не совпадают.printf
вызовы -style, чтобы сделать их безопасными для типов.printf
строки формата, похожие на форматы (в частности, Boost.Format практически идентичныprintf
), сохраняя при этомiostreams
«безопасность типов и расширяемость типов».источник
Синтаксис printf в основном хорош, за исключением некоторых непонятных типов. Если вы считаете, что это неправильно, почему C #, Python и другие языки используют очень похожую конструкцию? Проблема в C или C ++: он не является частью языка и, следовательно, не проверяется компилятором на предмет правильного синтаксиса (*) и не разбивается на серии собственных вызовов при оптимизации по скорости. Обратите внимание, что при оптимизации по размеру вызовы printf могут оказаться более эффективными! Синтаксис потокового C ++ imho совсем не хорош. Это работает, безопасность типов есть, но подробный синтаксис ... блеф. Я имею в виду, я использую это, но без радости.
(*) некоторые компиляторы выполняют эту проверку плюс почти все инструменты статического анализа (я использую Lint и с тех пор у меня никогда не было проблем с printf).
источник
format("fmt") % arg1 % arg2 ...;
) с безопасностью типов. За счет некоторой дополнительной производительности, поскольку он генерирует вызовы stringstream, которые внутренне генерируют вызовы sprintf во многих реализациях.printf
По моему мнению, это гораздо более гибкий инструмент вывода для работы с переменными, чем любой из результатов потока CPP. Например:Однако вам может потребоваться использовать
<<
оператор CPP, когда вы перегружаете его для определенного метода ... например, чтобы получить дамп объекта, который содержит данные конкретного человека,PersonData
....Для этого было бы гораздо эффективнее сказать (если предположить,
a
что это объект PersonData)чем:
Первый гораздо больше соответствует принципу инкапсуляции (нет необходимости знать специфику, частные переменные-члены), а также его легче читать.
источник
Вы не должны использовать
printf
в C ++. Когда-либо. Причина, как вы правильно заметили, в том, что это источник ошибок и тот факт, что печать пользовательских типов, а в C ++ почти все должны быть пользовательскими типами, - это боль. C ++ решение - это потоки.Однако существует критическая проблема, которая делает потоки непригодными для любого и видимого пользователю вывода! Проблема в переводе. Взяв пример из руководства по gettext , вы хотите написать:
Теперь приходит немецкий переводчик и говорит: «Хорошо, на немецком языке сообщение должно быть
А теперь у вас неприятности, потому что ему нужны перемешанные кусочки. Следует сказать, что даже многие реализации
printf
имеют проблемы с этим. Если они не поддерживают расширение, чтобы вы могли использоватьВ Boost.Format поддерживает форматы PRINTF стиля и имеет эту функцию. Итак, вы пишете:
К сожалению, он несет в себе некоторое снижение производительности, поскольку внутренне он создает поток строк и использует
<<
оператор для форматирования каждого бита, а во многих реализациях<<
оператор вызывает его внутреннеsprintf
. Я подозреваю, что более эффективная реализация была бы возможна, если бы действительно желательноисточник
Вы выполняете много бесполезной работы, помимо того, что
stl
это зло или нет, вы отлаживаете свой код серией всегоprintf
лишь на 1 уровень возможных сбоев.Просто используйте отладчик и прочитайте что-нибудь об Исключениях и о том, как их перехватить и выбросить; постарайтесь не быть более многословным, чем на самом деле.
PS
printf
используется в C, для C ++ у вас естьstd::cout
источник