Nếu bạn là một lập trình viên đang bắt đầu hành trình của mình trên hệ điều hành Linux, việc làm quen với GCC là một trong những bước đi đầu tiên và quan trọng nhất. GCC không chỉ là một trình biên dịch; nó là công cụ nền tảng, là trái tim của hệ sinh thái phát triển phần mềm trên Linux, đặc biệt là với ngôn ngữ C và C++. Việc nắm vững cách cài đặt, sử dụng và tối ưu GCC sẽ mở ra một thế giới lập trình đầy tiềm năng, giúp bạn biến những dòng mã của mình thành các chương trình thực thi mạnh mẽ. Bài viết này sẽ là kim chỉ nam chi tiết, dẫn dắt bạn qua từng bước từ cơ bản đến nâng cao, giúp bạn tự tin làm chủ công cụ thiết yếu này.
Giới thiệu về GCC trên Linux
GCC, viết tắt của GNU Compiler Collection, là một bộ trình biên dịch mã nguồn mở được phát triển bởi dự án GNU. Ban đầu, nó chỉ được tạo ra để biên dịch ngôn ngữ C, nhưng qua nhiều năm phát triển, GCC đã mở rộng để hỗ trợ nhiều ngôn ngữ lập trình khác như C++, Objective-C, Fortran, Ada, và Go. Trên hệ điều hành Linux, GCC đóng vai trò trung tâm trong việc phát triển phần mềm. Hầu hết các chương trình và chính nhân Kernel Linux (Kernel là gì) đều được biên dịch bằng GCC.
Lý do GCC trở nên phổ biến và được ưa chuộng trong cộng đồng lập trình C/C++ trên Linux đến từ nhiều yếu tố. Đầu tiên, nó hoàn toàn miễn phí và là phần mềm mã nguồn mở, cho phép mọi người tự do sử dụng, sửa đổi và phân phối. Thứ hai, GCC có tính tương thích cao, hỗ trợ nhiều kiến trúc vi xử lý và tuân thủ chặt chẽ các tiêu chuẩn ngôn ngữ. Cuối cùng, nó cung cấp một hệ thống các tùy chọn (flags) mạnh mẽ, giúp lập trình viên tối ưu hóa hiệu suất, kiểm soát quá trình biên dịch và gỡ lỗi một cách hiệu quả.

Trong bài viết này, Bùi Mạnh Đức sẽ cùng bạn khám phá chi tiết về GCC. Chúng ta sẽ bắt đầu từ cách cài đặt GCC trên các bản phân phối Linux phổ biến như Ubuntu và CentOS. Sau đó, chúng ta sẽ đi sâu vào cách sử dụng dòng lệnh để biên dịch các chương trình C và C++, tìm hiểu các tùy chọn biên dịch quan trọng, và khắc phục những lỗi thường gặp. Cuối cùng là những mẹo và lưu ý hữu ích để bạn làm việc với GCC một cách chuyên nghiệp hơn.
Cài đặt GCC trên các bản phân phối Linux phổ biến
Việc cài đặt GCC trên Linux khá đơn giản vì nó là một phần không thể thiếu của hầu hết các môi trường phát triển. Tùy thuộc vào bản phân phối bạn đang sử dụng, bạn sẽ dùng các trình quản lý gói khác nhau như APT cho Debian/Debian/Ubuntu hay YUM/DNF cho RHEL/CentOS/Fedora. Hãy cùng xem qua cách thực hiện trên từng hệ thống.
Cài đặt GCC trên Ubuntu
Đối với Ubuntu và các bản phân phối dựa trên Debian, cách dễ nhất để cài đặt GCC cùng với các công cụ phát triển cần thiết khác là sử dụng gói build-essential. Gói này không chỉ chứa GCC mà còn có cả G++ (trình biên dịch C++), make và các thư viện quan trọng khác.
Trước tiên, hãy mở Terminal và cập nhật danh sách gói của hệ thống để đảm bảo bạn cài đặt phiên bản mới nhất. Sử dụng lệnh sau:
sudo apt update
Sau khi quá trình cập nhật hoàn tất, bạn có thể tiến hành cài đặt gói build-essential bằng lệnh:
sudo apt install build-essential
Hệ thống sẽ yêu cầu bạn xác nhận việc cài đặt. Nhấn Y và Enter để tiếp tục. Quá trình này có thể mất vài phút tùy thuộc vào tốc độ mạng của bạn.

Khi cài đặt xong, bạn có thể kiểm tra phiên bản GCC đã được cài đặt để xác nhận mọi thứ hoạt động chính xác. Chạy lệnh sau trong Terminal:
gcc --version
Lệnh này sẽ hiển thị thông tin về phiên bản GCC hiện tại trên máy của bạn, ví dụ như gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0. Điều này khẳng định rằng GCC đã sẵn sàng để bạn sử dụng.
Cài đặt GCC trên CentOS và Fedora
Trên các bản phân phối thuộc họ Red Hat như CentOS và Fedora, quy trình cài đặt cũng tương tự nhưng sử dụng trình quản lý gói khác là yum (trên CentOS 7 trở về trước) hoặc dnf (trên CentOS 8+, Fedora). Thay vì một gói lẻ, chúng ta thường cài đặt một nhóm gói có tên là “Development Tools”.
Đối với người dùng CentOS 7 hoặc các phiên bản cũ hơn, bạn sẽ sử dụng yum. Mở Terminal và chạy lệnh sau:
sudo yum groupinstall 'Development Tools'
Đối với người dùng CentOS 8, Fedora hoặc các bản phân phối mới hơn sử dụng dnf, lệnh sẽ là:
sudo dnf groupinstall 'Development Tools'

Nhóm gói này bao gồm GCC, G++, make, gdb (trình gỡ lỗi), và nhiều công cụ thiết yếu khác cho việc lập trình. Trình quản lý gói sẽ tự động giải quyết các phụ thuộc và cài đặt mọi thứ cần thiết.
Sau khi quá trình cài đặt hoàn tất, bạn cũng có thể xác minh phiên bản GCC bằng cách sử dụng cùng một lệnh như trên Ubuntu:
gcc --version
Kết quả sẽ cho bạn biết phiên bản GCC đang chạy trên hệ thống CentOS hoặc Fedora của bạn. Nếu bạn cần cập nhật GCC lên phiên bản mới hơn, bạn có thể chạy lại lệnh groupinstall hoặc sử dụng sudo yum update / sudo dnf update để nâng cấp tất cả các gói hệ thống, bao gồm cả các công cụ phát triển.
Cách sử dụng GCC để biên dịch mã nguồn trên Linux
Sau khi đã cài đặt thành công GCC, bước tiếp theo là học cách sử dụng nó để biến những tệp mã nguồn .c hoặc .cpp thành các chương trình có thể thực thi. Quá trình này được thực hiện hoàn toàn trên giao diện dòng lệnh (Terminal), một kỹ năng cơ bản nhưng cực kỳ quan trọng đối với mọi lập trình viên làm việc trên Linux.
Biên dịch chương trình C với GCC trên dòng lệnh
Biên dịch một chương trình C với GCC rất trực quan. Cú pháp cơ bản nhất là gcc [tên_file_nguồn]. Hãy bắt đầu với một ví dụ kinh điển: chương trình “Hello, World!”.
Đầu tiên, hãy tạo một tệp tin có tên hello.c bằng trình soạn thảo văn bản yêu thích của bạn (ví dụ: nano, vim, hoặc gedit).
nano hello.c
Sau đó, nhập đoạn mã C đơn giản sau vào tệp:
#include <stdio.h>
int main() {
printf("Chào mừng bạn đến với Bùi Mạnh Đức!\n");
return 0;
}
Lưu tệp và thoát khỏi trình soạn thảo. Bây giờ, trong Terminal, bạn hãy sử dụng GCC để biên dịch tệp hello.c. Cú pháp đầy đủ và tốt nhất là chỉ định tên tệp đầu ra bằng tùy chọn -o.
gcc hello.c -o hello

Hãy phân tích lệnh này:
gcc: Gọi trình biên dịch GCC.
hello.c: Là tệp mã nguồn đầu vào.
-o hello: Là một tùy chọn (flag). -o có nghĩa là “output” (đầu ra). hello là tên của tệp thực thi mà bạn muốn tạo ra. Nếu bạn không sử dụng tùy chọn -o, GCC sẽ mặc định tạo ra một tệp có tên là a.out.
Nếu không có lỗi nào trong mã của bạn, lệnh sẽ thực thi một cách im lặng và bạn sẽ thấy một tệp mới tên là hello trong cùng thư mục. Để chạy chương trình, bạn gõ lệnh sau:
./hello
Dấu ./ là cần thiết để chỉ cho shell biết rằng bạn muốn chạy một tệp thực thi trong thư mục hiện tại. Kết quả trên màn hình sẽ là: Chào mừng bạn đến với Bùi Mạnh Đức!
Biên dịch và chạy chương trình C++ với g++
Đối với mã nguồn C++, quy trình cũng gần như tương tự, nhưng chúng ta nên sử dụng lệnh g++ thay vì gcc. Mặc dù về mặt kỹ thuật, gcc có thể biên dịch mã C++ nếu bạn liên kết đúng thư viện, g++ được thiết kế để tự động làm việc này, giúp quá trình trở nên đơn giản hơn rất nhiều.
Sự khác biệt cơ bản giữa gcc và g++ là g++ được cấu hình mặc định để biên dịch các tệp .cpp và tự động liên kết (link) với Thư viện Chuẩn C++ (C++ Standard Library). Nếu bạn dùng gcc để biên dịch mã C++, bạn sẽ phải tự thêm tùy chọn -lstdc++ để liên kết thư viện này, nếu không sẽ gặp lỗi.
Hãy tạo một tệp C++ có tên greeting.cpp.
nano greeting.cpp
Nội dung tệp như sau:
#include <iostream>
int main() {
std::cout << "Học C++ trên Linux thật tuyệt vời!" << std::endl;
return 0;
}
Lưu tệp lại. Bây giờ, biên dịch nó bằng g++:
g++ greeting.cpp -o greeting
-Step-16-Version-3.jpg)
Lệnh này sẽ tạo ra một tệp thực thi có tên greeting. Để chạy nó, bạn cũng sử dụng cú pháp tương tự:
./greeting
Màn hình sẽ hiển thị: Học C++ trên Linux thật tuyệt vời!. Như bạn thấy, quy trình biên dịch C++ với g++ cũng đơn giản và trực tiếp như biên dịch C với gcc.
Các lệnh cơ bản trong GCC giúp biên dịch và chạy chương trình
Để khai thác hết sức mạnh của GCC, bạn cần phải làm quen với các tùy chọn (flags) của nó. Các tùy chọn này cho phép bạn kiểm soát mọi khía cạnh của quá trình biên dịch, từ tối ưu hóa mã, hiển thị cảnh báo, đến tạo thông tin gỡ lỗi. Bên cạnh đó, việc kết hợp GCC với các công cụ khác như make sẽ giúp tự động hóa quy trình làm việc của bạn.
Các tùy chọn thường dùng trong GCC
Dưới đây là một số tùy chọn quan trọng và được sử dụng thường xuyên nhất khi làm việc với GCC:
- -o <tên_file>: Như đã đề cập, tùy chọn này dùng để chỉ định tên của tệp đầu ra (tệp thực thi). Ví dụ:
gcc main.c -o my_program.
- -Wall: Tùy chọn này bật “tất cả” các cảnh báo (Warning all). GCC sẽ hiển thị các cảnh báo về những đoạn mã có thể tiềm ẩn lỗi hoặc không tuân thủ chuẩn, ngay cả khi chúng không phải là lỗi cú pháp. Đây là một thực hành tốt để luôn sử dụng
-Wall nhằm viết mã sạch hơn và an toàn hơn.

