我通常使用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;
Run Code Online (Sandbox Code Playgroud)
我知道我可以使用格式化输出,cout但我已经知道如何使用printf.有什么理由我不应该使用这个printf陈述吗?
Nor*_*sey 68
我的学生,谁学习cin和cout第一,然后学习printf后,绝大多数喜欢printf(或者更通常fprintf).我自己已经发现该printf模型具有足够的可读性,我已将其移植到其他编程语言中.所以有奥利维尔·丹维,谁已经连上了类型安全.
如果你有一个能够对类型进行类型检查的编译器printf,我认为没有理由不在fprintfC++中使用和朋友.
免责声明:我是一个可怕的C++程序员.
R S*_*hko 48
如果您希望自己的计划,请远离iostreams.问题是,如果句子由多个片段组成,就像使用iostream一样,就不可能正确地本地化你的字符串.
除了消息片段的问题,您还有一个订购问题.考虑一个打印学生姓名和平均成绩点的报告:
std::cout << name << " has a GPA of " << gpa << std::endl;
Run Code Online (Sandbox Code Playgroud)
当您将其翻译成另一种语言时,另一种语言的语法可能需要您在名称前显示GPA.AFAIK,iostreams无法重新排序插值.
如果您想要两全其美(类型安全且能够使用i18n),请使用Boost.Format.
Seb*_*ach 21
任何printf非POD的尝试都会导致未定义的行为:
struct Foo {
virtual ~Foo() {}
operator float() const { return 0.f; }
};
printf ("%f", Foo());
std::string foo;
printf ("%s", foo);
Run Code Online (Sandbox Code Playgroud)
上面的printf调用产生了未定义的行为.您的编译器可能会向您发出警告,但标准不要求这些警告,并且对于仅在运行时已知的格式字符串不可能.
IO流:
std::cout << Foo();
std::string foo;
std::cout << foo;
Run Code Online (Sandbox Code Playgroud)
判断自己.
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;
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
要么:
// 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);
Run Code Online (Sandbox Code Playgroud)
注意如何在C中使用正确的调用参数/签名(例如person_fprint(stderr, ...,person_fprint(myfile, ...),在C++中,"- FILEargument"自动从表达式"派生".这个推导的更精确等价实际上更像是这样的:
FILE *fout = stdout;
...
fprintf(fout, "Hello World!\n");
person_fprint(fout, ...);
fprintf(fout, "\n");
Run Code Online (Sandbox Code Playgroud)
我们重用我们的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());
Run Code Online (Sandbox Code Playgroud)
判断自己.
我发现今天(2017年)的相关性较低.也许只是一种直觉,但I18N并不是普通C或C++程序员每天都能完成的事情.另外,无论如何,这都是一种痛苦.
如果你持续使用iostreams,你可以
std::ios::sync_with_stdio(false);
Run Code Online (Sandbox Code Playgroud)
并通过良好的编译器获得相同的运行时间:
#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";
}
Run Code Online (Sandbox Code Playgroud)
结果(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
Run Code Online (Sandbox Code Playgroud)
判断......你自己.
借助可变参数模板,您可以在C++ 11中使用类型安全的I18N友好printf.并且您将能够使用用户定义的文字非常,非常高效,即可以编写完全静态的化身.
我有一个概念证明.那时候,对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); ...
Run Code Online (Sandbox Code Playgroud)
之后,您的数据变得如此之大,您必须这样做
// foo.h
...
unsigned long long x;
...
Run Code Online (Sandbox Code Playgroud)
这是一个有趣的练习,可以保持这种无错误.特别是当其他非耦合项目使用foo.h时.
Bug潜力:使用printf会有很多空间来提交错误,特别是当你在混合中输入用户输入基础字符串时(想想你的I18N团队).你必须注意正确地转义每个这样的格式字符串,你必须确保传递正确的参数等等.
IO-Streams使我的二进制文件更大:如果这是一个比可维护性,代码质量,可重用性更重要的问题,那么(在验证问题之后!)使用printf.
没有理由.我认为只是一些奇怪的意识形态驱使人们只使用C++库,即使旧的C lib仍然有效.我是一个C++人,我也使用C函数.从来没有遇到任何问题.
流是规范的方式.尝试使用此代码printf:
template <typename T>
void output(const T& pX)
{
std::cout << pX << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
祝好运.
我的意思是,你可以让操作员允许输出你的类型ostream,而不用麻烦就像任何其他类型一样使用它.printf不符合C++的一般性,或更具体的模板.
不仅仅是可用性.还有一致性.在我的所有项目中,我都有cout(cerr和clog)tee'd也输出到文件.如果您使用printf,则跳过所有这些.此外,一致性本身是一件好事; 混合cout和printf,而完全有效的,是丑陋的.
如果你有一个对象,并且想要使它成为可输出的,那么最简洁的方法就是operator<<为该类重载.那你打算怎么用printf?你会用混乱的代码最终cout的和printf的.
如果您真的想要格式化,请在维护流接口的同时使用Boost.Format.一致性和格式.
使用printf.不要使用C++流.printf为您提供了更好的控制(例如浮动精度等).代码通常也更短,更易读.
谷歌C++风格指南同意.
除了日志记录界面要求之外,不要使用流.请改用类似printf的例程.
使用流有各种各样的利弊,但在这种情况下,与许多其他情况一样,一致性胜过辩论.不要在代码中使用流.
总的来说我同意(讨厌<<语法,特别是如果你需要复杂的格式)
但我应该指出安全方面.
printf("%x",2.0f)
printf("%x %x",2)
printf("%x",2,2)
Run Code Online (Sandbox Code Playgroud)
可能不会被编译器注意到,但可能会使您的应用程序崩溃.
您可以使用{fmt} 库实现两全其美,该库将 iostream 的安全性和可扩展性与(s)printf. 例子:
fmt::print("The answer is {}.", 42);
Run Code Online (Sandbox Code Playgroud)
该库支持类似 Python 和 printf 格式的字符串语法。
免责声明:我是这个库的作者。