我发布了一个带有代码的问题,其中唯一的#include
指令如下:
#include <bits/stdc++.h>
我的老师让我做这个,但在评论区有人告诉我不应该这样做。
为什么?
我发布了一个带有代码的问题,其中唯一的#include
指令如下:
#include <bits/stdc++.h>
我的老师让我做这个,但在评论区有人告诉我不应该这样做。
为什么?
包括<bits/stdc++.h>
似乎越来越常见于Stack Overflow,可能是当前学年的国家课程中新添加的内容。
我想,其优点大致如下:
#include
。不幸的是,这是一种懒惰的方法,直接命名GCC内部头文件而不是像<string>
、<iostream>
和<vector>
等单独的标准头文件。它破坏了可移植性并培养了糟糕的习惯。
缺点包括:
不要这样做!
更多信息:
Quora不好的一个例子:
为什么?因为它的使用方式好像是应该作为C++标准头文件的,但是没有标准提到它。因此你的代码在构建时就非可移植的。你在cppreference上找不到任何关于它的文档。所以它可能不存在。这只是某个人想象出来的东西 :)
我发现一个众所周知的教程网站每个C++示例都似乎包含了这个头文件,令我感到恐惧和不信。世界疯了。这就是证明。
对于写这种“教程”的人
请停止使用这个头文件。忘记它。不要传播这种疯狂的行为。如果您不愿意理解为什么这样做是错误的,请相信我的话。我不希望被当作任何事情的权威人士,而且我可能有一半的时间都是胡说八道,但我只在这一个特殊情况下例外。我声称我知道我在这里在谈论些什么。相信我。我恳求你。
P.S. 我可以很好地想象出这种邪恶想法可能已经发生在哪种可怕的“教学标准”中,以及导致这种情况的情况。只是因为似乎存在实际需要并不意味着它是可以接受的——即使是回顾也不行。
P.P.S. 不,没有实际需要。C++标准头文件并不是很多,而且它们都有良好的文档。如果您作为教师,通过添加这样的“魔法”来帮助学生,那么您将给他们造成不便。我们最不想要的就是培养出一群神奇思维的程序员。如果您需要为学生提供C++的子集以方便他们的学习,请提供一个手册,包含适用于所教授课程的少量头文件,并提供对您期望学生使用的库构造的简明文档。
using namespace std;
以及相应的头文件时,像用户自定义的gcd、swap或名为data的变量等简单事物将会表现出奇怪的行为或者根本无法编译,这会让编码者不知所措。 - PaulMcKenzie它们的目的是为了娱乐,而不是像工作中遇到的现实问题那样让程序员感到愉悦。 Code Golf是“一种娱乐性的计算机编程竞赛,参与者努力实现一定算法的最短源代码”。在PP&CG网站的答案中,您会看到人们指定答案中的字节数。当他们找到缩减几个字节的方法时,他们会划掉原始数字并记录新数字。一种玩具、问题或其他设计,旨在通过提出需要巧妙或耐心努力解决的困难来娱乐。
stdc++.h
有什么关系?正如其他人指出的那样,使用它是懒惰的。它是不可移植的,因此您不知道它是否适用于您的编译器或下一个版本的编译器。它养成了坏习惯。它是非标准的,因此您的程序行为可能与您预期的不同。它可能会增加编译时间和可执行文件大小。stdc++.h
。来自C++编程语言的标准草案N4606:
17.6.1.2 头文件[headers]
每个C++标准库元素在头文件中声明或定义(如果适用)。
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.
<bits/stdc++.h>同样会发出警告:
* This is an implementation file for a precompiled header.
我想看一些真实数据--比较编译时间和二进制可执行文件大小。因此,这里是一个快速的“hello world”比较测试。
注意:要了解哪里是<bits/stdc++.h>
头文件,以及其中的内容,请直接跳到底部标题为“<bits/stdc++.h>
在哪里以及什么是它?”的部分。
包含<bits/stdc++.h>
“包含所有头文件”的头文件很容易,但相对编译速度较慢。
包含头文件<bits/stdc++.h>
在gcc/g++编译器(以及可能的llvm clang编译器,因为它们旨在与gcc兼容)上可以正常工作,并且:
这里是一个C++程序示例:
// 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
平均编译时间: (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.3752
秒。
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/precompiled/stdc++.h
至少我喜欢看到所有可以包含的标头列表,以及它们与哪个C++版本相关,通过查看该头文件。在这方面,它非常有用。
如果您在具有出色索引器的IDE(例如 Eclipse,它拥有我发现过的最好的索引器;它的索引比 MS VSCode 好得多)中打开上面的代码,并在#include <bits/stdc++.h>
行上按下 Ctrl + Click,它将直接跳转到系统上的头文件!在 Linux Ubuntu 上,它将直接跳转到此路径并打开此文件:/usr/include/x86_64-linux-gnu/c++/8/bits/stdc++.h
。
<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
size
输出中text
、data
、bss
和dec
的含义:
我们不使用的原因:
#include <bits/stdc++.h>
这是因为效率问题。 让我打个比方: 对于那些了解Java的人来说: 如果你问你的教练以下是否是一个好主意,除非他们是一位糟糕的教练,否则他们会说不:
import java.*.*
#include <bits/stdc++.h>
看起来很方便,只需键入一个包含语句就可以工作了,移动整个库也是一样,只需要移动整个库而不是逐个移动五本书。对于你来说似乎很方便,但对于实际需要搬运的人来说呢?并不是那么方便,而且猜猜在C++中负责搬运的人将会是您的计算机......计算机并不喜欢为您编写的每个源文件搬移整个库:)
import java.*.*
是有效且定义良好的,在C++中,#include <bits/stdc++.h>
不是,因为该头文件甚至说明了它是GCC的内部头文件和实现细节。它随时可能会更改,可能包含使您的代码格式化驱动器的宏,也可能在下一个版本中消失。 - ABaumstumpf如果你的老师是一个ICPC教练,那他/她是正确的;但如果你的老师是一位软件工程师,可能他/她就不对了。
两者都有利弊:
利:
弊:
如@Lightness Races in Orbit提到的Quora问题的顶部答案所解释的那样,在编程竞赛的情境下包含bits/stdc++.h
没有任何问题。在那里,与可移植性、编译时间和标准化相关的缺点并不重要。如果示例代码使用该包含,则在大学编程课程中也是如此。
如果你正在编写生产代码,则不要使用它。根据你当前编写的代码的目的轻松切换回去和切换回来不应该是什么大问题。
#include <bits/stdc++.h>
是完全不同的事情! - Lightness Races in Orbit对我来说最大的问题是包含这个头文件无法编译。因此,如果它存在,我就必须删除它,尝试编译,并添加所需的标准头文件。
using namespace std;
也有一个 include 版本存在。 - user4581301bits/stdc++.h.gch
,即预编译版本。它存在的原因是必须存在,以便可以生成它的预编译版本。 - Jonathan Wakely