Kon*_*ski 296
令我感到惊讶的是,这个问题中的每个人都声称这种情况std::cout要好得多printf,即使这个问题只是要求差异.现在,有一个区别 - std::cout是C++,而且printf是C(但是,你可以在C++中使用它,就像C中几乎所有其他东西一样).现在,我会在这里说实话; 双方printf并std::cout有自己的优势.
std::cout是可扩展的.我知道人们会说这printf也是可扩展的,但是C标准中没有提到这种扩展(所以你必须使用非标准功能 - 但是甚至不存在常见的非标准功能),并且这样的扩展是一个字母(因此很容易与已存在的格式冲突).
与完全不同printf,std::cout完全取决于运算符重载,因此自定义格式没有问题 - 您所做的就是定义一个子程序std::ostream作为第一个参数,您的类型作为第二个参数.因此,没有命名空间问题 - 只要你有一个类(不限于一个字符),你就可std::ostream以为它工作重载.
但是,我怀疑很多人会想要扩展ostream(说实话,我很少看到这样的扩展,即使它们很容易制作).但是,如果你需要它就在这里.
因为它可以很容易地发现,无论是printf和std::cout使用不同的语法.printf使用模式字符串和可变长度参数列表使用标准函数语法.实际上,printf这就是为什么C有它们的原因 - printf格式太复杂,没有它们就无法使用.但是,std::cout使用不同的API - operator <<返回自身的API.
通常,这意味着C版本会更短,但在大多数情况下它并不重要.打印多个参数时,差异很明显.如果您必须编写类似的内容Error 2: File not found.,假设错误编号,并且其描述是占位符,则代码将如下所示.两个示例的工作方式相同(好吧,std::endl实际上是刷新缓冲区).
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
Run Code Online (Sandbox Code Playgroud)
虽然这看起来并不太疯狂(只是它的两倍),但实际设置参数格式时,事情变得更加疯狂,而不仅仅是打印它们.例如,打印类似的东西0x0424只是疯了.这是由std::cout混合状态和实际值引起的.我从来没有看到类似于std::setfill某种类型的语言(当然除了C++之外).printf明确区分参数和实际类型.我真的更愿意保持printf它的版本(即使它看起来有点神秘)与iostream它的版本相比(因为它包含太多的噪音).
printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;
Run Code Online (Sandbox Code Playgroud)
这就是printf谎言的真正优势所在.该printf格式字符串是很好...字符串.与operator <<虐待相比,这使得翻译变得非常容易iostream.假设gettext()函数转换,并且您想要显示Error 2: File not found.,用于转换先前显示的格式字符串的代码将如下所示:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Run Code Online (Sandbox Code Playgroud)
现在,让我们假设我们翻译为Fictionish,其中错误编号在描述之后.翻译后的字符串看起来像%2$s oru %1$d.\n.现在,如何在C++中完成它?好吧,我不知道.为了翻译的目的,我想你可以伪造iostream哪些构造printf你可以传递给gettext什么东西.当然,$不是C标准,但它很常见,在我看来它是安全的.
C有很多整数类型,C++也是如此.std::cout为你处理所有类型,同时printf需要特定的语法取决于整数类型(有非整数类型,但你将在实践中使用的唯一非整数类型printf是const char *(C字符串,可以使用to_c方法获得std::string)).例如,要打印size_t,您需要使用%zd,同时int64_t需要使用%"PRId64".这些表格可在http://en.cppreference.com/w/cpp/io/c/fprintf和http://en.cppreference.com/w/cpp/types/integer上找到.
\0因为printf使用C字符串而不是C++字符串,所以如果没有特定的技巧,它就无法打印NUL字节.在某些情况下,它可以使用%c与'\0'作为参数,尽管这显然是一个黑客.
更新:事实证明它iostream太慢了,它通常比你的硬盘驱动器慢(如果你将程序重定向到文件).stdio如果需要输出大量数据,禁用同步可能会有所帮助.如果性能是一个真正的问题(而不是写几行到STDOUT),只需使用printf.
每个人都认为他们关心表现,但没有人愿意测量它.我的答案是,无论你是否使用printf或I/O都是瓶颈iostream.我认为从快速查看汇编(使用编译器选项使用clang 编译)printf 可能会更快-O3.假设我的错误示例,printf示例执行的调用比cout示例少.这是int main有printf:
main: @ @main
@ BB#0:
push {lr}
ldr r0, .LCPI0_0
ldr r2, .LCPI0_1
mov r1, #2
bl printf
mov r0, #0
pop {lr}
mov pc, lr
.align 2
@ BB#1:
Run Code Online (Sandbox Code Playgroud)
您可以很容易地注意到两个字符串和2(数字)被作为printf参数推送.就是这样; 没有别的.为了比较,这被iostream编译为汇编.不,没有内联; 每一次operator <<调用都意味着使用另一组参数进行另一次调用.
main: @ @main
@ BB#0:
push {r4, r5, lr}
ldr r4, .LCPI0_0
ldr r1, .LCPI0_1
mov r2, #6
mov r3, #0
mov r0, r4
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
mov r0, r4
mov r1, #2
bl _ZNSolsEi
ldr r1, .LCPI0_2
mov r2, #2
mov r3, #0
mov r4, r0
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
ldr r1, .LCPI0_3
mov r0, r4
mov r2, #14
mov r3, #0
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
ldr r1, .LCPI0_4
mov r0, r4
mov r2, #1
mov r3, #0
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
ldr r0, [r4]
sub r0, r0, #24
ldr r0, [r0]
add r0, r0, r4
ldr r5, [r0, #240]
cmp r5, #0
beq .LBB0_5
@ BB#1: @ %_ZSt13__check_facetISt5ctypeIcEERKT_PS3_.exit
ldrb r0, [r5, #28]
cmp r0, #0
beq .LBB0_3
@ BB#2:
ldrb r0, [r5, #39]
b .LBB0_4
.LBB0_3:
mov r0, r5
bl _ZNKSt5ctypeIcE13_M_widen_initEv
ldr r0, [r5]
mov r1, #10
ldr r2, [r0, #24]
mov r0, r5
mov lr, pc
mov pc, r2
.LBB0_4: @ %_ZNKSt5ctypeIcE5widenEc.exit
lsl r0, r0, #24
asr r1, r0, #24
mov r0, r4
bl _ZNSo3putEc
bl _ZNSo5flushEv
mov r0, #0
pop {r4, r5, lr}
mov pc, lr
.LBB0_5:
bl _ZSt16__throw_bad_castv
.align 2
@ BB#6:
Run Code Online (Sandbox Code Playgroud)
但是,说实话,这意味着什么,因为I/O无论如何都是瓶颈.我只是想表明它iostream不是更快,因为它是"类型安全".大多数C实现printf使用计算goto 实现格式,因此printf即使没有编译器意识到它也是如此printf(不是它们不是 - 某些编译器printf在某些情况下可以优化- 常量字符串结尾\n通常被优化为puts) .
我不知道你为什么要继承ostream,但我不在乎.它也有可能FILE.
class MyFile : public FILE {}
Run Code Online (Sandbox Code Playgroud)
确实,可变长度参数列表没有安全性,但这并不重要,因为printf如果启用警告,流行的C编译器可以检测格式字符串的问题.事实上,Clang可以在不启用警告的情况下做到这一点.
$ cat safety.c
#include <stdio.h>
int main(void) {
printf("String: %s\n", 42);
return 0;
}
$ clang safety.c
safety.c:4:28: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
printf("String: %s\n", 42);
~~ ^~
%d
1 warning generated.
$ gcc -Wall safety.c
safety.c: In function ‘main’:
safety.c:4:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("String: %s\n", 42);
^
Run Code Online (Sandbox Code Playgroud)
Mik*_*age 198
从C++ FAQ:
[15.1]为什么要使用
<iostream>而不是传统的<cstdio>?提高类型安全性,减少错误,允许可扩展性并提供可继承性.
printf()可以说它没有被破坏,scanf()尽管容易出错,但它可能是适合居住的,但两者在C++ I/O方面都有限.C++ I/O(使用<<和>>)相对于C(使用printf()和scanf()):
- 更类型安全:有了
<iostream>I/O的对象类型,编译器静态地知道.相反,<cstdio>使用"%"字段动态地计算出类型.- 不易出错:有了
<iostream>,没有多余的"%"令牌必须与I/O的实际对象保持一致.删除冗余会消除一类错误.- 可扩展:C++
<iostream>机制允许新的用户定义类型进行I/O而不破坏现有代码.想象一下,如果每个人都同时添加新的不兼容的"%"字段,那么混乱printf()和scanf()?!- 可继承:C++
<iostream>机制是由真实的类构建的,例如std::ostream和std::istream.与<cstdio>s 不同FILE*,这些是真正的类,因此是可继承的.这意味着你可以拥有其他用户定义的东西,它们看起来像溪流一样,但它可以做你想要的任何奇怪和奇妙的事情.您可以自动使用由您甚至不知道的用户编写的数以万计的I/O代码行,并且他们不需要了解您的"扩展流"类.
另一方面,printf明显更快,这可能有理由使用它优先cout于非常具体和有限的情况.始终首先介绍.(例如,参见http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)
Tho*_*mas 41
人们经常声称printf速度要快得多.这在很大程度上是一个神话.我刚测试了它,结果如下:
cout with only endl 1461.310252 ms
cout with only '\n' 343.080217 ms
printf with only '\n' 90.295948 ms
cout with string constant and endl 1892.975381 ms
cout with string constant and '\n' 416.123446 ms
printf with string constant and '\n' 472.073070 ms
cout with some stuff and endl 3496.489748 ms
cout with some stuff and '\n' 2638.272046 ms
printf with some stuff and '\n' 2520.318314 ms
Run Code Online (Sandbox Code Playgroud)
结论:如果您只想换行,请使用printf; 否则,cout几乎一样快,甚至更快.更多细节可以在我的博客上找到.
要清楚,我并不想说iostreams总是好于printf; 我只是想说你应该根据真实数据作出明智的决定,而不是基于一些常见的误导性假设的疯狂猜测.
更新:这是我用于测试的完整代码.编译时g++没有任何其他选项(除了-lrt时间).
#include <stdio.h>
#include <iostream>
#include <ctime>
class TimedSection {
char const *d_name;
timespec d_start;
public:
TimedSection(char const *name) :
d_name(name)
{
clock_gettime(CLOCK_REALTIME, &d_start);
}
~TimedSection() {
timespec end;
clock_gettime(CLOCK_REALTIME, &end);
double duration = 1e3 * (end.tv_sec - d_start.tv_sec) +
1e-6 * (end.tv_nsec - d_start.tv_nsec);
std::cerr << d_name << '\t' << std::fixed << duration << " ms\n";
}
};
int main() {
const int iters = 10000000;
char const *text = "01234567890123456789";
{
TimedSection s("cout with only endl");
for (int i = 0; i < iters; ++i)
std::cout << std::endl;
}
{
TimedSection s("cout with only '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << '\n';
}
{
TimedSection s("printf with only '\\n'");
for (int i = 0; i < iters; ++i)
printf("\n");
}
{
TimedSection s("cout with string constant and endl");
for (int i = 0; i < iters; ++i)
std::cout << "01234567890123456789" << std::endl;
}
{
TimedSection s("cout with string constant and '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << "01234567890123456789\n";
}
{
TimedSection s("printf with string constant and '\\n'");
for (int i = 0; i < iters; ++i)
printf("01234567890123456789\n");
}
{
TimedSection s("cout with some stuff and endl");
for (int i = 0; i < iters; ++i)
std::cout << text << "01234567890123456789" << i << std::endl;
}
{
TimedSection s("cout with some stuff and '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << text << "01234567890123456789" << i << '\n';
}
{
TimedSection s("printf with some stuff and '\\n'");
for (int i = 0; i < iters; ++i)
printf("%s01234567890123456789%i\n", text, i);
}
}
Run Code Online (Sandbox Code Playgroud)
mis*_*153 12
对我来说,真正的差异让我选择'cout'而不是'printf'是:
1)<<运算符可以为我的类重载.
2)cout的输出流可以很容易地更改为文件:(:copy paste :)
#include <iostream>
#include <fstream>
using namespace std;
int main ()
{
cout << "This is sent to prompt" << endl;
ofstream file;
file.open ("test.txt");
streambuf* sbuf = cout.rdbuf();
cout.rdbuf(file.rdbuf());
cout << "This is sent to file" << endl;
cout.rdbuf(sbuf);
cout << "This is also sent to prompt" << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
3)我发现cout更具可读性,特别是当我们有很多参数时.
一个问题用cout的是格式选项.格式化数据(精度,对齐等)printf更容易.
小智 7
我不是程序员,但我一直是人因工程师。我觉得编程语言应该易于学习、理解和使用,这就要求它具有简单且一致的语言结构。尽管所有语言都是符号性的,因此其核心是任意的,但有一些约定,遵循这些约定可以使语言更容易学习和使用。
C++ 和其他语言中有大量函数被编写为函数(参数),这种语法最初用于前计算机时代数学中的函数关系。printf()遵循此语法,如果 C++ 的编写者想要创建任何逻辑上不同的方法来读取和写入文件,他们可以简单地使用类似的语法创建不同的函数。
在Python 中,我们当然可以使用相当标准的object.method语法进行打印,即variablename.print,因为变量是对象,但在C++ 中它们不是。
我不喜欢 cout 语法,因为 << 运算符不遵循任何规则。它是一个方法或函数,即它接受一个参数并对其执行某些操作。然而,它的编写方式就好像它是一个数学比较运算符。从人为因素的角度来看,这是一个糟糕的方法。
对于原语,您使用哪一种可能并不重要。我说当你想输出复杂的对象时它会有用处。
例如,如果你有一个班级,
#include <iostream>
#include <cstdlib>
using namespace std;
class Something
{
public:
Something(int x, int y, int z) : a(x), b(y), c(z) { }
int a;
int b;
int c;
friend ostream& operator<<(ostream&, const Something&);
};
ostream& operator<<(ostream& o, const Something& s)
{
o << s.a << ", " << s.b << ", " << s.c;
return o;
}
int main(void)
{
Something s(3, 2, 1);
// output with printf
printf("%i, %i, %i\n", s.a, s.b, s.c);
// output with cout
cout << s << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在上面的内容可能看起来不是那么好,但是假设您必须在代码的多个位置输出它。不仅如此,假设您添加了一个字段“int d”。使用 cout,您只需更改一次。但是,使用 printf 时,您可能需要在很多地方更改它,不仅如此,您还必须提醒自己要输出哪些地方。
话虽如此,使用 cout,您可以减少大量用于维护代码的时间,不仅如此,如果您在新应用程序中重新使用对象“Something”,您就不必担心输出。
这里没有另外提到的两点我觉得很重要:
1)cout如果您尚未使用STL,则需要携带大量行李.它为您的目标文件增加了两倍的代码printf.这也是如此string,这是我倾向于使用自己的字符串库的主要原因.
2)cout使用重载<<运算符,我觉得很不幸.如果您还将<<操作符用于其预期目的(向左移动),则会增加混淆.我个人不喜欢将操作员重载到与其预期用途相关的目的.
底线:如果我已经在使用STL,我将使用cout(和string).否则,我倾向于避免它.
| 归档时间: |
|
| 查看次数: |
312779 次 |
| 最近记录: |