C++中的'printf'和'cout'有什么区别?

516
16个回答

3
cout<< "Hello";
printf("%s", "Hello"); 

两者都用于打印值,它们的语法完全不同。C++中两种方式都有,而C语言只有printf。


20
“什么?你搞混了什么?” - xtofl
1
修复了问题。评分为-1,因为它需要修复,而且答案还有很多不足之处。 - Yacoby
3
函数名称被颠倒了:cout与printf的语法混用了,printf则使用了cout的语法。这种情况本不应被接受! - Mahmoud Al-Qudsi
2
cout的主要缺点是它使用operator<<,这是冗长且丑陋的,可以说是滥用运算符。 :) - jalf
10
尽管这肯定不是最好的答案,但我不明白为什么Scatman因为他的回答被选为最佳答案而被惩罚。在我看来,xbit的回答要糟糕得多,但只有-1票。我不是说xbit应该再被投反对票,但我认为像这样仅仅因为OP的错误而对Scatman进行贬低评价是不公平的... - Jesse
显示剩余5条评论

2
TL;DR: 在信任随机在线评论之前,包括此评论在内,请始终进行自己的研究,涉及生成的机器代码大小、性能、可读性和编码时间。
我不是专家。我只是碰巧听到两个同事谈论我们应该避免在嵌入式系统中使用C++,因为存在性能问题。有趣的是,我基于一个真实项目任务进行了基准测试。
在这个任务中,我们需要将一些配置写入RAM。类似于:
coffee=hot sugar=none milk=breast mac=AA:BB:CC:DD:EE:FF
这是我的基准测试程序(是的,我知道OP问的是printf()而不是fprintf()。尽量捕捉本质,顺便说一句,OP的链接指向fprintf())。
C程序:
char coffee[10], sugar[10], milk[10];
unsigned char mac[6];

/* Initialize those things here. */

FILE * f = fopen("a.txt", "wt");

fprintf(f, "coffee=%s\nsugar=%s\nmilk=%s\nmac=%02X:%02X:%02X:%02X:%02X:%02X\n", coffee, sugar, milk, mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]);

fclose(f);

C++程序:

//Everything else is identical except:

std::ofstream f("a.txt", std::ios::out);

f << "coffee=" << coffee << "\n";
f << "sugar=" << sugar << "\n";
f << "milk=" << milk << "\n";
f << "mac=" << (int)mac[0] << ":"
    << (int)mac[1] << ":"
    << (int)mac[2] << ":"
    << (int)mac[3] << ":"
    << (int)mac[4] << ":"
    << (int)mac[5] << endl;
f.close();

我在循环100,000次之前尽力优化了它们。以下是结果:
C程序:
real    0m 8.01s
user    0m 2.37s
sys     0m 5.58s

C++程序:

real    0m 6.07s
user    0m 3.18s
sys     0m 2.84s

对象文件大小:
C   - 2,092 bytes
C++ - 3,272 bytes

结论:在我的特定平台、使用一个非常具体的处理器,运行一个非常具体的Linux内核版本,在编译了一个非常具体版本的GCC的程序中,为了完成一个非常具体的任务,我认为C++方法更适合,因为它运行速度更快,可读性更好。另一方面,C语言提供小的占用空间,但在我看来,这意味着几乎没有什么影响,因为程序大小不是我们关心的问题。

记住,你的情况可能有所不同。


