简体   繁体   English

我应该在我的 C++ 代码中使用 printf 吗?

[英]Should I use printf in my C++ code?

I generally use cout and cerr to write text to the console.我通常使用coutcerr将文本写入控制台。 However sometimes I find it easier to use the good old printf statement.但是有时我发现使用旧的printf语句更容易。 I use it when I need to format the output.我在需要格式化输出时使用它。

One example of where I would use this is:我将使用它的一个例子是:

// 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;

I know I can format output using cout but I already know how to use the printf .我知道我可以使用cout格式化输出,但我已经知道如何使用printf Is there any reason I shouldn't use the printf statement?有什么理由我不应该使用printf语句吗?

My students, who learn cin and cout first, then learn printf later, overwhelmingly prefer printf (or more usually fprintf ).我的学生,他们先学习cincout ,然后再学习printf ,绝大多数更喜欢printf (或者更常见的fprintf )。 I myself have found the printf model sufficiently readable that I have ported it to other programming languages.我自己发现printf模型具有足够的可读性,因此我已将其移植到其他编程语言中。 So has Olivier Danvy , who has even made it type-safe. Olivier Danvy 也是如此,他甚至使它类型安全。

Provided you have a compiler that is capable of type-checking calls to printf , I see no reason not to use fprintf and friends in C++.如果您有一个能够对printf调用进行类型检查的编译器,我认为没有理由不在 C++ 中使用fprintf和朋友。

Disclaimer: I am a terrible C++ programmer.免责声明:我是一个糟糕的 C++ 程序员。

If you ever hope to i18n your program, stay away from iostreams.如果您希望对您的程序进行 i18n,请远离 iostreams。 The problem is that it can be impossible to properly localize your strings if the sentence is composed of multiple fragments as is done with iostream.问题是,如果句子像使用 iostream 那样由多个片段组成,则不可能正确本地化您的字符串。

Besides the issue of message fragments, you also have an issue of ordering.除了消息片段的问题,您还有一个排序问题。 Consider a report that prints a student's name and their grade point average:考虑一个打印学生姓名及其平均成绩的报告:

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

When you translate that to another language, the other language's grammar may need you to show the GPA before the name.当您将其翻译成另一种语言时,另一种语言的语法可能需要您在名称前显示 GPA。 AFAIK, iostreams has not way to reorder the interpolated values. AFAIK,iostreams 无法重新排序插值。

If you want the best of both worlds (type safety and being able to i18n), use Boost.Format .如果您想要两全其美(类型安全和能够 i18n),请使用Boost.Format

我使用 printf 是因为我讨厌丑陋的<<cout<<语法。

Adaptability适应性

Any attempt to printf a non-POD results in undefined behaviour:任何对非 POD 的printf尝试都会导致未定义的行为:

struct Foo { 
    virtual ~Foo() {}
    operator float() const { return 0.f; }
};

printf ("%f", Foo());

std::string foo;
printf ("%s", foo);

The above printf-calls yield undefined behaviour.上面的 printf 调用产生未定义的行为。 Your compiler may warn you indeed, but those warnings are not required by the standards and not possible for format strings only known at runtime.您的编译器确实可能会警告您,但这些警告不是标准所要求的,并且对于仅在运行时已知的格式字符串是不可能的。

IO-Streams: IO 流:

std::cout << Foo();
std::string foo;
std::cout << foo;

Judge yourself.自己判断吧。

Extensibility可扩展性

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);

or:或:

// 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);

