在C++中,我为不吃的东西付钱吗?

Sah*_*her 168 c c++

让我们考虑C和C++中的以下hello world示例:

main.c

#include <stdio.h>

int main()
{
    printf("Hello world\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

main.cpp

#include <iostream>

int main()
{
    std::cout<<"Hello world"<<std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我在godbolt中将它们编译为汇编时,C代码的大小只有9行(gcc -O3):

.LC0:
        .string "Hello world"
main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        xor     eax, eax
        add     rsp, 8
        ret
Run Code Online (Sandbox Code Playgroud)

但是C++代码的大小是22行(g++ -O3):

.LC0:
        .string "Hello world"
main:
        sub     rsp, 8
        mov     edx, 11
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
        xor     eax, eax
        add     rsp, 8
        ret
_GLOBAL__sub_I_main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        add     rsp, 8
        jmp     __cxa_atexit
Run Code Online (Sandbox Code Playgroud)

......哪个更大.

众所周知,在C++中,你需要为你所吃的东西买单.所以,在这种情况下,我付的是什么?

Vit*_*meo 208

所以,在这种情况下,我付的是什么?

std::cout比...更强大,更复杂printf.它支持区域设置,有状态格式标记等内容.

如果您不需要,请使用std::printfstd::puts- 它们可用<cstdio>.


众所周知,在C++中,你需要为你所吃的东西买单.

我还要说清楚C++ != C++标准库.标准库应该是通用的并且"足够快",但它通常比您需要的专门实现慢.

另一方面,C++语言努力使编写代码成为可能,而无需支付不必要的额外隐藏成本(例如,选择加入virtual,不进行垃圾回收).

  • @Craig OTOH标准库的许多部分通常比通常生产的更快更正确. (7认同)
  • @PeterA.Schneider OTOH,当 STL 版本慢 20-30 倍时,自己滚动是件好事。在此处查看我的答案:https://codereview.stackexchange.com/questions/191747/c-interview-problem-finding-an-island-in-a-2d-grid/191921#191921 其中,其他人也建议 [至少部分] 推出你自己的。 (3认同)
  • +1表示_标准库应该是通用的,并且"足够快",但它通常比你需要的专门实现要慢._许多人似乎轻率地使用STL组件而不考虑性能影响与滚动你自己. (2认同)

psc*_*ill 173

您不是在比较C和C++.您比较printfstd::cout,其能不同的东西(的语言环境,状态格式等).

尝试使用以下代码进行比较.Godbolt为两个文件生成相同的程序集(使用gcc 8.2,-O3进行测试).

main.c中:

#include <stdio.h>

int main()
{
    int arr[6] = {1, 2, 3, 4, 5, 6};
    for (int i = 0; i < 6; ++i)
    {
        printf("%d\n", arr[i]);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

main.cpp中:

#include <array>
#include <cstdio>

int main()
{
    std::array<int, 6> arr {1, 2, 3, 4, 5, 6};
    for (auto x : arr)
    {
        std::printf("%d\n", x);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • [这是一个展示这个例子的神禁链接](https://godbolt.org/z/eFduHh) (14认同)

Kon*_*lph 134

您的列表确实在比较苹果和橙子,但不是因为大多数其他答案暗示的原因.

我们来看看你的代码实际上做了什么:

C:

  • 打印一个字符串, "Hello world\n"

C++:

  • 将字符串"Hello world"流入std::cout
  • std::endl操纵器流入std::cout

显然你的C++代码做了两倍的工作.为了公平比较,我们应该结合这个:

#include <iostream>

int main()
{
    std::cout<<"Hello world\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

...突然你的汇编代码main看起来非常类似于C:

main:
        sub     rsp, 8
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        xor     eax, eax
        add     rsp, 8
        ret
Run Code Online (Sandbox Code Playgroud)

实际上,我们可以逐行比较C和C++代码,并且差异很小:

sub     rsp, 8                      sub     rsp, 8
mov     edi, OFFSET FLAT:.LC0   |   mov     esi, OFFSET FLAT:.LC0
                                >   mov     edi, OFFSET FLAT:_ZSt4cout
call    puts                    |   call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
xor     eax, eax                    xor     eax, eax
add     rsp, 8                      add     rsp, 8
ret                                 ret
Run Code Online (Sandbox Code Playgroud)

唯一真正的区别是在C++中我们调用operator <<两个参数(std::cout和字符串).我们可以通过使用更接近的C eqivalent:来删除那个微小的差异fprintf,它也有一个指定流的第一个参数.

这留下了汇编代码_GLOBAL__sub_I_main,它是为C++而不是C生成的.这是在这个汇编列表中可见的唯一真正的开销(当然,这两种语言的开销更多,不可见).此代码在C++程序的开头执行一些C++标准库函数的一次性设置.

但是,正如其他答案中所解释的那样,这两个程序之间的相关差异将不会在main函数的汇编输出中找到,因为所有繁重的工作都发生在幕后.

  • 顺便提一下,C runtime*也需要设置,这发生在一个名为`_start`的函数中,但它的代码是C运行库的一部分.无论如何,C和C++都会发生这种情况. (20认同)
  • @KonradRudolph:实际上,`printf`不需要在这里刷新流.事实上,在一个常见的用例(输出重定向到文件)中,您通常会发现`printf`语句*不会刷新.只有当输出是行缓冲或无缓冲时,`printf`才会触发刷新. (6认同)
  • @Deduplicator:实际上,默认情况下,iostream库不执行`std :: cout`的*any*缓冲,而是将I/O传递给stdio实现(它使用自己的缓冲机制).特别是,当连接到(已知的)交互式终端时,默认情况下,在写入`std :: cout`时,您永远不会看到完全缓冲的输出.如果希望iostream库为`std :: cout`使用自己的缓冲机制,则必须显式禁用与stdio的同步. (2认同)
  • @PeterCordes:对,你不能使用未刷新的输出缓冲区来阻止,但是你可以遇到程序已经接受你的输入并在没有显示预期输出的情况下进行游戏的惊喜.我知道这是因为我有机会调试"帮助,我的程序在输入期间挂起但我无法弄清楚为什么!" 这给了另一位开发者适合了几天. (2认同)
  • @PeterCordes:我所做的论证是"写出你的意思" - 当你的意思是输出最终可用时,换行是合适的,当你意味着输出立即可用时,endl是合适的. (2认同)
  • @PeterCordes:是的; 有一个`std :: flush`,其使用方式与`std :: endl`相同,或者你也可以`std :: cout.flush()`. (2认同)

Ara*_*ash 59

你要付出的代价就是调用一个沉重的库(不像打印到控制台那么重).您初始化一个ostream对象.有一些隐藏的存储空间.然后,你调用std::endl哪个不是同义词\n.该iostream库可帮助您调整许多设置并将负担放在处理器而不是程序员身上.这就是你要付出的代价.

我们来看看代码:

.LC0:
        .string "Hello world"
main:
Run Code Online (Sandbox Code Playgroud)

初始化ostream对象+ cout

    sub     rsp, 8
    mov     edx, 11
    mov     esi, OFFSET FLAT:.LC0
    mov     edi, OFFSET FLAT:_ZSt4cout
    call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
Run Code Online (Sandbox Code Playgroud)

cout再次呼叫打印新线路并刷新

    mov     edi, OFFSET FLAT:_ZSt4cout
    call    std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
    xor     eax, eax
    add     rsp, 8
    ret
Run Code Online (Sandbox Code Playgroud)

静态存储初始化:

_GLOBAL__sub_I_main:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        add     rsp, 8
        jmp     __cxa_atexit
Run Code Online (Sandbox Code Playgroud)

此外,必须区分语言和库.

顺便说一下,这只是故事的一部分.你不知道你正在调用的函数中写了什么.

  • 另外需要注意的是,彻底的测试将显示在C++程序前加上"ios_base :: sync_with_stdio(false);" 和"cin.tie(NULL);" 将使cout比printf更快(Printf具有格式字符串开销).第一个消除了确保`cout; printf的; cout`按顺序写入(因为它们有自己的缓冲区).第二个将同步`cout`和`cin`,导致`cout; cin`可能首先要求用户提供信息.刷新将强制它仅在您真正需要时才同步. (4认同)

el.*_*ado 53

众所周知,在C++中,你需要为你所吃的东西买单.所以,在这种情况下,我付的是什么?

这很简单.你买单std::cout."你只为你所吃的东西买单"并不意味着"你总是得到最优惠的价格".当然,printf更便宜.人们可以说这std::cout是更安全和更通用的,因此其更高的成本是合理的(它花费更多,但提供更多的价值),但这忽略了这一点.你不使用printf,你使用std::cout,所以你付费使用std::cout.你不用付钱printf.

一个很好的例子是虚函数.虚函数具有一些运行时成本和空间要求 - 但前提是您实际使用它们.如果您不使用虚拟功能,则不需要支付任何费用.

一些评论

  1. 即使C++代码评估更多的汇编指令,它仍然是一些指令,并且任何性能开销仍然可能与实际的I/O操作相比相形见绌.

  2. 实际上,有时它甚至比"在C++中为你所吃的东西买单"更好.例如,编译器可以推断在某些情况下不需要虚函数调用,并将其转换为非虚拟调用.这意味着您可以免费获得虚拟功能.那不是很好吗?

  • 你没有免费获得虚拟功能.您仍然需要支付首先编写它们的成本,然后调试编译器对代码的转换,当它与您应该执行的操作的想法不符时. (6认同)
  • @alephzero我不确定将开发成本与性能成本进行比较尤为重要. (2认同)

小智 47

"printf的汇编列表"不适用于printf,但适用于puts(编译器优化的种类?); printf比put更复杂...不要忘记!

  • 这是迄今为止最好的答案,因为所有其他人都被挂在关于`std :: cout`的内部的红色鲱鱼身上,这在组装清单中是看不到的. (12认同)
  • 汇编列表用于*调用*`puts`,如果只传递单个格式字符串和零额外args,它看起来与调用`printf`相同.(除了还有一个`xor%eax,%eax`,因为我们将寄存器中的零FP args传递给一个可变函数.)这些都不是实现,只是将指向字符串的指针传递给库函数.但是,将`printf`优化为`puts`是gcc对只有"%s"的格式所做的事情,或者当没有转换时,字符串以换行符结束. (12认同)

Mar*_*lli 44

我在这里看到了一些有效的答案,但我将更详细地了解细节.

如果您不想浏览整个文本墙,请跳至下面的摘要以获取主要问题的答案.


抽象化

所以,在这种情况下,我付的是什么?

你是在为抽象买单.能够编写更简单,更人性化的代码需要付出代价.在C++中,它是一种面向对象的语言,几乎所有东西都是一个对象.当您使用任何对象时,总有三件事情会发生在幕后:

  1. 对象创建,基本上是对象本身及其数据的内存分配.
  2. 对象初始化(通常通过某种init()方法).通常,内存分配在这个步骤中首先发生在引擎盖下.
  3. 对象破坏(并非总是如此).

你没有在代码中看到它,但每次使用一个对象时,上述三个方面都需要以某种方式发生.如果您手动完成所有操作,代码显然会更长.

现在,可以在不增加开销的情况下有效地进行抽象:编译器和程序员可以使用方法内联和其他技术来消除抽象开销,但这不是您的情况.

C++中真正发生了什么?

在这里,细分:

  1. std::ios_base类初始化,这是一切的I/O相关的基类.
  2. std::cout对象已初始化.
  3. 你的字符串被加载并传递给std::__ostream_insert它,(正如你已经通过名称所知)是一种std::cout(基本上是<<运算符)的方法,它将一个字符串添加到流中.
  4. cout::endl也被传递给std::__ostream_insert.
  5. __std_dso_handle传递给__cxa_atexit,这是一个全局函数,负责在退出程序之前"清理".__std_dso_handle本函数调用它本身来释放和销毁剩余的全局对象.

那么使用C ==不支付任何费用?

在C代码中,发生了很少的步骤:

  1. 您的字符串已加载并通过寄存器传递给putsedi.
  2. puts 被叫.

任何地方都没有对象,因此无需初始化/销毁任何东西.

然而,这并不意味着你没有为C中的任何东西"付钱".您仍在为抽象付费,并且还要对C标准库进行初始化并动态解析该printf函数(或者,实际上puts,由编译器优化,因为您不需要任何格式字符串)仍然会在幕后进行.

如果你用纯汇编编写这个程序,它看起来像这样:

jmp start

msg db "Hello world\n"

start:
    mov rdi, 1
    mov rsi, offset msg
    mov rdx, 11
    mov rax, 1          ; write
    syscall
    xor rdi, rdi
    mov rax, 60         ; exit
    syscall
Run Code Online (Sandbox Code Playgroud)

这基本上只会导致调用write syscall后跟exitsyscall.现在,将是完成同样事情的最低限度.


总结一下

C更加简单,只需要最低限度,只需对用户完全控制,即可完全优化和自定义他们想要的任何东西.您告诉处理器在寄存器中加载一个字符串,然后调用库函数来使用该字符串.另一方面,C++更复杂,更抽象.这在编写复杂代码时具有巨大的优势,并且允许更容易编写和更人性化的代码,但显然需要付出代价.在这种情况下,如果与C相比,C++的性能总会出现问题,因为C++提供的不仅仅是完成这些基本任务所需要的,因此会增加更多的开销.

回答你的主要问题:

我付的是我不吃的东西吗?

在这个具体案例中,是的.你没有利用C++提供的任何东西而不是C,但这仅仅是因为C++可以帮助你的那段简单代码中没有任何东西:它非常简单,你真的根本不需要C++.


哦,还有一件事!

乍一看,C++的优点可能看起来不那么明显,因为你编写了一个非常简单的小程序,但是看一些更复杂的例子并看到差异(两个程序完全相同):

C:

#include <stdio.h>
#include <stdlib.h>

int cmp(const void *a, const void *b) {
    return *(int*)a - *(int*)b;
}

int main(void) {
    int i, n, *arr;

    printf("How many integers do you want to input? ");
    scanf("%d", &n);

    arr = malloc(sizeof(int) * n);

    for (i = 0; i < n; i++) {
        printf("Index %d: ", i);
        scanf("%d", &arr[i]);
    }

    qsort(arr, n, sizeof(int), cmp)

    puts("Here are your numbers, ordered:");

    for (i = 0; i < n; i++)
        printf("%d\n", arr[i]);

    free(arr);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

C++:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(void) {
    int n;

    cout << "How many integers do you want to input? ";
    cin >> n;

    vector<int> vec(n);

    for (int i = 0; i < vec.size(); i++) {
        cout << "Index " << i << ": ";
        cin >> vec[i];
    }

    sort(vec.begin(), vec.end());

    cout << "Here are your numbers:" << endl;

    for (int item : vec)
        cout << item << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

希望你能清楚地看到我的意思.另请注意在C中如何使用较低级别管理内存malloc以及free如何在索引和大小方面需要更加小心,以及在进行输入和打印时如何非常具体.


Dam*_*mon 27

开始时有一些误解.首先,C++程序不会产生22条指令,它更像22,000条指令(我从我的帽子中提取了这个数字,但它大致在球场上).此外,C代码也不会产生9条指令.那些只是你看到的.

C代码做的是,在做了很多你看不到的东西后,它从CRT中调用一个函数(通常但不一定作为共享库存在),然后检查返回值或句柄错误,并纾困.根据编译器和优化设置,它甚至不真的叫printf,但是puts,甚至更原始的东西.
你可以在C++中编写或多或少相同的程序(除了一些不可见的init函数),如果你只是以相同的方式调用相同的函数.或者,如果你想要超正确,那同样的功能前缀std::.

相应的C++代码实际上并非完全相同.虽然整个<iostream>它都是一个肥胖丑陋的猪,为小型程序增加了巨大的开销(在"真正的"程序中你并没有真正注意到那么多),但是更为公平的解释是它做得太糟糕了很多东西,你没有看到,哪些只是工作.包括但不限于几乎任何杂乱的东西的魔法格式,包括不同的数字格式和区域设置和诸如此类,缓冲和正确的错误处理.错误处理?是的,猜猜看,输出字符串实际上可能会失败,而且与C程序不同,C++程序不会默默地忽略它.考虑std::ostream到引擎盖下的内容,没有人知道,它实际上非常轻巧.不像我正在使用它,因为我讨厌激情的流语法.但是,如果你考虑一下它的作用,那就太棒了.

但肯定的是,C++总体是一样高效为C即可.它不是那么有效,因为它不是同一个东西而且它没有同样的事情.如果不出意外,C++会生成异常(以及生成,处理或失败的代码),并且它提供了C不提供的一些保证.所以,当然,C++程序有点需要更大一点.然而,从大局来看,这无论如何都无关紧要.相反,对于真正的程序,我并不是很少发现C++表现更好,因为出于某种原因,它似乎有助于更有利的优化.不要问我为什么特别,我不会知道.

如果你不是为了最好的方法而不是为了最好的方法而是你想要编写正确的 C代码(即你实际检查错误,并且程序在出现错误时表现正确),那么差异是微不足道的,如果存在的话.

  • 非常好的答案,除了这个断言:"但可以肯定的是,C++整体效率不如C可以"是完全错误的.C++可以像C一样高效,并且足够高级的代码可以比等效的C代码更高效.是的,C++由于必须处理异常而有一些开销,但在现代编译器上,与更好的无成本抽象的性能提升相比,其开销可以忽略不计. (16认同)
  • @Saher:是的,不,也许吧.`std :: cout`是一个`std :: basic_ostream`和一个_can_ throw,如果[配置](https://en.cppreference.com/w/cpp/io/basic_ios),它就会_can_重新抛出原本发生的异常/例外)这样做或者_can_吞下异常.事情是_can_失败,C++以及C++标准库(大部分)构建,所以失败不容易被忽视.这是一种烦恼_和_祝福(但更多的是祝福而不是烦恼).另一方面,C只显示你的中指.你不检查返回代码,你永远不知道发生了什么. (6认同)

Kev*_*inZ 22

你正在为错误买单.在80年代,当编译器不足以检查格式字符串时,运算符重载被认为是在io期间强制执行某些类型安全性的好方法.但是,它的每一个横幅功能都从一开始就严重执行或从概念上破产:

<了iomanip>

C++流io api中最令人厌恶的部分是这个格式化头库的存在.除了有状态,丑陋和容易出错之外,它还将格式与流相结合.

假设您要打印出一行,其中包含8位零填充十六进制无符号整数,后跟一个空格,后跟一个带有3位小数的双精度数.有了<cstdio>,您可以阅读简洁的格式字符串.使用时<ostream>,您必须保存旧状态,将对齐设置为右,设置填充字符,设置填充宽度,将基数设置为十六进制,输出整数,恢复保存状态(否则您的整数格式将污染您的浮动格式),输出空间,将表示法设置为固定,设置精度,输出double和换行符,然后恢复旧格式.

// <cstdio>
std::printf( "%08x %.3lf\n", ival, fval );

// <ostream> & <iomanip>
std::ios old_fmt {nullptr};
old_fmt.copyfmt (std::cout);
std::cout << std::right << std::setfill('0') << std::setw(8) << std::hex << ival;
std::cout.copyfmt (old_fmt);
std::cout << " " << std::fixed << std::setprecision(3) << fval << "\n";
std::cout.copyfmt (old_fmt);
Run Code Online (Sandbox Code Playgroud)

运算符重载

<iostream> 是如何不使用运算符重载的典型代表:

std::cout << 2 << 3 && 0 << 5;
Run Code Online (Sandbox Code Playgroud)

性能

std::cout慢几倍printf().猖獗的特征性和虚拟调度确实造成了损失.

线程安全

这两个<cstdio><iostream>是线程在安全的每一个函数调用是原子.但是,printf()每次通话都可以完成更多工作.如果使用该<cstdio>选项运行以下程序,则只会看到一行f.如果您<iostream>在多核计算机上使用,您可能会看到其他内容.

// g++ -Wall -Wextra -Wpedantic -pthread -std=c++17 cout.test.cpp

#define USE_STREAM 1
#define REPS 50
#define THREADS 10

#include <thread>
#include <vector>

#if USE_STREAM
    #include <iostream>
#else
    #include <cstdio>
#endif

void task()
{
    for ( int i = 0; i < REPS; ++i )
#if USE_STREAM
        std::cout << std::hex << 15 << std::dec;
#else
        std::printf ( "%x", 15);
#endif

}

int main()
{
    auto threads = std::vector<std::thread> {};
    for ( int i = 0; i < THREADS; ++i )
        threads.emplace_back(task);

    for ( auto & t : threads )
        t.join();

#if USE_STREAM
        std::cout << "\n<iostream>\n";
#else
        std::printf ( "\n<cstdio>\n" );
#endif
}
Run Code Online (Sandbox Code Playgroud)

对这个例子的反驳是,大多数人都会遵守规则,从而永远不会从多个线程写入单个文件描述符.那么,在这种情况下,你必须观察到,<iostream>会帮忙,抓住每一个锁<<和每一个>>.而在<cstdio>,你不会经常锁定,你甚至可以选择不锁定.

<iostream> 花费更多的锁来实现不太一致的结果.

  • "`std :: cout`慢几倍`printf()`" - 这个说法在整个网络上都重复了,但多年来都没有.现代IOstream实现与`printf`相同.后者还在内部执行虚拟调度以处理缓冲流和本地化IO(由操作系统完成但仍然完成). (6认同)
  • @KevinZ这很棒,但它是一个单一的特定调用的基准测试,它展示了fmt的特定优势(单个字符串中有许多不同的格式).在更典型的用法中,`printf`和`cout`之间的差异缩小了.顺便提一下,这个网站上有很多这样的基准测试. (3认同)
  • @KonradRudolph这也不是真的.微型计算机标记通常会低估膨胀和间接成本,因为它们不会耗尽某些有限的资源(无论是寄存器,icache,内存,分支预测器).当你提到"更典型的用法"时,它基本上是说你在其他地方有更多的膨胀,这很好,但不是主题.在我看来,如果你没有性能要求,你不需要用C++编程. (3认同)
  • printf的大多数实现都具有非常有用的本地化功能:编号参数.如果您需要以两种不同的语言(如英语和法语)生成一些输出并且单词顺序不同,则可以使用具有不同格式字符串的相同printf,并且它可以按不同顺序打印参数. (2认同)
  • 流的状态格式必须给很多难以找到的bug我不知道该说些什么.很好的答案.如果可能的话,会不止一次地投票. (2认同)

Pha*_*rap 18

除了所有其他答案所说的,
还有一个事实std::endl一样的'\n'.

这是一个不幸的常见误解.std::endl并不意味着"新行",
它意味着"打印新行然后刷新流 ".法拉盛并不便宜!

完全无视之间的差异printfstd::cout了一会儿,在功能上eqvuialent到C例如,你的C++例子应该是这样的:

#include <iostream>

int main()
{
    std::cout << "Hello world\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这里有一个示例,如果您包含刷新,您的示例应该是什么样的.

C

#include <stdio.h>

int main()
{
    printf("Hello world\n");
    fflush(stdout);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

C++

#include <iostream>

int main()
{
    std::cout << "Hello world\n";
    std::cout << std::flush;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在比较代码时,您应该始终小心,您要比较喜欢,并了解您的代码正在做什么的含义.有时即使是最简单的例子也比有些人意识到的要复杂得多.


The*_*kis 16

虽然现有的技术答案是正确的,但我认为这个问题最终源于这种误解:

众所周知,在C++中,你需要为你所吃的东西买单.

这只是来自C++社区的营销演讲.(公平地说,每个语言社区都有营销演讲.)这并不意味着你可以认真依赖的任何具体内容.

"你支付你使用的费用"应该意味着如果你使用该功能,C++功能只会有开销.但是"特征"的定义并非无限细节.通常,您最终会激活具有多个方面的功能,即使您只需要这些方面的子集,但实现部分地使用该功能通常也不实际或不可能.

一般来说,许多(尽管可能不是全部)语言都努力提高效率,并取得了不同程度的成功.C++在某种程度上是一个规模,但它的设计没有任何特殊或神奇的东西可以让它在这个目标中取得完美的成功.

  • @ Rakete1111长期以来,如果不抛出异常,它们就不会花费.如果您的程序一直在投掷,则应重新设计.如果失败的条件超出您的控制范围,您应该在调用依赖于条件为假的方法之前检查具有bool返回完整性检查的条件. (2认同)
  • 嗯,这是营销,但几乎完全正确.您可以坚持使用`<cstdio>`而不包含`<iostream>`,就像使用`-fno-exceptions -fno-rtti -fno-unwind-tables -fno-asynchronous-unwind-tables`编译一样. (2认同)

Bat*_*eba 12

C++中的输入/输出函数编写得很优雅,设计使它们易于使用.在许多方面,它们是C++中面向对象特性的展示.

但是你确实放弃了一些性能,但与操作系统处理较低级别的功能所花费的时间相比,这可以忽略不计.

您可以随时回退到C风格函数,因为它们是C++标准的一部分,或者可能完全放弃可移植性并使用对操作系统的直接调用.

  • "C++中的输入/输出功能是可怕的怪物,他们正在努力隐藏他们的Cthulian性质背后的薄薄的有用性.在许多方面,它们是如何不设计现代C++代码的展示".可能会更准确. (22认同)
  • @ user673679:非常正确.C++ I/O流的重大问题在于:真正有很多复杂性正在发生,任何曾经处理过这些问题的人(我指的是'std :: basic_*stream`向下)都知道传入的eadaches.它们被设计成广泛的一般性并通过继承扩展; 但是没有人最终这样做,因为它们的复杂性(在iostreams上写的字面书),以及为此而出生的新图书馆(例如,加速,ICU等).我怀疑我们永远不会为这个错误付出代价. (3认同)