3
我不同意C++在这个例子中更易读的观点,因为你的例子将多行内容压缩成单个printf调用。与你编写的C++代码相比,这显然不太易读,并且在C语言中很少这样做,因为这会让代码难以阅读和维护。一个公平的比较应该将C代码分散到不同的printf中,每行代码对应一个printf。 - Alcamtar
1
@maharvey67 你说的没错。然而,我在C语言中提供的示例是考虑到性能的。将fprintf打包成一个调用比C++等价物慢了两秒钟。如果我要让C代码可读性更高,那么它可能会更慢。免责声明:这是一年前的事情,我记得我尽力优化了C和C++代码。我没有证据表明分开调用fprintf比单个调用更快,但我这样做的原因可能表明它并不是更快的方法。 - Wesley
我会说C++方法更合适,因为它运行速度明显更快,提供了更好的可读性。但事实并非如此。C++版本的用户时间为3.18秒,而C版本的用户时间只有2.37秒。这意味着C二进制在执行其用户空间工作时更加高效,整个性能差异完全是由于C++版本的系统时间远低于C版本。由于您没有指定已编译的二进制文件实际上如何执行IO,所以无法确定C++二进制文件为什么使用了更少的系统时间。 - Andrew Henle
可能是因为C++输出的缓冲方式不同而导致这种情况。也可能是由于C调用中的锁定问题。那么,如何使用“O_DIRECT”对sprintf()write()进行基准测试呢? - Andrew Henle

2
当然,你可以写得更好一些以便于维护:

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

        void print() const { printf("%i, %i, %i\n", a, b, c); }
};

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
    s.print(); // Simple as well, isn't it?

    // Output with cout
    cout << s << endl;

    return 0;
}

以下是关于cout和printf的扩展测试,包括一个对双精度浮点数的测试。如果有人想进行更多的测试(使用Visual Studio 2008,可执行文件的发布版本):

#include <stdio.h>
#include <iostream>
#include <ctime>

class TimedSection {
    char const *d_name;
    //timespec d_start;
    clock_t d_start;

    public:
        TimedSection(char const *name) :
            d_name(name)
        {
            //clock_gettime(CLOCK_REALTIME, &d_start);
            d_start = clock();
        }
        ~TimedSection() {
            clock_t end;
            //clock_gettime(CLOCK_REALTIME, &end);
            end = clock();
            double duration = /*1e3 * (end.tv_sec - d_start.tv_sec) +
                              1e-6 * (end.tv_nsec - d_start.tv_nsec);
                              */
                              (double) (end - d_start) / CLOCKS_PER_SEC;

            std::cerr << d_name << '\t' << std::fixed << duration * 1000.0 << " ms\n";
        }
};


int main() {
    const int iters = 1000000;
    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);
    }
    {
        TimedSection s("cout with formatted double (width & precision once)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;
        std::cout.width(8);
        for (int i = 0; i < iters; ++i)
            std::cout << text << 8.315 << i << '\n';
    }
    {
        TimedSection s("cout with formatted double (width & precision on each call)");
        std::cout << std::fixed << std::scientific << std::right << std::showpoint;

        for (int i = 0; i < iters; ++i)
            { std::cout.width(8);
              std::cout.precision(3);
              std::cout << text << 8.315 << i << '\n';
            }
    }
    {
        TimedSection s("printf with formatted double");
        for (int i = 0; i < iters; ++i)
            printf("%8.3f%i\n", 8.315, i);
    }
}

结果是:
cout with only endl    6453.000000 ms
cout with only '\n'    125.000000 ms
printf with only '\n'    156.000000 ms
cout with string constant and endl    6937.000000 ms
cout with string constant and '\n'    1391.000000 ms
printf with string constant and '\n'    3391.000000 ms
cout with some stuff and endl    9672.000000 ms
cout with some stuff and '\n'    7296.000000 ms
printf with some stuff and '\n'    12235.000000 ms
cout with formatted double (width & precision once)    7906.000000 ms
cout with formatted double (width & precision on each call)    9141.000000 ms
printf with formatted double    3312.000000 ms

哇,为什么endl'\n'低效那么多? - Nicholas Hamilton
1
我认为这是因为 endl 刷新了缓冲区,而 \n 没有刷新,尽管我不确定这是否是明确的原因。 - Caleb Xu
这不是对问题的回答,更像是对丹尼尔托马斯的回答。 - Fabio says Reinstate Monica
@CalebXu,没错,这就是唯一的原因。如果你查看std::endl的定义template <class _Elem, class _Traits> basic_ostream<_Elem, _Traits> &__CLRCALL_OR_CDECL endl(basic_ostream<_Elem, _Traits> &_Ostr) { // insert newline and flush stream _Ostr.put(_Ostr.widen('\n')); _Ostr.flush(); return _Ostr; }它会插入一个换行符并刷新流。 - avighnac

2
更多的区别: "printf" 返回整数值(等于打印的字符数),而 "cout" 不返回任何内容。
另外, cout << "y = " << 7; 不是原子的。 printf("%s = %d", "y", 7); 是原子的。
cout 执行类型检查,printf 不执行。
没有 "% d" 的 iostream 相当之处。

3
cout不返回任何内容,因为它是一个对象而不是函数。 operator<<会返回一些内容(通常是其左操作数,但如果存在错误则返回false值)。那么,“原子”的意义是什么意思?在printf调用中呢? - Keith Thompson
10
就像一枚原子弹。printf("%s\n",7); - artless noise
@artlessnoise 等等,为什么会出现分段错误?%s是什么? - Abhinav Gauniyal
2
这就是“原子弹”语句的意义所在。printf%s参数必须具有指向空终止字符串的有效指针。内存范围“7”(一个指针)通常无效;分段错误可能会很幸运。在某些系统上,“7”可能会向控制台打印大量垃圾,您需要花一天时间查看它,然后程序才会停止。换句话说,这是关于printf的一个坏事情。静态分析工具可以捕获许多这些问题。 - artless noise
1
虽然从技术上讲,printf不会进行类型检查,但我从来没有使用过一个编译器不会在使用printf时警告我类型错误的情况... - CoffeeTableEspresso

1
我想说printf缺乏可扩展性并不完全正确:
在C语言中是正确的。但在C++语言中,存在真正的类。
在C++语言中,可以重载类型转换运算符,因此可以重载char *运算符,并像这样使用printf:
Foo bar;
...;
printf("%s",bar);

如果Foo重载了好的运算符,或者你编写了一个好的方法,那么可能是可以的。简而言之,对于我来说,printfcout一样可扩展。

我能看到C++流(不仅仅是cout)的技术优势有:

  • 类型安全。(顺便说一下,如果我想打印单个'\n',我会使用putchar('\n'),不会用核弹炸死一只昆虫。)

  • 更容易学习。(没有“复杂”的参数要学习,只需使用<<>>操作符即可)

  • 原生支持std::string(对于printf,只有std::string::c_str(),但对于scanf呢?)

对于printf,我认为:

  • 更容易或至少更短(在写入字符方面)的复杂格式。对我来说更易读(我想这是品味问题)。

  • 更好地控制函数的作用(返回写入的字符数,有%n格式化程序:“未打印任何内容。参数必须是指向已签名整数的指针,其中存储了迄今为止写入的字符数。”(来自printf - C++ Reference

  • 更好的调试可能性。出于同样的原因作为最后一个参数。

我的个人偏好是printf(和scanf)函数,主要是因为我喜欢短行,而且我认为在打印文本时类型问题并不难避免。 我唯一遗憾的是C风格的函数不支持std::string。我们必须通过char*才能将其传递给printf(使用std::string::c_str()进行读取,但如何编写?)


3
编译器无法获取可变参数函数的类型信息,因此它不会转换实际参数(除了 默认参数提升,如标准整数提升)。请查看5.2.2p7。用户定义的转换为 char* 不会被使用。 - Ben Voigt
1
即使这个方法可行,它也不是 sprintf 可扩展性的例子,只是一个聪明的 hack,为了让 sprintf 符合预期,而忽略了一些严重的问题,比如 char* 存在的位置和存活时间,以及用户定义的隐式转换的危险。 - Marcelo Cantos

-5

printf 是一个函数,而 cout 是一个变量。


10
我进行了回滚操作,因为尽管答案本身可能是错误的,但它仍然是一个真正的答案。如果你(正确地)认为答案是错误的,你有两个选项:1)添加评论或2)添加新的答案(或者两者都可以)。不要更改他人的答案,使其完全不同于作者的原意。 - Mark
1
printf是一个函数,但printf()是一个函数调用 =) - vp_arth
1
cout 是一个对象,而不是一个变量。 - Lin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接