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

Why should I not #include <bits/stdc++.h>?

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

#include <bits/stdc++.h>

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

为什么?

包括 <bits/stdc++.h> 似乎是 Stack Overflow 上越来越常见的事情,也许是本学年新添加到国家课程中的东西。

我想优点是这样模糊地给出的:

  • 你只需要写一个 #include 行。
  • 你不需要查找哪个标准header所有东西都在。

不幸的是,这是一个懒惰的 hack,直接命名 GCC 内部 header 而不是像 <string><iostream> 和 [=14= 这样的个别标准 header ].它破坏了便携性并养成了糟糕的习惯。

缺点包括:

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

别这样!


更多信息:

为什么 Quora 不好的例子:

为什么?因为它被用作好像它应该是 C++ 标准 header,但没有标准提到它。所以你的代码是 non-portable 的构造。您不会在 cppreference 上找到它的任何文档。所以它也可能不存在。这是某人的想象:)

我惊恐地发现,有一个 well-known 教程站点,其中 每个 C++ 示例似乎都包含这个 header。世界疯了。这就是证据。


致所有编写此类“教程”的人

请停止使用此 header。忘掉它。不要传播这种精神错乱。如果您不愿意理解为什么这样做 错误,请相信我。在任何事情上我都不能接受被视为权威人物,而且我可能有一半的时间都被它充满了,但我只会在这种情况下破例。我声称我知道我在这里说的是什么。相信我的话。求求你了。

P.S。我完全可以想象出这种邪恶想法可能产生的可恶的“教学标准”,以及导致这种想法的情况。仅仅因为似乎有实际需要并不能使它被接受——即使现在回想起来也不行。

P.P.S。不,没有实际需要。没有那么多 C++ 标准 header,而且它们都有详细的文档记录。如果你教书,你会通过添加这样的“魔法”来伤害你的学生。培养具有神奇思维的程序员是我们最不想要的。如果您需要向学生提供 C++ 的一个子集以使他们的生活更轻松,只需制作一份讲义,其中包含适用于您教授的课程的 header 的简短列表,以及您希望学生使用的库结构的简明文档使用。

正如 Quora question mentioned by @Lightness Races in Orbit 的最佳答案中所解释的,在编程竞赛的上下文中包含 bits/stdc++.h 并没有错。可移植性、编译时间和标准化方面的缺点与此无关。如果示例代码使用 include.

,那么在大学编程中也是如此 class

如果您正在编写生产代码,请不要使用它。根据您当前编写的代码的目的来回切换应该没什么大不了的。

有一个名为 Programming Puzzles & Code Golf. The programming puzzles on that site fit this definition of puzzle 的 Stack Exchange 站点:

a toy, problem, or other contrivance designed to amuse by presenting difficulties to be solved by ingenuity or patient effort.

它们是为了娱乐而设计的,而不是以工作程序员可能被日常工作中遇到的现实问题来娱乐的方式。

Code Golf 是 "a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that implements a certain algorithm." 在 PP&CG 站点的答案中,您会看到人们在他们的答案中指定了字节数。当他们找到削减几个字节的方法时,他们会删除原来的数字并记录新的数字。

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

如果程序员在工作中提交包含高尔夫风格代码的拉取请求,它将被拒绝。他们的同事会嘲笑他们。他们的经理会到他们的办公桌旁聊天。尽管如此,程序员还是通过向 PP&CG 提交答案来消遣。

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

这些都是有效且正确的反对意见。那么为什么会有人使用这个怪物?

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

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

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

练习的缺点

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

所以是否使用 stdc++.h 的问题归结为编程竞赛中编码速度的好处是否超过使用它可能养成的坏习惯。

这个问题问的是:"Why should I not #include <bits/stdc++.h>?"我知道有人提出这个问题并回答了一个问题,接受的答案旨在成为这个问题的唯一正确答案。但问题不是 "Why should I not #include <bits/stdc++.h> in production code?" 因此,我认为考虑答案可能不同的其他情况是合理的。

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