- -g: Tùy chọn này yêu cầu GCC tạo ra thông tin gỡ lỗi (debug information) và nhúng nó vào tệp thực thi. Thông tin này rất cần thiết cho các trình gỡ lỗi như GDB (GNU Debugger) để có thể phân tích chương trình từng dòng lệnh, xem giá trị biến, và theo dõi luồng thực thi.
- -std=<chuẩn>: Dùng để chỉ định phiên bản tiêu chuẩn của ngôn ngữ mà bạn muốn tuân thủ. Ví dụ, để biên dịch mã C++ theo chuẩn C++17, bạn sẽ dùng
g++ main.cpp -std=c++17. Tương tự, với mã C theo chuẩn C11, bạn dùng gcc main.c -std=c11.
- -c: Tùy chọn này chỉ biên dịch mã nguồn thành tệp đối tượng (object file, có đuôi
.o) mà không thực hiện bước liên kết (linking) để tạo tệp thực thi. Điều này hữu ích khi bạn làm việc với các dự án lớn có nhiều tệp nguồn.
- -O<level>: Dùng để tối ưu hóa mã. Mức độ tối ưu hóa có thể là
-O1, -O2, -O3 hoặc -Os (tối ưu hóa kích thước). Ví dụ, -O2 là mức tối ưu hóa phổ biến, giúp cải thiện hiệu suất chương trình mà không làm tăng quá nhiều thời gian biên dịch.
Cách sử dụng các tùy chọn này là kết hợp chúng trong cùng một lệnh. Ví dụ, một lệnh biên dịch C++ hoàn chỉnh và chuyên nghiệp có thể trông như sau:
g++ -std=c++17 -Wall -g -O2 main.cpp -o my_app
Các lệnh hỗ trợ khác: make, ld và debugging tools
Khi dự án của bạn lớn dần, việc gõ lại lệnh biên dịch với hàng tá tệp nguồn và tùy chọn sẽ trở nên cồng kềnh và dễ sai sót. Đây là lúc công cụ make phát huy tác dụng.
make là một công cụ tự động hóa quá trình biên dịch. Bạn chỉ cần định nghĩa các quy tắc biên dịch trong một tệp đặc biệt tên là Makefile. Khi bạn chạy lệnh make, nó sẽ đọc Makefile, kiểm tra xem tệp nguồn nào đã thay đổi kể từ lần biên dịch cuối cùng, và chỉ biên dịch lại những gì cần thiết. Điều này giúp tiết kiệm thời gian và đảm bảo tính nhất quán.
Một ví dụ về Makefile đơn giản:
# Đây là một Makefile đơn giản
my_app: main.o utils.o
g++ -o my_app main.o utils.o
main.o: main.cpp
g++ -c main.cpp
utils.o: utils.cpp
g++ -c utils.cpp
clean:
rm -f *.o my_app
Với Makefile này, bạn chỉ cần gõ make để biên dịch toàn bộ dự án và make clean để dọn dẹp các tệp đã tạo.
ld (GNU Linker) là công cụ mà GCC sử dụng ở bước cuối cùng của quá trình biên dịch để liên kết các tệp đối tượng (.o) và thư viện lại với nhau để tạo ra tệp thực thi cuối cùng. Thông thường, bạn không cần gọi ld trực tiếp vì GCC đã tự động làm việc này.
gdb (GNU Debugger) là trình gỡ lỗi mạnh mẽ nhất trên Linux. Để sử dụng gdb, bạn phải biên dịch chương trình của mình với tùy chọn -g. Sau đó, bạn có thể khởi chạy phiên gỡ lỗi bằng lệnh gdb ./my_app. Bên trong gdb, bạn có thể đặt điểm dừng (breakpoints), chạy chương trình từng bước, kiểm tra giá trị của biến và nhiều hơn thế nữa.

Những vấn đề thường gặp và cách khắc phục
Trong quá trình làm việc với GCC, không thể tránh khỏi việc gặp phải các lỗi. Hiểu rõ nguyên nhân và cách khắc phục những lỗi phổ biến sẽ giúp bạn tiết kiệm rất nhiều thời gian và công sức. Dưới đây là hai trong số những vấn đề thường gặp nhất.
Lỗi thiếu thư viện hoặc package khi biên dịch
Một trong những lỗi phổ biến nhất là lỗi liên kết (linker error) xảy ra khi GCC không tìm thấy một thư viện mà mã nguồn của bạn cần sử dụng. Lỗi này thường có dạng undefined reference to ... hoặc cannot find -l<tên_thư_viện>.
Ví dụ, bạn đang viết một chương trình sử dụng thư viện toán học math.h và gọi hàm sqrt(). Khi biên dịch với lệnh gcc my_math_program.c -o my_math_program, bạn có thể gặp lỗi. Nguyên nhân là vì mặc dù bạn đã #include <math.h>, bạn vẫn cần phải yêu cầu GCC liên kết với thư viện toán học một cách tường minh. Lệnh đúng phải là:
gcc my_math_program.c -o my_math_program -lm
Tùy chọn -lm chỉ thị cho trình liên kết (linker) tìm và liên kết thư viện m (thư viện toán học).
Một trường hợp khác là khi bạn sử dụng một thư viện bên ngoài (ví dụ: libcurl để làm việc với HTTP). Nếu bạn chưa cài đặt gói phát triển (development package) của thư viện đó, GCC sẽ không thể tìm thấy các tệp tiêu đề (.h) và tệp thư viện (.so hoặc .a). Lỗi thường là fatal error: curl/curl.h: No such file or directory.

Cách khắc phục là cài đặt gói -dev (trên Debian/Ubuntu) hoặc -devel (trên CentOS/Fedora) tương ứng. Ví dụ, để sửa lỗi trên, bạn cần chạy:
# Trên Ubuntu/Debian
sudo apt install libcurl4-openssl-dev
# Trên CentOS/Fedora
sudo dnf install libcurl-devel
Việc cài đặt gói phát triển này sẽ cung cấp đầy đủ các tệp cần thiết để GCC có thể biên dịch và liên kết chương trình của bạn thành công.
Lỗi môi trường biến PATH không đúng
Lỗi bash: gcc: command not found là một vấn đề kinh điển, đặc biệt là với người mới. Lỗi này xảy ra khi hệ điều hành không thể tìm thấy tệp thực thi gcc trong các thư mục được liệt kê trong biến môi trường PATH.
Biến PATH là một danh sách các đường dẫn thư mục mà shell (như Bash) sẽ tìm kiếm khi bạn gõ một lệnh. Để xem nội dung của biến PATH, bạn hãy chạy lệnh:
echo $PATH
Kết quả sẽ là một chuỗi các đường dẫn được ngăn cách bởi dấu hai chấm, ví dụ: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin.
Thông thường, sau khi cài đặt GCC qua apt hoặc dnf, tệp thực thi gcc sẽ nằm ở /usr/bin, một thư mục đã có sẵn trong PATH. Tuy nhiên, nếu bạn tự cài đặt GCC từ mã nguồn vào một thư mục tùy chỉnh (ví dụ: /opt/gcc-12), shell sẽ không tự động biết vị trí này.
Để khắc phục, bạn cần thêm đường dẫn đến thư mục chứa GCC vào biến PATH. Bạn có thể làm điều này tạm thời cho phiên Terminal hiện tại bằng lệnh export:
export PATH=/opt/gcc-12/bin:$PATH
Lệnh này sẽ thêm /opt/gcc-12/bin vào đầu danh sách PATH. Bây giờ, khi bạn gõ gcc, shell sẽ tìm thấy nó.
Để thay đổi này có hiệu lực vĩnh viễn, bạn cần thêm dòng export trên vào tệp cấu hình của shell, chẳng hạn như ~/.bashrc (cho Bash) hoặc ~/.zshrc (cho Zsh). Sau khi thêm dòng đó, bạn cần chạy lệnh source ~/.bashrc hoặc mở một cửa sổ Terminal mới để thay đổi có hiệu lực.
Mẹo và lưu ý khi sử dụng GCC trên Linux
Việc sử dụng thành thạo GCC không chỉ dừng lại ở việc biết các lệnh cơ bản. Để làm việc hiệu quả và chuyên nghiệp, bạn nên áp dụng các thói quen tốt và lưu ý quan trọng sau đây. Những mẹo này sẽ giúp bạn tối ưu hóa chương trình, tránh các lỗi không đáng có và đảm bảo an toàn cho hệ thống.

Luôn cập nhật phiên bản GCC: Các phiên bản mới của GCC thường xuyên được phát hành, mang theo các tính năng ngôn ngữ mới (ví dụ: hỗ trợ các chuẩn C/C++ mới nhất), cải tiến về tối ưu hóa mã và các bản vá bảo mật quan trọng. Hãy định kỳ cập nhật hệ thống của bạn (sudo apt update && sudo apt upgrade hoặc sudo dnf update) để đảm bảo bạn đang sử dụng phiên bản GCC ổn định và mới nhất.
Sử dụng flags biên dịch phù hợp: Đừng chỉ biên dịch một cách đơn giản với gcc main.c. Hãy tận dụng các tùy chọn để kiểm soát quá trình. Luôn dùng -Wall để bật cảnh báo và -g trong quá trình phát triển để gỡ lỗi dễ dàng hơn. Khi phát hành sản phẩm, hãy sử dụng các cờ tối ưu hóa như -O2 hoặc -O3 để tăng hiệu suất của chương trình. Việc chọn đúng cờ biên dịch có thể tạo ra sự khác biệt lớn về tốc độ và sự ổn định.
Backup mã nguồn thường xuyên: Đây là một lời khuyên không bao giờ cũ. Trước khi thực hiện các thay đổi lớn hoặc thử nghiệm các kỹ thuật biên dịch phức tạp, hãy đảm bảo bạn đã có một bản sao lưu của mã nguồn. Sử dụng các hệ thống quản lý phiên bản như Git là cách tốt nhất để theo dõi thay đổi và dễ dàng quay lại các phiên bản cũ nếu có sự cố.
Kiểm tra lỗi cú pháp thường xuyên: Đừng đợi đến khi viết xong hàng trăm dòng mã mới bắt đầu biên dịch. Hãy biên dịch chương trình của bạn thường xuyên sau khi hoàn thành mỗi chức năng nhỏ. Điều này giúp bạn phát hiện và sửa lỗi cú pháp ngay lập tức, khi vấn đề còn nhỏ và dễ xác định.
Tránh chạy GCC với quyền root: Trừ khi có lý do thực sự đặc biệt (ví dụ: cài đặt vào thư mục hệ thống), bạn không bao giờ nên biên dịch hoặc chạy mã nguồn của mình với quyền sudo. Việc này có thể gây ra các rủi ro bảo mật nghiêm trọng nếu mã nguồn của bạn có lỗi hoặc chứa mã độc. Hãy luôn làm việc với quyền người dùng thông thường để đảm bảo an toàn cho hệ thống.
Kết luận
Qua bài viết này, chúng ta đã cùng nhau thực hiện một hành trình chi tiết từ việc cài đặt, sử dụng đến việc tối ưu hóa và khắc phục sự cố với GCC trên hệ điều hành Linux. Bạn đã học được cách cài đặt GCC trên các bản phân phối phổ biến như Ubuntu và CentOS, nắm vững cú pháp dòng lệnh để biên dịch cả chương trình C và C++, cũng như hiểu rõ ý nghĩa của các tùy chọn biên dịch quan trọng như -o, -Wall, và -g.

Hơn thế nữa, chúng ta đã tìm hiểu về các công cụ hỗ trợ như make để tự động hóa quy trình và gdb để gỡ lỗi hiệu quả. Việc nhận biết và xử lý các lỗi thường gặp như thiếu thư viện hay sai biến môi trường PATH sẽ giúp bạn tự tin hơn khi đối mặt với các dự án phức tạp. Những mẹo và lưu ý cuối cùng là những kinh nghiệm quý báu để bạn làm việc một cách chuyên nghiệp và an toàn hơn.
GCC là một công cụ cực kỳ mạnh mẽ và linh hoạt. Cách tốt nhất để thực sự làm chủ nó là thông qua thực hành. Đừng ngần ngại mở Terminal lên, viết một vài chương trình nhỏ và thử nghiệm với các tùy chọn biên dịch khác nhau. Hãy xem việc gặp lỗi là một cơ hội để học hỏi. Để tìm hiểu sâu hơn, bạn có thể tham khảo tài liệu chính thức của GNU hoặc các diễn đàn cộng đồng lập trình. Chúc bạn có những trải nghiệm lập trình thú vị và hiệu quả trên Linux!