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

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

#include <bits/stdc++.h>

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

为什么?

73610 次浏览

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

我想好处大概是这样给出的:

  • 你只需要写一行#include
  • 您不需要查找所有内容都在哪个标准标头中。

不幸的是,这是一个懒惰的hack,直接命名GCC内部头,而不是单独的标准头,如<string><iostream><vector>。它破坏了可移植性,养成了糟糕的习惯。

缺点包括:

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

不要这样做!


更多信息:

Quora为什么不好的例子:

为什么?因为它被当作c++标准标头使用,但没有标准提到它。所以你的代码在构造上是不可移植的。你在cppreference上找不到任何关于它的文档。所以它还不如不存在。这是某人想象出来的。

我已经发现-我的恐惧和难以置信-有一个著名的教程网站每个c++示例似乎都包含这个头文件。世界疯了。这就是证明。


致任何撰写此类“教程”的人;

请停止使用此标头。算了吧。不要传播这种疯狂。如果你不愿意理解为什么这样做是错误的,相信我的话。我不喜欢在任何事情上被当作权威人物,我可能有一半的时间都是这样,但我只在这件事上破例。我说我知道我在说什么。相信我的话。我恳求你。

附言:我完全可以想象到那可恶的教学标准。这个邪恶的想法可能发生在哪里,以及导致它的情况。仅仅因为它似乎有实际的需要,并不意味着它就可以被接受——甚至在回想起来也是如此。

顺便说一句,其实也没什么实际需要。c++标准头文件并没有那么多,而且它们有很好的文档。如果你在教书,你会因为加了这样的“魔法”而对你的学生造成伤害。我们最不想要的就是培养一种神奇思维的程序员。如果你需要为学生提供c++的一个子集来让他们的生活更轻松,只需制作一份讲义,上面有适用于你所教课程的简短标题列表,以及你希望学生使用的库结构的简明文档。

正如@Lightness Races在轨道上提到的Quora问题的顶部答案所解释的那样,在编程比赛的上下文中包含bits/stdc++.h并没有错。可移植性、编译时间和标准化方面的缺点与此无关。在大学编程课上,如果示例代码使用了该包含,情况也是如此。

如果您正在编写产品代码,那么就不要使用它。根据当前正在编写的代码的目的来回切换应该不是什么大问题。

有一个叫做编程难题&代码高尔夫的堆栈交换站点。该站点上的编程难题符合谜题的定义:

玩具,难题:一种玩具、难题或其他设计来娱乐的东西,通过提出需要用聪明才智或耐心努力来解决的困难

设计它们是为了娱乐,而不是像工作中的程序员在日常工作中遇到现实世界的问题时那样感到有趣。

代码高尔夫是“一种娱乐性的计算机编程比赛,参与者努力实现实现某种算法的最短源代码。”在PP&CG网站上的答案中,你会看到人们在他们的答案中指定字节数。当他们找到一种方法来减少几个字节时,他们会去掉原来的数字并记录下新的数字。

正如您所期望的,代码高尔夫会导致极端的编程语言滥用。一个字母的变量名。没有空格。创造性地使用图书馆功能。无证功能。非标准编程实践。骇人听闻的黑客。

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

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

这些都是合理和正确的反对意见。那为什么会有人用这个怪物呢?

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

这是个好主意吗?让我们来看看缺点清单。可移植性吗?这没有关系,因为这些编码活动使用的是参赛者事先知道的特定编译器版本。标准的兼容性呢?对于有效寿命小于一小时的代码块来说,这是不相关的。编译时间和可执行文件大小?这些都不是比赛评分标准的一部分。

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

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

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

这个问题问:“为什么我不应该#包含<bits/stdc++.h>?”我意识到它的提问和回答是为了表明一个观点,而公认的答案是为了成为这个问题的唯一正确答案。但问题不是“为什么我不应该在产品代码中#包含<bits/stdc++.h> ?”因此,我认为考虑其他答案可能不同的情况是合理的。

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

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

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

& lt;比特/ stdc + + .h>还附带警告:

*  This is an implementation file for a precompiled header.

我们不使用的原因:

#include <bits/stdc++.h>

是因为效率。 让我打个比方: 了解Java的同学: 如果你问你的教练下面这个主意是不是好主意,除非他们是一个糟糕的教练,否则他们会说不:

import java.*.*
< p > # include……事情基本上是一样的…这不是不使用它的唯一原因,但它是不使用它的主要原因之一。 打个现实生活的比方: 假设你有一个图书馆,你想从图书馆借几本书,你会把整个图书馆搬到你家旁边吗?这既昂贵又低效。如果你只需要5本书,那就只拿5本……不是整个库.....

#include <bits/stdc++.h>

看起来很方便的程序看,我只需要输入一个包含语句,它工作,同样的事情,移动整个图书馆,看,我只需要移动整个图书馆,而不是5本书,一个接一个。对你来说看起来很方便,对真正需要搬家的人来说??没有那么多,你猜怎么着,在c++中,做移动的人将是你的电脑……计算机不会喜欢为您编写的每个源文件移动整个库:).....

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

包含<bits/stdc++.h>到底有多糟糕?

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

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

简介:

包括<bits/stdc++.h> "包括所有头"Header很简单,但是编译相对较慢。

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

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

# 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

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

如果我运行“编译”;下面是我看到的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

__abc2 __abc0 = __abc3。

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

__abc2 __abc0 = __abc3。

size bin/a节目:

text    data     bss     dec     hex filename
2142     656     280    3078     c06 bin/a

结论

因此,在gcc/g++编译器中包含头文件可以很好地工作,并使二进制可执行文件大小没有差异,但它需要1.3752秒/ 0.3412秒= 4倍的编译时间!

<bits/stdc++.h>是什么?

总结

<bits/stdc++.h>头文件是gcc/g++编译器的一部分。

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

你可以在这里直接在线查看gcc源代码中的文件:gcc / libstdc + + v3 / include /预编译/ stdc + + . h

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

细节

如果您在一个具有优秀索引器的IDE中打开上面的代码,例如Eclipse(它拥有我所发现的最好的索引器;它索引更好的而不是MS VSCode),以及#include <bits/stdc++.h>行上的Ctrl + 点击,它将直接跳转到系统上的头文件!在Linux Ubuntu上,它直接跳转到这个路径并打开这个文件:/usr/include/x86_64-linux-gnu/c++/8/bits/stdc++.h

你可以在gcc源代码中直接查看该文件的最新版本,在这里:gcc / libstdc + + v3 / include /预编译/ stdc + + . h。这是非常有用和深刻的,只看所有的头文件在一个地方,以获得他们是什么,他们包括什么感觉!同样,在Eclipse中,您可以轻松地对每个包含的头文件进行Ctrl + 点击,从而直接跳转到其源代码实现。

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

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. [my Questions &了解textdatabssdecsize输出中的含义:
    1. 电气工程堆栈交换:我如何在编译时发现有多少STM32的闪存和动态内存(SRAM)被用完了?< / >
    2. 转换binutils size输出从"sysv"format (size --format=sysv my_executable) to "berkeley"格式(size --format=berkeley my_executable) < / >

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

如果你的老师是一个ICPC老师,那么他/她是对的,但如果你的老师是一个软件工程老师,他/她可能不是。 两者都有优缺点:

  1. 使用它可以节省编码时间,但会增加编译时间。
  2. 由于它包含大量名称空间,您可能会意外地遇到难以调试的问题。