17.6.1.2 Headers [headers]

  1. C++ 标准库的每个元素都在 header.

  2. 中声明或定义(视情况而定)
  3. C++标准库提供了61个C++库header,如图Table14.

Table 14 — C++ 库 headers

<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>

那里没有。这并不奇怪,因为 headers 是实现细节,通常带有警告:

*  This is an internal header file, included by other library headers.
*  Do not attempt to use it directly. 

也带有警告:

*  This is an implementation file for a precompiled header.

我们不使用的原因:

#include <bits/stdc++.h>

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

import java.*.*

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

#include <bits/stdc++.h>

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

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

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

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

注意:学习其中<bits/stdc++.h>header文件,里面有什么,直接跳到底部标题为“<bits/stdc++.h> 在哪里,什么是什么?”的部分。

总结:

包含<bits/stdc++.h>“包含所有headers”header很容易,但编译速度相对较慢。

包括 <bits/stdc++.h> header 文件在 gcc/g++ 编译器中工作良好(并且可能 llvm clang compiler 也是如此,因为它们的目标是 gcc-compatible), 和

  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;
}

这里有一些构建和 运行 命令:

# make a bin dir
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

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

如果我 运行 使用代码 as-is 上面的“编译”命令,这里是我看到的 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

平均编译时间: (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

顶部有 #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

平均编译时间: (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

结论

因此,包括 header 可以很好地与 gcc/g++ 编译器一起工作,并且 二进制可执行文件大小 没有区别,但它需要 1.3752秒 / 0.3412 秒 = 编译时间延长 4 倍!

<bits/stdc++.h> 在哪里,什么是什么?

总结

<bits/stdc++.h> header 文件包含在 gcc/g++ 编译器中。

如果在 Linux 上,它将位于您本地系统的 /usr/include/x86_64-linux-gnu/c++/8/bits/stdc++.h

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

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

详情

如果您在 IDE 中打开上面的代码并使用出色的索引器,例如 Eclipse(它具有我所发现的最好的索引器;它索引 好得多 than MS VSCode), and Ctrl + Click on the #include <bits/stdc++.h> line, it will jump straight to that header 文件在您的系统上!在 Linux Ubuntu 上,它直接跳转到这个路径并打开这个文件:/usr/include/x86_64-linux-gnu/c++/8/bits/stdc++.h.

你可以直接在gcc源代码中查看这个文件的最新版本,这里:gcc/libstdc++-v3/include/precompiled/stdc++.h它只是一个 header 文件,其中 包括所有其他 header 文件! 这真的很有用,而且很有见地,只是看看将所有 header 个文件放在一个地方,以了解它们是什么以及它们包含什么!同样,在 Eclipse 中,您可以轻松地 Ctrl + 单击 每个包含的 header 文件以直接跳转到其源代码实现。

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

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

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

// Copyright (C) 2003-2022 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 <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 <algorithm>
#include <bitset>
#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 <string_view>
#include <variant>
#endif

#if __cplusplus >= 202002L
#include <barrier>
#include <bit>
#include <compare>
#include <concepts>
#if __cpp_impl_coroutine
# include <coroutine>
#endif
#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 <spanstream>
#if __has_include(<stacktrace>)
# include <stacktrace>
#endif
#include <stdatomic.h>
#endif

另见

  1. https://www.geeksforgeeks.org/bitsstdc-h-c/
  2. [我的问题与答案] 了解 textdatabssdecsize 输出中的含义:
    1. Electrical Engineering Stack Exchange: How do I find out at compile time how much of an STM32's Flash memory and dynamic memory (SRAM) is used up?
    2. Convert binutils size output from "sysv" format (size --format=sysv my_executable) to "berkeley" format (size --format=berkeley my_executable)

对我来说最大的问题是包含这个头文件不会编译。所以如果它在那里,我将不得不删除它,尝试编译,并添加所需的标准头文件。