Note how you have to take care of using the proper call arguments/signatures in C (eg person_fprint(stderr, ... , person_fprint(myfile, ... ), where in C++, the " FILE -argument" is automatically "derived" from the expression. A more exact equivalent of this derivation is actually more like this:请注意如何在 C 中使用正确的调用参数/签名(例如person_fprint(stderr, ... , person_fprint(myfile, ... ),其中在 C++ 中,“ FILE -argument”是自动“派生的” " 来自表达式。这个推导的更精确的等价物实际上更像这样:

FILE *fout = stdout;
...
fprintf(fout, "Hello World!\n");
person_fprint(fout, ...);
fprintf(fout, "\n");

I18N 18N

We reuse our Person definition: 我们重用我们的 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());
 
 

Judge yourself. 自己判断吧。

I find this less relevant as of today (2017).我发现这与今天(2017 年)不太相关。 Maybe just a gut feeling, but I18N is not something that is done on a daily basis by your average C or C++ programmer.也许只是一种直觉,但 I18N 并不是普通 C 或 C++ 程序员每天都在做的事情。 Plus, it's a pain in the a...natomy anyways.另外,无论如何,这在解剖学上是一种痛苦。

Performance性能

  1. Have you measured the actual significance of printf performance?你测量过 printf 性能的实际意义吗? Are your bottleneck applications seriously so lazy that the output of computation results is a bottleneck?您的瓶颈应用程序是否严重懒惰以致计算结果的输出成为瓶颈? Are you sure you need C++ at all?你确定你需要 C++ 吗?
  2. The dreaded performance penalty is to satisfy those of you who want to use a mix of printf and cout.可怕的性能损失是为了满足那些想要混合使用 printf 和 cout 的人。 It is a feature, not a bug!这是一个功能,而不是一个错误!

If you use iostreams consistently, you can如果您始终如一地使用 iostreams,则可以

std::ios::sync_with_stdio(false);

and reap equal runtime with a good compiler:并使用一个好的编译器获得相同的运行时间:

#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";
}

Results ( g++ -O3 synced-unsynced-printf.cc , ./a.out > /dev/null , cat RESULTS ):结果( 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

Judge ... yourself.判断......你自己。

No. You won't forbid me my printf.不,你不会禁止我使用我的 printf。

You can haz a typesafe, I18N friendly printf in C++11, thanks to variadic templates.由于可变参数模板,您可以在 C++11 中使用类型安全、I18N 友好的 printf。 And you will be able to have them very, very performant using user-defined literals, ie it will be possible to write a fully static incarnation.并且您将能够使用用户定义的文字使它们非常非常高效,即可以编写完全静态的化身。

I have a proof of concept .我有一个概念证明 Back then, support for C++11 was not as mature as it is now, but you get an idea.那时,对 C++11 的支持还没有现在那么成熟,但您应该有一个想法。

Temporal Adaptability时间适应性

// 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); ...

Later, your data grows so big you must do后来,您的数据变得如此之大,您必须这样做

// foo.h
...
    unsigned long long x;
...

It is an interesting exercise maintaining that and doing it bug-free.这是一个有趣的练习,可以保持它并且没有错误。 Especially when other, non-coupled projects use foo.h .特别是当其他非耦合项目使用foo.h 时

Other.其他。

  • Bug Potential : There's a lot of space to commit mistakes with printf, especially when you throw user input bases strings in the mix (think of your I18N team).潜在错误: printf 有很大的空间可以犯错误,尤其是当您将用户输入基础字符串混入时(想想您的 I18N 团队)。 You must take care to properly escape every such format string, you must be sure to pass the right arguments, etc. etc..您必须注意正确转义每个此类格式字符串,您必须确保传递正确的参数等。

  • IO-Streams make my binary bigger : If this is a more important issue than maintainability, code-quality, reuseability, then (after verifying the issue!) use printf. IO-Streams 使我的二进制文件更大:如果这是一个比可维护性、代码质量、可重用性更重要的问题,那么(在验证问题之后!)使用 printf。

Use boost::format.使用 boost::format。 You get type safety, std::string support, printf like interface, ability to use cout, and lots of other good stuff.您可以获得类型安全、std::string 支持、类似 printf 的接口、使用 cout 的能力以及许多其他好东西。 You won't go back.你不会回去。

No reason at all.完全没有理由。 I think it's just some strange ideology that drives people towards using only C++ libraries even though good old C libs are still valid.我认为这只是一些奇怪的意识形态,驱使人们只使用 C++ 库,即使好的旧 C 库仍然有效。 I'm a C++ guy and I use C functions a lot too.我是一个 C++ 人,我也经常使用 C 函数。 Never had any problems with them.他们从来没有遇到过任何问题。

Use printf.使用printf。 Do not use C++ streams.不要使用 C++ 流。 printf gives you much better control (such as float precision etc.). printf 为您提供更好的控制(例如浮点精度等)。 The code is also usually shorter and more readable.代码通常也更短且更具可读性。

Google C++ style guide agrees. 谷歌 C++ 风格指南同意。

Do not use streams, except where required by a logging interface.不要使用流,除非日志接口需要。 Use printf-like routines instead.改用类似 printf 的例程。

There are various pros and cons to using streams, but in this case, as in many other cases, consistency trumps the debate.使用流有各种优点和缺点,但在这种情况下,与许多其他情况一样,一致性胜过辩论。 Do not use streams in your code.不要在代码中使用流。

Streams are the canonical way.流是规范的方式。 Try making this code work with printf :尝试使此代码与printf

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

Good luck.祝你好运。

What I mean is, you can make operators to allow your types to be outputted to ostream 's, and without hassle use it just like any other type.我的意思是,你可以让操作符允许你的类型输出到ostream ,并且像任何其他类型一样轻松使用它。 printf doesn't fit the the generality of C++, or more specifically templates. printf不适合 C++ 的通用性,或者更具体地说是模板。

There's more than usability.不仅仅是可用性。 There's also consistency.还有一致性。 In all my projects, I have cout (and cerr and clog ) tee'd to also output to a file.在我所有的项目中,我都有 cout (以及cerrclog )tee 也可以输出到文件。 If you use printf , you skip all of that.如果您使用printf ,则跳过所有这些。 Additionally, consistency itself is a good thing;此外,一致性本身是一件好事; mixing cout and printf , while perfectly valid, is ugly.混合coutprintf ,虽然完全有效,但很难看。

If you have an object, and you want to make it output-able, the cleanest way to do this is overload operator<< for that class.如果您有一个对象,并且希望使其可输出,那么最简洁的方法是为该类重载operator<< How are you going to use printf then?那么你打算如何使用printf呢? You're going to end up with code jumbled with cout 's and printf 's.你最终会得到与coutprintf混淆的代码。

If you really want formatting, use Boost.Format while maintaining the stream interface.如果您真的想要格式化,请在保持流接口的同时使用 Boost.Format。 Consistency and formatting.一致性格式。

On the whole I agree (hate the << syntax especially if you need complex formatting)总的来说,我同意(讨厌 << 语法,特别是如果您需要复杂的格式)

But I should point out the safety aspects.但我应该指出安全方面。

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

Probably won't be noticed by the compiler but could crash your app.编译器可能不会注意到,但可能会使您的应用程序崩溃。

Use whatever fits your needs and preferences.使用适合您的需求和偏好的任何东西。 If you're comfortable with printf then by all means use it.如果您对 printf 感到满意,那么一定要使用它。 If you're happier with iostreams stick to 'em.如果您对 iostreams 感到满意,请坚持使用它们。 Mix and match as best fits your requirements.混合搭配最适合您的要求。 This is software, after all - there's better ways and worse ways, but seldom is there only ONE way.毕竟这是软件 - 有更好的方法和更坏的方法,但很少只有一种方法。

Share and enjoy.分享和享受。

You can get the best of both worlds with the {fmt} library which combines safety and extensibility of iostreams with usability and performance of (s)printf .您可以使用{fmt} 库获得两全其美,该库将 iostream 的安全性和可扩展性与(s)printf可用性和性能相结合。 Example:示例:

fmt::print("The answer is {}.", 42);

The library supports Python-like and printf format string syntax.该库支持类似 Python 和 printf 格式的字符串语法。

Disclaimer : I'm the author of this library.免责声明:我是这个库的作者。

I often "drop back" to using printf() , but more often snprintf() for easier formatted output.我经常“回退”使用printf() ,但更多时候使用snprintf()以便于格式化输出。 When programming in C++ I use this wrapper I wrote a while back, called like this (to use your example as above): cout << format("(%d,%d)\\n", x, y);在 C++ 中编程时,我使用了我不久前写的这个包装器,像这样调用(使用上面的示例): cout << format("(%d,%d)\\n", x, y);

Here's the header ( stdiomm.h ):这是标题( 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);

And the source ( stdiomm.cpp ):和源( 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, ...);

Update更新

After reading some of the other answers, I might have to make a switch to boost::format() myself!阅读其他一些答案后,我可能不得不自己切换到boost::format()

I do not like printf.我不喜欢 printf。 Its lack of type-safety makes it dangerous to use, plus the need to remember format specifiers is a pain.它缺乏类型安全性使其使用起来很危险,而且需要记住格式说明符是一种痛苦。 The templated operators that smartly do the right thing are much better.巧妙地做正确事情的模板化运算符要好得多。 So I always use the C++ streams in C++.所以我总是在 C++ 中使用 C++ 流。

Granted, many people prefer printf, for other reasons, enumerated elsewhere.诚然,出于其他原因,许多人更喜欢 printf ,原因在别处列举。

Even though the question is rather old, I want to add my two cents.尽管这个问题很老,但我想补充我的两分钱。

Printing user-created objects with printf()使用 printf() 打印用户创建的对象

This is rather simple if you think about it - you can stringify your type and sent the string to printf:如果您考虑一下,这相当简单 - 您可以将类型字符串化并将字符串发送到 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

A shame there wasn't (there is C++11 to_string()) standarized C++ interface to stringify objects...遗憾的是没有(有 C++11 to_string())标准化的 C++ 接口来字符串化对象......

printf() pitfall printf() 陷阱

A single flag - %n单个标志 - %n

The only one that is an output parameter - it expects pointer to int.唯一一个是输出参数 - 它需要指向 int 的指针。 It writes number of succesfully written characters to location pointed by this pointer.它将成功写入的字符数写入该指针指向的位置。 Skillful use of it can trigger overrun, which is security vulnerability (see printf() format string attack).熟练使用它会触发溢出,这是安全漏洞(参见 printf() 格式字符串攻击)。

(See the fmt library homepage ) (见fmt 库主页

In C++20, the fmt library is standardized for the formatting part:在 C++20 中, fmt库对格式化部分进行了标准化:

std::format("({},{})\n", x, y) // returns a std::string

You can avoid the dynamic allocation overhead by using format_to :您可以通过使用format_to来避免动态分配开销:

std::format_to(/* output iterator */, "({},{})\n", x, y);

This should be considered the canonical way of formatting because it combines the benefits of streams:这应该被视为格式化的规范方式,因为它结合了流的好处:

  • Safety : the library is fully type safe.安全性:库是完全类型安全的。 Automatic memory management prevents buffer overflow.自动内存管理可防止缓冲区溢出。 Errors in format strings are reported using exceptions or at compile time.使用异常或在编译时报告格式字符串中的错误。

  • Extensibility : overloading operator<< is easy, whereas extending printf is ... not so easy.可扩展性:重载operator<<很容易,而扩展printf则……不是那么容易。

and that of printf :printf

  • Ease to use : the % syntax is supported rather than the verbose manipulators.易于使用:支持%语法而不是冗长的操作符。 The {} syntax is also introduced to eliminate the specifiers.还引入了{}语法以消除说明符。

  • Performance : measurement has shown that the fmt library is so far the fastest output method in C++.性能:测量表明 fmt 库是迄今为止 C++ 中最快的输出方法。 Faster than printf and streams.printf和流更快。

I have read warnings saying that cout and cerr are unsafe for multithreading.我读过警告说 cout 和 cerr 对于多线程是不安全的。 If true, this is a good reason to avoid using them.如果为真,这是避免使用它们的一个很好的理由。 Note: I use GNU g++ with openMP.注意:我将 GNU g++ 与 openMP 一起使用。

I almost always use printf for temporary debugging statements.我几乎总是将 printf 用于临时调试语句。 For more permanent code, I prefer the 'c' streams as they are The C++ Way .对于更永久的代码,我更喜欢 'c' 流,因为它们是The C++ Way Although boost::format looks promising and might replace my stream usage (especially for complexly formatted output), probably nothing will replace printf for me for a long time.尽管 boost::format 看起来很有希望并且可能会取代我的流使用(特别是对于复杂格式的输出),但很长一段时间内可能没有什么能取代 printf 。

C++ streams are overrated, after all they're in fact just classes with an overloaded operator << . C++被高估了,毕竟它们实际上只是带有重载运算符<<
I've read many times that streams are the C++ way as printf is the C way , but they are both library features available in C++, so you should use what suits best.我已经多次阅读流是C++ 方式,因为 printf 是C 方式,但它们都是 C++ 中可用的库功能,因此您应该使用最适合的方式。
I mostly prefer printf, but I've also used streams, which provide cleaner code and prevent you from having to match % placeholders to arguments.我最喜欢 printf,但我也使用过流,它提供更清晰的代码并防止您必须将 % 占位符与参数匹配。

It depends on the situation.这取决于情况。 Nothing is perfect.没有什么是完美的。 I use both.我两个都用。 Streams are good for custom types as you can overload the >> operator in ostream.流适用于自定义类型,因为您可以在 ostream 中重载 >> 运算符。 But when it comes to spacing and etc it's better to use printf().但是当涉及到间距等问题时,最好使用 printf()。 stringstream and like are better than the C style strcat(). stringstream 和 like 比 C 风格的 strcat() 更好。 So use one that's appropriate for the situation.因此,请使用适合该情况的方法。

streams are preferred in cpp as they adhere to the object oriented paradigm of cpp, beside being type safe.流在 cpp 中是首选,因为它们遵守 cpp 的面向对象范例,除了类型安全。

printf , on the other hand is more of a functional approach. printf ,另一方面更像是一种功能方法。

only reason for not using printf in cpp code that i can think of is not being object oriented.我能想到的在 cpp 代码中不使用 printf 的唯一原因不是面向对象。

its more of a personal choice.它更多的是个人选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM