Có nên sử dụng #include<bits/stdc++.h> trong C++ hay không?
Nếu là dân competitive programming sử dụng C++ để code, chắc hẳn bạn đã từng "ồ", "wow", hú hét khi được khai sáng với dòng code #include <bits/stdc++.h>
🤩
Chữ "bits/
" chính là tên thư mục chứa file "stdc++.h
"
Cụ thể hơn thì thư mục bits/
là một thư mục nội bộ (internal directory) dùng bởi trình biên dịch GNU GCC, nơi chứa các header file của libstdc++.
Để kiểm chứng thông tin này, ví dụ như trên máy mình đang cài Code::Blocks để code C++, trong đó có sử dụng trình biên dịch GCC, mình sẽ vào thư mục bits
thông qua đường dẫn sau:
C:\Program Files\CodeBlocks\MinGW\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\x86_64-w64-mingw32\bits
Kết quả sẽ thấy các file như sau (trong đó có file stdc++.h
nằm ở gần cuối danh sách):
Nếu bạn đã từng xem source code của file stdc++.h, bạn sẽ thấy trong này gồm rất nhiều những câu lệnh #include
các header file khác nhau, là thư viện chuẩn trong C++ như iostream
, string
, vector
, map
, set
, stack
, queue
, ... (chữ "std" trong stdc++ là viết tắt của standard, tức là tiêu chuẩn)
Điều này đồng nghĩa với việc, chỉ với 1 dòng #include<bits/stdc++.h>
, bạn sẽ không còn phải nhớ và khai báo nhiều header file khi muốn sử dụng các đối tượng và hàm khác nhau trong đó nữa.
Việc này đặc biệt hữu ích khi chúng ta đang tham gia các cuộc thi lập trình thi đấu, nơi mà mọi người cần tập trung nhiều hơn vào việc tìm ra thuật toán để giải quyết vấn đề một cách hiệu quả, thì rõ ràng <bits/stdc++.h>
sẽ giúp anh em tiết kiệm được rất nhiều thời gian và hạn chế lỗi thiếu thư viện khi code ✅
Tuy nhiên, bước ra khỏi những cuộc thi này, đặc biệt là đến khi đi làm, việc sử dụng <bits/stdc++.h>
lại không được khuyến khích ❌
1. Tăng thời gian biên dịch
Bình thường khi cần dùng hàm của thư viện nào, chúng ta chỉ cần include thư viện đó, thì trình biên dịch sẽ thực hiện nhanh chóng.
Nhưng khi sử dụng <bits/stdc++.h>
, trong header file này include rất nhiều những header file của thư viện chuẩn trong C++, tức là bao gồm cả những thư viện mà trong chương trình của chúng ta không cần dùng đến. Do đó, thời gian biên dịch sẽ lâu hơn ⏳
Đặc biệt là khi làm việc với những project lớn, thường xuyên phải code-run-test, mà biên dịch chương trình còn lâu nữa chắc chắn sẽ khiến cho anh em developer vô cùng khó chịu 🤬
Để kiếm chứng điều này, trước tiên các bạn cần hiểu rõ hơn về quá trình biên dịch (compile).
Chúng ta thường biết chung chung rằng, để có thể chạy được chương trình code bằng C++, thì trình biên dịch (compiler) sẽ biên dịch source code thành mã máy (machine code). Thế nhưng thực tế quá trình này sẽ được gồm 4 bước chính như sau:
Bước 1: Preprocessing - Tiền xử lý:
-
Bước này sẽ xử lý các dòng
#include
,#define
,#ifdef
để tạo ra source code sau khi đã thay thế đầy đủ macro và chèn nội dung từ các header file. -
Do đó, nếu chúng ta chỉ
#include
những header file cụ thể mà chương trình cần, ví dụ như#include <iostream>
thôi, thì kết quả tạo ra ở bước này sẽ chỉ gồm nội dung từ header fileiostream.h
và các header file liên quan đến nó. -
Nhưng nếu sử dụng
#include <bits/stdc++.h>
thì kết quả tạo ra ở bước này sẽ gồm nội dung từ tất các các header file được liệt kê trong filestdc++.h
-
Không chỉ nói lý thuyết suông, mình sẽ kiểm chứng để các bạn nhìn thấy kết quả và hiểu rõ. Giả sử minh có một file
test.cpp
chỉ đơn giản là in ra dòng chữ"Hello, world!"
. Ban đầu mình sẽ chỉ#include <iostream>
thì đoạn code sẽ như sau:#include <iostream> using namespace std; int main() { cout << "Hello, world!"; return 0; }
-
Sau đó mình sẽ chạy lệnh dưới đây ở Command Prompt để tạo file đầu ra
test.i
chính là kết quả của bước tiền xử lý từ file source codetest.c
:gcc -E test.cpp -o test.i
-
Mở file
test.i
ra xem, chúng ta sẽ thấy trong này chỉ gồm nội dung củaiostream.h
cùng các header file liên quan đến nó và phần codecout << "Hello, world!"
của mình. Tổng cộng sẽ rơi vào khoảng26.562
dòng code. Và khi mình thử search từ "vector" để xem trong này có bị include nội dung header filevector.h
hay không, thì đương nhiên kết quả sẽ làNot found
, vì mình chỉ#include <iostream>
thôi mà: -
Thế nhưng, khi mình sửa lại code để sử dụng
#include <bits/stdc++.h>
, khác biệt sẽ cực kỳ lớn. Đoạn code sau khi chỉnh sửa sẽ như này:#include <bits/stdc++.h> using namespace std; int main() { cout << "Hello, world!"; return 0; }
-
Sau khi chạy lệnh
gcc -E test.cpp -o test.i
để có được kết quả đầu ra của bước tiền xử lý, mở filetest.i
lên, thử search chữ "vector", chúng ta sẽ thấy nó cũng được include vào luôn. Rồi kể cả "queue", "stack", "map", "set", ... cũng như thế. Đó chính là bởi vì trong filestdc++.h
có include tất cả các header file này, khiến cho khi thực hiện bước tiền xử lý, tất cả nội dung thật của các header file này cũng được đưa vào luôn. Dẫn đến filetest.i
lúc này có tới104.559
dòng code, nhiều gấp 4 lần so với việc chỉ#include <iostream>
như ở thử nghiệm trước đó. -
Từ vấn đề này, chắc hẳn các bạn đã hiểu rằng nó sẽ dẫn đến một loạt các vấn đề ở các bước tiếp theo của quá trình biên dịch, bao gồm:
Bước 2: Compiling - Chuyển source code sau quá trình tiền xử lý sang hợp ngữ (assembly)
Bước 3: Assembling - Chuyển hợp ngữ ở bước 2 sang mã máy (machine code), cụ thể là dạng object code (file
.o
hoặc.obj
)bởi vì source code sau khi tiền xử lý lớn hơn hẳn như vậy, thì file assembly tạo ra cũng sẽ lớn hơn, rồi khi tạo thành object code đương nhiên cũng lớn hơn nữa, dẫn đến thời gian biên dịch lâu hơn.
Chỉ khi thực hiện đến Bước 4: Linking - Kết hợp nhiều file
.o
tạo thành một chương trình hoàn chỉnh (tạo ra file thực thi ví dụ như.exe
,.out
, ...), linker sẽ giữ lại những đoạn mã thực sự được dùng trong file thực thi, và loại bỏ những đoạn mã không dùng đến, giúp cho kích thước file thực thi.exe
hoặc.out
tạo ra không tăng nhiều so với cách phía trước đó (chỉ include các header file cần thiết ví dụ nhưiostream
)
Tóm lại, qua những bước ở trên, mình đã giúp các bạn tự tin có thể kiểm chứng được việc sử dụng <bits/stdc++.h>
sẽ khiến thời gian biên dịch diễn ra lâu hơn, chứ không chỉ là nói lý thuyết suông.
2. Một số trình biên dịch không hỗ trợ
Vì <bits/stdc++.h> chỉ là một phần của GNU C++ Standard Library (libstdc++), được sử dụng trong trình biên dịch GCC (GNU Compiler Collection), chứ không phải một thư viện chuẩn trong C++, nên các bạn sẽ có thể gặp lỗi khi build chương trình trên những trình biên dịch khác, ví dụ như MSVC hay Clang.
Điển hình là hiện tại mình cũng đang dùng 1 con máy MacBook, trên đó có cài VSCode để code C++ sử dụng trình biên dịch Clang. Tuy nhiên mặc định thì trong trình biên dịch này không có thư mục bits
để chứa các file giống như stdc++.h
.
Vậy nên khi mình cố tình sử dụng #include <bits/stdc++.h>
trong code, thì sẽ gặp lỗi như hình dưới đây:
3. Tóm lại
Khi đi thi lập trình thi đấu, bạn có thể dùng <bits/stdc++.h>
, nó không làm chương trình của bạn chạy chậm đi so với include từng header file đâu, chỉ làm chậm thời gian biên dịch thôi. Mà lập trình thi đấu thì đâu có chấm thời gian biên dịch đúng không nào 🤗
Oái oăm lắm thì có chăng là trường hợp: cuộc thi mà các bạn tham gia sử dụng trình biên dịch trên máy chấm điểm không hỗ trợ <bits/stdc++.h>
. Nhưng trường hợp này rất hiếm gặp, bản thân mình cũng chưa gặp bao giờ.
Thế còn sau này đi làm dự án thì không nên dùng bits/stdc++
nhé, bị các anh gõ cho u đầu đó 🥴
4. Ngoài lề
Trong quá trình chia sẻ về bits/stdc++.h
, có một bạn học sinh từng đặt cho mình một câu hỏi khá thú vị: Tại sao người ta lại đặt tên thư mục chứa file stdc++.h
là bits
?
Câu trả lời là: Chắc các bạn cũng từng biết đến khái niệm bit 1
, bit 0
trong hệ nhị phân rồi đúng không. Ngoài khái niệm đó ra thì từ bit ở trong tiếng Anh còn có 1 nghĩa khác đó là từng mảnh nhỏ.
Và tên thư mục bits ở đây chúng ta có thể hiểu là tập hợp những "mảnh ghép nhỏ" - chính là các file như trong hình dưới đây, để tạo nên thư viện chuẩn trong C++:
Hi vọng kiến thức này hữu ích với bạn. Hẹn gặp lại 👋
All rights reserved