我为什么不#include <bits/stdc ++.h>?

Lig*_*ica 215 c++ portability c++-faq turbo-c++ implementation-defined-behavior

我在我的代码中发布了一个问题,其唯一的#include指令如下:

#include <bits/stdc++.h>
Run Code Online (Sandbox Code Playgroud)

我的老师告诉我这样做,但在评论部分,我被告知我不应该这样做.

为什么?

Lig*_*ica 250

包括<bits/stdc++.h>在Stack Overflow上看起来越来越常见的东西,也许是本学年新增加到国家课程中的东西.

我想这些含义很模糊:

  • 你只需要写#include一行
  • 您无需查找所有标准标头

不幸的是,这是一个懒惰劈,命名GCC内部报头,而不是直接从个人标准头像<string>,<iostream><vector>.它破坏了便携性并养成了可怕的习惯.

缺点包括:

  • 它可能只适用于该编译器
  • 您不知道使用它时它会做什么,因为它的内容不是由标准设置的
  • 即使只是将编译器升级到自己的下一个版本也可能会破坏您的程序
  • 每个标准头必须与源代码一起进行解析和编译,这很慢并且在某些编译设置下会导致庞大的可执行文件

不要这样做!


更多信息:

Quora为什么不好的例子:

  • "也许在本学年新增加到国家课程中的东西"盲人领导盲人:( (63认同)
  • 刚刚从另一个问题来到虫洞,非常好.是什么让这种教学习惯变得更糟的是,通常会直接使用"使用namesapce std;".只有两行,几乎每个好的标识符都使用.看到它被教导令人难以置信. (22认同)
  • @KubaOber:没错. (13认同)
  • 关于quora示例,它可能随着时间而变化.我今天访问了这个页面以及<bits/stdc ++.h>的优点和缺点,其中列出了在线编程竞赛的特定背景.我发现他们的结论还可以. (4认同)
  • @MM:不是真的 - 只是因为你预编译标头并不意味着你需要预编译 _every_ 标头 (3认同)
  • 希望不是课程,而只是在“竞赛”网站或其他东西上传播的货物崇拜……尽管遗憾的是,无论哪种方式都不会让我感到惊讶。 (3认同)
  • @EvgeniSergeev:尝试确定其效果时,2KiB是*大量*的代码,数据,符号信息等。您了解所添加的所有内容吗?对于您的编译器?当前版本?所有版本之间?所有将来的版本?如果您需要在方便性和正确性之间做出选择,则只有一个有效选项。 (2认同)
  • @SujayPhadke&gt; 是的,编译器应该猜测开发人员的想法。当然,因为世界上只有一个名为“距离”的函数,“矢量”一词在容器之外当然没有任何意义,……你是对的,工具应该对此有所帮助。但这不是编译器的工作,而是 IDE 的工作(其中一些确实做到了)。 (2认同)
  • “每个标准头都必须与源代码一起解析和编译,这很慢”。有多慢?我做了一个快速的“hello world”测试并[在此处发布了结果](/sf/answers/5030463161/)。 (2认同)

Rei*_*ica 34

为什么?因为它被用作好像它应该是一个C++标准头,但没有标准提到它.所以你的代码是不可移植的.您将无法在http://cppreference.com上找到任何相关文档.因此它可能不存在.这是某人想象力的虚构:)

  • 他正在谈论 GeeksForGeeks (8认同)
  • 我说的是典型的“众所周知的网站”。不幸的是,这样的人非常多。他们看起来总是像盲人领盲人:( (8认同)
  • 那个众所周知的网站是每个 C++ 示例看起来都像 C 程序的网站? (5认同)
  • 当包含该标头以及“using namespace std;”时,将会很有趣。然后是简单的事情,比如用户定义的 [gcd](https://en.cppreference.com/w/cpp/numeric/gcd)、[swap](https://en.cppreference.com/w/cpp/algorithm) /swap) 或名为 [data](https://en.cppreference.com/w/cpp/iterator/data) 的变量将表现奇怪或根本无法编译,让编码人员摸不着头脑,不知道问题可能是什么。 (2认同)

Bul*_*net 21

来自 N4606,工作草案,编程语言 C++ 标准:

17.6.1.2 头文件 [headers]

  1. C++ 标准库的每个元素都在头文件中声明或定义(视情况而定)。

  2. C++标准库提供了61个C++库头文件,如表14所示。

表 14 — C++ 库头文件

<algorithm> <future> <numeric> <strstream>
<any> <initializer_list> <optional> <system_error>
<array> <iomanip> <ostream> <thread>
<atomic> <ios> <queue> <tuple>
<bitset> <iosfwd> <random> <type_traits>
<chrono> <iostream> <ratio> <typeindex>
<codecvt> <istream> <regex> <typeinfo>
<complex> <iterator> <scoped_allocator> <unordered_map>
<condition_variable> <limits> <set> <unordered_set>
<deque> <list> <shared_mutex> <utility>
<exception> <locale> <sstream> <valarray>
<execution> <map> <stack> <variant>
<filesystem> <memory> <stdexcept> <vector>
<forward_list> <memory_resorce> <streambuf>
<fstream> <mutex> <string>
<functional> <new> <string_view>
Run Code Online (Sandbox Code Playgroud)

那里没有 <bits/stdc++.h> 。这并不奇怪,因为 <bits/...> 标头是实现细节,通常带有警告:

*  This is an internal header file, included by other library headers.
*  Do not attempt to use it directly. 
Run Code Online (Sandbox Code Playgroud)

<bits/stdc++.h> 还带有警告:

*  This is an implementation file for a precompiled header.
Run Code Online (Sandbox Code Playgroud)


Red*_*ode 17

有一个叫做Programming Puzzles&Code Golf的Stack Exchange网站。该站点上的编程难题符合以下这个难题的定义:

一种玩具,问题或其他设计,旨在通过提出要通过机智或患者的努力来解决的困难来进行娱乐。

他们被设计为娱乐性的,而不是以使工作中的程序员在日常工作中遇到的现实世界问题为乐的方式。

Code Golf是“一种休闲型计算机编程竞赛,参赛者努力争取实现实现某种算法的最短源代码。” 在PP&CG网站上的答案中,您会看到人们在他们的答案中指定了字节数。当他们找到一种方法来减少一些字节时,他们将删除原始数字并记录新的数字。

如您所料,代码打高尔夫球会奖励极端的编程语言滥用。一字母变量名称。没有空格。创造性地使用库函数。未记录的功能。非标准编程实践。骇人听闻的骇客。

如果程序员在工作中提交包含高尔夫风格代码的拉取请求,该请求将被拒绝。他们的同事会嘲笑他们。他们的经理将坐在他们的办公桌旁聊天。即使这样,程序员也可以通过向PP&CG提交答案来娱乐自己。

这和什么有关系stdc++.h?正如其他人指出的那样,使用它是懒惰的。它是不可移植的,因此您不知道它是否可以在您的编译器或下一版本的编译器上运行。它养成不良习惯。它是非标准的,因此您的程序的行为可能与您期望的不同。它可能会增加编译时间和可执行文件的大小。

这些都是有效和正确的反对。那么,为什么有人会使用这种怪物?

事实证明,有些人喜欢没有代码高尔夫的编程难题。他们聚在一起,参加诸如ACM-ICPC,Google Code Jam和Facebook Hacker Cup之类的活动,或在Topcoder和Codeforces之类的网站上竞争。他们的排名取决于程序的正确性,执行速度以及提交解决方案的速度。为了最大化执行速度,许多参与者使用C ++。为了最大化编码速度,其中一些使用。stdc++.h

这是个好主意吗?让我们检查一下缺点列表。可移植性?没关系,因为这些编码事件使用了参赛者事先知道的特定编译器版本。是否符合标准?与使用寿命少于一小时的代码块无关。编译时间和可执行文件大小?这些不属于比赛计分规则。

所以我们留下了不良习惯。这是一个有效的反对意见。通过使用此头文件,参赛者避免了学习哪个标准头文件定义其在程序中使用的功能的机会。当他们编写实际代码(而不使用stdc++.h)时,他们将不得不花时间查找这些信息,这意味着他们的工作效率将降低。这是与练习的缺点stdc++.h

这就提出了一个问题,即如果它鼓励使用stdc++.h和违反其他编码标准等不良习惯,为什么值得参加竞争性编程。一个答案是人们之所以这么做是因为他们在PP&CG上发布程序的原因相同:一些程序员发现在类似游戏的环境中使用他们的编码技能很有趣。

因此,是否使用的问题stdc++.h归结为编程竞赛中的编码速度收益是否超过了使用它可能养成的不良习惯。

这个问题问:“为什么不应该#include <bits/stdc++.h>?” 我意识到有人提出并回答了这个问题,所接受的答案旨在成为该问题的唯一真实答案。但是问题不是“为什么不应该<bits/stdc++.h>在生产代码中包含#?” 因此,我认为考虑答案可能不同的其他情况是合理的。

  • @MartinBonnersupportsMonica 在简历中提及 CP 的结果——展示相关的问题解决能力和积极的热情——是一种 __*负面*__ 的想法简直是疯狂。编码风格、良好的架构运用、团队合作等都是必不可少的,但申请人不必在简历的每个点上勾选所有这些框。 (7认同)
  • @MartinBonner 我知道一些招聘经理将有竞争力的编程经验视为危险信号。但只要顶级软件公司在面试中使用 CP 式的问题,并举办编程竞赛来寻找新人,CP 将继续受到有抱负的开发人员的欢迎。 (5认同)
  • 虽然这个标头确实可以在一些竞争性编程中使用,但它并不是它的来源。是从一间教室传来的。无论是谁在教室里教书,都有足够的影响力,通过随后的连锁反应,污染数以万计甚至数十万的学生(通过教育当时不知不觉地传播了这种疾病的老师和同学)。现在,这些学生也在一个必去的教程地方编写教程。我只想躲在角落里哭一场。竞争性编程网站应该只有一个**正则表达式来拒绝任何非标准标头**。 (5认同)
  • @YunfeiChen 有些人认为它会鼓励候选人在工作中需要忘记的坏习惯(例如使用“#include &lt;bits/stdc++.h&gt;”或编写不可读的代码)。零编程经验也是一个危险信号,但这就是我们进行面试的原因。 (3认同)
  • 我已经投票赞成,但是可能值得指出的是,“为了好玩”是参加竞争性编程的一个很好的理由。另一方面,“打动潜在雇主”不是-它将积极地*伤害*您与我的案子。 (2认同)
  • @JesperJuhl如果贵公司的技术面试官在面试中使用算法难题(与许多人一样),那么具有竞争性编程经验的应聘者将具有优势。候选人的合理选择也许是参加CP,但要避免在简历/简历中提及。 (2认同)

Gab*_*les 9

我至少喜欢通过查看这个头文件来看到可以包含的所有头文件的列表,以及它们属于哪个版本的 C++。在这方面它确实很有用。

包括 真的有多糟糕吗<bits/stdc++.h>

我想看到一些真实的数据——一些用于比较编译时间二进制可执行文件大小的数字。因此,这是一个快速的“hello world”比较测试。

注意:要了解头文件在哪里<bits/stdc++.h>以及其中包含什么,请直接跳到底部标题为“哪里以及什么是<bits/stdc++.h>?”的部分。

概括:

包含<bits/stdc++.h>“include all headers”头文件很容易,但编译速度相对较慢。

包含<bits/stdc++.h>头文件可以很好地与 GCC/g++ 编译器配合使用(大概还有LLVM Clang 编译器,因为它们的目标是与 GCC 兼容),并且

  1. 对二进制可执行文件的大小没有影响,但是
  2. 编译时间延长了4 倍!

我的测试

下面是一个 C++ 程序示例:

include_bits_stdc++.cpp

// We will test including this header vs NOT including this header
#include <bits/stdc++.h>

#include <iostream>  // For `std::cin`, `std::cout`, `std::endl`, etc.

int main()
{
    printf("Hello ");
    std::cout << "world!\n\n";

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

以下是一些构建和运行命令:

# make a bin directory
mkdir -p bin

# compile, timing how long it takes
time g++ -Wall -Wextra -Werror -O3 -std=c++17 include_bits_stdc++.cpp -o bin/a

# check binary executable size
size bin/a

# run
bin/a
Run Code Online (Sandbox Code Playgroud)

#include <bits/stdc++.h>顶部没有 OUT

如果我按原样运行上面的“compile”命令,我会看到以下 10 次编译时间:

real    0m0.362s
real    0m0.372s
real    0m0.502s
real    0m0.383s
real    0m0.367s
real    0m0.283s
real    0m0.294s
real    0m0.281s
real    0m0.292s
real    0m0.276s
Run Code Online (Sandbox Code Playgroud)

平均编译时间: (0.362 + 0.372 + 0.502 + 0.383 + 0.367 + 0.283 + 0.294 + 0.281 + 0.292 + 0.276)/10 =0.3412

size bin/a显示:

text    data     bss     dec     hex filename
2142     656     280    3078     c06 bin/a
Run Code Online (Sandbox Code Playgroud)

#include <bits/stdc++.h>在顶部

10次​​编译时间:

real    0m1.398s
real    0m1.006s
real    0m0.952s
real    0m1.331s
real    0m1.549s
real    0m1.454s
real    0m1.417s
real    0m1.541s
real    0m1.546s
real    0m1.558s
Run Code Online (Sandbox Code Playgroud)

平均编译时间: (1.398 + 1.006 + 0.952 + 1.331 + 1.549 + 1.454 + 1.417 + 1.541 + 1.546 + 1.558)/10 =1.3752

size bin/a显示:

text    data     bss     dec     hex filename
2142     656     280    3078     c06 bin/a
Run Code Online (Sandbox Code Playgroud)

结论

因此,包含头文件可以与 gcc/g++ 编译器一起正常工作,并且对二进制可执行文件大小没有影响,但编译时间需要 1.3752 秒 / 0.3412 秒 = 4 倍长!

哪里、是什么<bits/stdc++.h>

概括

头文件<bits/stdc++.h>作为 gcc/g++ 编译器的一部分包含在内。

您可以使用以下命令在 Linux(例如:Ubuntu)系统上找到它:

find /usr/include -ipath "*bits/stdc++.h*"
Run Code Online (Sandbox Code Playgroud)

上面的命令几乎是即时的。但是,如果没有找到任何内容,您可以像这样搜索整个文件系统,这将花费更长的时间:

# Option 1: showing read permissions errors
find / -ipath "*bits/stdc++.h*"

# Option 2 (preferred): hiding read permissions (stderr, file descriptor 2)
# errors
find / -ipath "*bits/stdc++.h*" 2>/dev/null
Run Code Online (Sandbox Code Playgroud)

如果在 Linux Ubuntu 上,<bits/stdc++.h>将位于您的本地系统上:

  1. 在 Ubuntu 18.04 上,使用 GCC 8:/usr/include/x86_64-linux-gnu/c++/8/bits/stdc++.h
  2. 在 Ubuntu 22.04 上,使用 GCC 11:/usr/include/x86_64-linux-gnu/c++/11/bits/stdc++.h

您可以在这里直接在线查看gcc源代码中的文件:gcc/libstdc++-v3/include/precompiled/stdc++.h

我至少喜欢通过查看该头文件来查看可以包含的所有头文件的列表,以及它们属于哪个版本的 C++。在这方面它确实很有用。

细节

如果您在具有出色索引器的 IDE 中打开上面的代码,例如 Eclipse(它具有我发现的最好的索引器;它的索引比 MS VSCode好得多),并且在线上加上Ctrl+ ,它将直接跳转到该代码您系统上的头文件!在 Linux Ubuntu 上,它直接跳转到此路径并打开此文件:.Click#include <bits/stdc++.h>/usr/include/x86_64-linux-gnu/c++/8/bits/stdc++.h

您可以直接在 gcc 源代码中查看该文件的最新版本,此处:gcc/libstdc++-v3/include/precompiled/stdc++.h它只是一个包含所有其他头文件的头文件!这非常有用且富有洞察力,只需在一个位置查看所有头文件即可了解它们是什么以及它们包含什么!同样,在 Eclipse 中,您可以轻松地在每个包含的头文件上Ctrl+Click来直接跳转到其源代码实现。

<bits/stdc++.h>这是gcc 编译器中包含的完整、最新的头文件。如果您想将其包含在您自己的个人项目中或与其他编译器一起使用,您始终可以复制并粘贴此内容并自行创建此文件。

gcc/libstdc++-v3/include/precompiled/stdc++.h

// C++ includes used for precompiling -*- C++ -*-

// Copyright (C) 2003-2024 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

/** @file stdc++.h
 *  This is an implementation file for a precompiled header.
 */

// 17.4.1.2 Headers

// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <csetjmp>
#include <cstdarg>
#include <cstddef>
#include <cstdlib>

#if __cplusplus >= 201103L
#include <cstdint>
#endif

// C++
// #include <bitset>
// #include <complex>
#include <algorithm>
#include <bitset>
#include <functional>
#include <iterator>
#include <limits>
#include <memory>
#include <new>
#include <numeric>
#include <typeinfo>
#include <utility>

#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <initializer_list>
#include <ratio>
#include <scoped_allocator>
#include <tuple>
#include <typeindex>
#include <type_traits>
#endif

#if __cplusplus >= 201402L
#endif

#if __cplusplus >= 201703L
#include <any>
// #include <execution>
#include <optional>
#include <variant>
#include <string_view>
#endif

#if __cplusplus >= 202002L
#include <bit>
#include <compare>
#include <concepts>
#include <numbers>
#include <ranges>
#include <span>
#include <source_location>
#include <version>
#endif

#if __cplusplus > 202002L
#include <expected>
#include <stdatomic.h>
#if __cpp_impl_coroutine
# include <coroutine>
#endif
#endif

#if _GLIBCXX_HOSTED
// C
#ifndef _GLIBCXX_NO_ASSERT
#include <cassert>
#endif
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cwchar>
#include <cwctype>

#if __cplusplus >= 201103L
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdalign>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cuchar>
#endif

// C++
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>

#if __cplusplus >= 201103L
#include <array>
#include <atomic>
#include <chrono>
#include <codecvt>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <scoped_allocator>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#endif

#if __cplusplus >= 201402L
#include <shared_mutex>
#endif

#if __cplusplus >= 201703L
#include <any>
#include <charconv>
// #include <execution>
#include <filesystem>
#include <optional>
#include <memory_resource>
#include <variant>
#endif

#if __cplusplus >= 202002L
#include <barrier>
#include <bit>
#include <compare>
#include <concepts>
#include <format>
#include <latch>
#include <numbers>
#include <ranges>
#include <span>
#include <stop_token>
#include <semaphore>
#include <source_location>
#include <syncstream>
#include <version>
#endif

#if __cplusplus > 202002L
#include <expected>
#include <generator>
#include <print>
#include <spanstream>
#if __has_include(<stacktrace>)
# include <stacktrace>
#endif
#include <stdatomic.h>
#include <stdfloat>
#endif

#if __cplusplus > 202302L
#include <text_encoding>
#endif

#endif // HOSTED
Run Code Online (Sandbox Code Playgroud)

也可以看看

  1. https://www.geeksforgeeks.org/bitsstdc-hc/
  2. [我的问题与解答] 了解输出中textdatabssdec的含义size
    1. Electrical Engineering Stack Exchange:如何在编译时查明 STM32 的闪存和动态存储器 (SRAM) 已使用了多少?
    2. 将 binutilssize输出从“sysv”格式 ( size --format=sysv my_executable) 转换为“berkeley”格式 ( size --format=berkeley my_executable)

  • 由于该文件是 GPL 许可的,因此您在为自己的项目复制它时应该小心。这可能会让您承担法律责任,这对您和您的雇主来说可能非常不幸。 (2认同)

Yun*_*hen 5

我们不使用的原因:

#include <bits/stdc++.h>
Run Code Online (Sandbox Code Playgroud)

是因为效率。让我打个比方:对于那些了解 Java 的人:如果你问你的导师以下是否是一个好主意,除非他们是一个糟糕的导师,否则他们会说不:

import java.*.*
Run Code Online (Sandbox Code Playgroud)

#include... 事情基本上做同样的事情... 这不是不使用它的唯一原因,但它是不使用它的主要原因之一。举个现实生活中的比喻:想象一下你有一个图书馆,你想从图书馆借几本书,你会把整个图书馆搬到你家旁边吗?这将是昂贵且低效的。如果你只需要 5 本书,那么只拿出 5 本书......而不是整个图书馆......

#include <bits/stdc++.h>
Run Code Online (Sandbox Code Playgroud)

看起来很方便的程序看起来我只需要键入一个包含语句就可以了,这与移动整个图书馆相同,看起来我只需要移动一个整个图书馆而不是 5 本书,一个接一个。对你来说看起来很方便,也就是说,对于真正需要搬家的人??没有那么多,猜猜在 C++ 中进行移动的人将是您的计算机...计算机不会喜欢为您编写的每个源文件移动整个库:)....

  • 这几乎可以肯定是没有人认真提出标准“包含所有内容”标头的根本原因。因此,有一个合理的说法称其为我们不使用它的“原因”。 (2认同)