+8

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

image.png

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)

bitsstdcpp.png

Đ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 file iostream.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 file stdc++.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 code test.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ủa iostream.h cùng các header file liên quan đến nó và phần code cout << "Hello, world!" của mình. Tổng cộng sẽ rơi vào khoảng 26.562 dòng code. Và khi mình thử search từ "vector" để xem trong này có bị include nội dung header file vector.h hay không, thì đương nhiên kết quả sẽ là Not found, vì mình chỉ #include <iostream> thôi mà:

    image.png

  • 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ở file test.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 file stdc++.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 file test.i lúc này có tới 104.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 đó.

    image.png

    image.png

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

image.png

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++.hbits?

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

image.png

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí