Menu
CoddyTech

Trình biên dịch là gì?

Trình biên dịch (compiler) là một chương trình dịch mã nguồn viết bằng một ngôn ngữ lập trình sang mã máy, hoặc sang một dạng cấp thấp hơn như bytecode, trước khi chương trình chạy. Chương trình đã dịch sau đó chạy được mà không cần mã nguồn gốc.

Tác giả Kevin Spektor, Co-founder & CTO

Cập nhật 24 tháng 9, 2026

Mã nguồn
  1. 1print("Hello")
  2. 2total = 2 + 3
  3. 3print(total)
  4. 4print("Done")
Đầu ra
Số lần biên dịch0
Số lần chạy0

Biên dịch một lần, rồi chạy chương trình bao nhiêu lần tùy thích: bộ đếm lần chạy tăng lên, bộ đếm biên dịch thì không. Bật lỗi gõ sai lên, trình biên dịch sẽ từ chối cả chương trình, nên không có gì được in ra.

Lưu chương trình C này thành hello.c:

#include <stdio.h>

int main(void) {
    int price = 4;
    printf("Total: %d\n", price * 3);
    return 0;
}

Rồi gõ gcc hello.c -o hello. Trong giây lát chẳng thấy gì xảy ra, sau đó một tệp mới tên hello xuất hiện cạnh mã nguồn của bạn. Tệp đó là đầu ra của trình biên dịch: mã máy mà bộ xử lý chạy được. Xóa hello.c đi thì ./hello vẫn chạy, vì mọi thứ chương trình cần đều đã được dịch xong.

gcc hello.c -o hello
./hello
Total: 12

Chưa cài gcc? Dán hello.c vào trình biên dịch C online.

Trình biên dịch hoạt động thế nào, từng bước

Trình biên dịch đọc mã nguồn như văn bản thuần và xử lý nó qua nhiều giai đoạn. Lấy một dòng duy nhất total = price * 3:

  1. Phân tích từ vựng. Văn bản được tách thành các token, tức các "từ" của ngôn ngữ: tên total, toán tử =, tên price, toán tử *, số 3. Khoảng trắng và chú thích bị loại bỏ ở bước này.
  2. Phân tích cú pháp. Các token được sắp xếp thành một cây cú pháp ghi lại cấu trúc: một phép gán có giá trị là phép nhân giữa price và 3. Code vi phạm ngữ pháp của ngôn ngữ sẽ dừng ở đây với một lỗi cú pháp.
  3. Phân tích ngữ nghĩa. Trình biên dịch kiểm tra ý nghĩa: price đã được khai báo chưa, các kiểu có tương thích không, hàm được gọi có tồn tại không.
  4. Tối ưu. Chương trình được viết lại để chạy nhanh hơn hoặc tốn ít bộ nhớ hơn mà không thay đổi việc nó làm. x * 2 có thể thành một lệnh dịch bit duy nhất, và code không bao giờ chạy tới sẽ bị loại bỏ.
  5. Sinh mã. Trình biên dịch ghi đầu ra cho một nền tảng đích: mã máy x86-64 hoặc ARM, hoặc bytecode cho một máy ảo.

Với C và C++ còn một bước cuối, liên kết (linking), ghép các tệp đã biên dịch của bạn với code thư viện (như printf của thư viện chuẩn C) thành một tệp thực thi. gcc tự chạy trình liên kết cho bạn, vì thế trông như chỉ có một lệnh.

Python cung cấp sẵn hai giai đoạn đầu dưới dạng module chuẩn, nên bạn có thể xem chúng xử lý dữ liệu thật:

NAME 'total'
OP '='
NAME 'price'
OP '*'
NUMBER '3'
Assign(
  targets=[
    Name(id='total', ctx=Store())],
  value=BinOp(
    left=Name(id='price', ctx=Load()),
    op=Mult(),
    right=Constant(value=3)))

Cái cây đó là thứ phần còn lại của trình biên dịch làm việc dựa vào. Nó không còn quan tâm đến khoảng trắng hay xuống dòng, chỉ quan tâm đến cấu trúc: gán cho total kết quả của phép nhân price với 3.

Thời điểm biên dịch: lỗi được phát hiện trước khi chương trình chạy

Vì trình biên dịch đọc toàn bộ chương trình trước khi bất kỳ phần nào chạy, nó có thể từ chối lỗi từ sớm. Trong ví dụ này, không dòng print nào trong ba dòng bên trong source được chạy, kể cả dòng đầu tiên, vì trình biên dịch của Python từ chối đoạn văn bản trước khi bắt đầu thực thi:

Rejected before running anything:
  '(' was never closed (line 3)

Trình biên dịch của các ngôn ngữ kiểu tĩnh bắt được nhiều hơn lỗi cú pháp. Dòng C này, trong một tệp tên bad.c, đúng ngữ pháp, nhưng lại lưu văn bản vào một biến số nguyên:

int count = "five";

GCC 14 trở lên dừng lại và báo lỗi:

bad.c:4:17: error: initialization of 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]

Cùng lỗi đó trong Python chỉ lộ ra khi dòng ấy chạy. Bắt được nó ở thời điểm biên dịch là một lý do khiến các nhóm lớn chọn ngôn ngữ biên dịch, kiểu tĩnh. Tuy vậy, trình biên dịch không bắt được mọi thứ: chia cho một số hóa ra bằng 0, hay đọc một tệp không tồn tại, vẫn thất bại lúc chạy.

Ví dụ về trình biên dịch

Trình biên dịchĐọcTạo ra
GCC, ClangC, C++Mã máy cho x86-64, ARM và các kiến trúc khác
MSVC (Microsoft Visual C++)C, C++Tệp thực thi Windows (.exe, .dll)
javacJavaBytecode JVM (tệp .class)
rustcRustMã máy
go buildGoMột tệp thực thi duy nhất
tscTypeScriptJavaScript

Hai hàng trong bảng cho thấy "mã máy" không phải đầu ra duy nhất. javac tạo ra bytecode do Máy ảo Java chạy, một phần của môi trường runtime Java. tsc biến TypeScript thành JavaScript, một ngôn ngữ bậc cao khác; trình biên dịch làm việc này thường được gọi là transpiler.

Mã máy đã biên dịch gắn với một dòng CPU và một hệ điều hành. Chương trình biên dịch cho Windows trên x86-64 không chạy được trên máy Mac dùng chip Apple dòng M; bạn phải biên dịch lại cùng mã nguồn đó cho từng nền tảng đích.

Trình biên dịch hay trình thông dịch

Trình thông dịch chạy mã nguồn trực tiếp thay vì tạo ra một tệp chương trình riêng, đó là cách Python và Ruby thường được chạy. Hầu hết công cụ ngôn ngữ hiện đại dùng cả hai ý tưởng: CPython biên dịch tệp của bạn sang bytecode rồi thông dịch nó, còn JVM và engine V8 của JavaScript biên dịch code hay dùng sang mã máy trong lúc chương trình đang chạy. Bảng so sánh đầy đủ nằm ở trang trình biên dịch và trình thông dịch.

Những hiểu lầm thường gặp

  • "Biên dịch được là chạy đúng." Trình biên dịch kiểm tra chương trình có hợp lệ không, chứ không kiểm tra nó có đúng không. Lỗi logic và lỗi lúc chạy như chia cho 0 vẫn lọt qua.
  • "Python không được biên dịch." CPython biên dịch mọi tệp sang bytecode trước khi chạy. Các tệp .pyc trong thư mục __pycache__ chính là bytecode đó, được lưu lại cho những module chương trình của bạn import để lần chạy sau bỏ qua được bước này.
  • "Trình biên dịch online là một trình biên dịch." Những trang chạy code ngay trên trình duyệt gửi code lên một máy chủ chạy toàn bộ chuỗi công cụ (trình biên dịch, trình liên kết và chính chương trình) rồi trả về đầu ra. Trình biên dịch chỉ là một phần trong đó.
  • "Code đã biên dịch chạy được ở mọi nơi." Nó chạy trên CPU và hệ điều hành mà nó được build cho. Tính di động đến từ việc biên dịch lại, hoặc từ một máy ảo như JVM.

Học tiếp ở đâu

Tài liệu C cho thấy trình biên dịch được dùng hằng ngày ra sao: cách biên dịch và chạy C đi qua tiền xử lý, biên dịch, hợp dịch và liên kết bằng các lệnh gcc thật. Để xem cách chạy code còn lại, hãy đọc trình biên dịch và trình thông dịch, rồi môi trường runtime để biết những gì bao quanh chương trình khi nó bắt đầu chạy. Khóa học C là nơi tốt để luyện tập với một ngôn ngữ biên dịch.

Câu hỏi thường gặp

Trình biên dịch gồm những thành phần nào?

Trình biên dịch thường được mô tả gồm ba phần. Phần front end đọc mã nguồn, tách nó thành token, phân tích thành cây cú pháp và kiểm tra kiểu. Phần middle end tối ưu một dạng trung gian của chương trình, còn phần back end sinh mã máy hoặc bytecode cho CPU đích.

Làm sao một trình biên dịch tự biên dịch được chính nó?

Nhờ kỹ thuật bootstrapping. Phiên bản đầu tiên của trình biên dịch được viết bằng một ngôn ngữ có sẵn, hoặc được một trình biên dịch cũ hơn biên dịch, rồi được dùng để biên dịch phiên bản tiếp theo viết bằng chính ngôn ngữ mới. Ví dụ, GCC được viết chủ yếu bằng C và C++ và được build bằng một trình biên dịch C++ có sẵn, thường là một bản GCC trước đó.

Trình biên dịch và trình hợp dịch khác nhau thế nào?

Trình biên dịch dịch một ngôn ngữ bậc cao như C hay Rust, nơi một dòng code có thể thành nhiều lệnh máy. Trình hợp dịch (assembler) dịch hợp ngữ, nơi mỗi dòng gần như tương ứng một một với một lệnh máy. Các trình biên dịch như GCC thường sinh ra hợp ngữ trước rồi chuyển nó cho trình hợp dịch.

Trình biên dịch JIT là gì?

Trình biên dịch just-in-time (JIT) dịch code sang mã máy trong lúc chương trình đang chạy, thay vì trước đó. Nó theo dõi phần nào chạy thường xuyên nhất và biên dịch những phần đó, nên một chương trình chạy lâu sẽ càng lúc càng nhanh. JVM, engine V8 của JavaScript và PyPy đều dùng biên dịch JIT.

Có cần cài trình biên dịch để học lập trình không?

Lúc đầu thì không. Các trình soạn thảo trên trình duyệt và playground online chạy trình biên dịch hoặc trình thông dịch trên máy chủ cho bạn. Khi chuyển sang máy của mình, C và C++ cần một trình biên dịch như GCC hoặc Clang, Java cần JDK, còn Python cần trình thông dịch Python.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu