← C Thực Chiến · Toolchain & Kiểm soát chất lượng

Bài 5 · Nâng cao · 16 phút· Cập nhật 11/06/2026

Định dạng mã (clang-format)

Biên soạn bởi Nguyễn Anh Tuấn

Định dạng mã C tự động với clang-format: viết file .clang-format, format-on-save trong editor, kiểm tra format trong CI - diff sạch, hết tranh cãi style.

Trong một nhóm, mỗi người một kiểu thụt lề/đặt ngoặc → code rối, diff đầy thay đổi khoảng trắng vô nghĩa, và những cuộc tranh cãi style vô tận. Giải pháp: một trình định dạng tự động áp cùng một quy tắc cho tất cả - không ai phải nghĩ về style nữa.

  • Đọc dễ: mọi file trông như một người viết.
  • Diff sạch: review chỉ thấy thay đổi thật, không phải khoảng trắng.
  • Hết tranh cãi: máy quyết định style, con người lo logic.

clang-format là trình định dạng C/C++ phổ biến nhất. Nó chỉ lo phần trình bày; việc bắt lỗi tiềm ẩn là phần của phân tích tĩnh (clang-tidy). Đặt một file .clang-format ở gốc dự án để mô tả style chung:

.clang-format

# Bat dau tu mot preset, roi tinh chinh:
BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
ColumnLimit: 100
PointerAlignment: Right          # int *p
AllowShortIfStatementsOnASingleLine: false
SortIncludes: true
  • BasedOnStyle: LLVM/Google/Mozilla/WebKit/GNU/Microsoft - chọn một làm gốc.
  • Override vài tuỳ chọn cho hợp ý team (thụt lề, độ rộng cột, căn con trỏ).
  • .clang-format ở gốc dự án; clang-format tự tìm lên cây thư mục.

Chỉnh vài tuỳ chọn (như các dòng trong .clang-format) và xem mã lộn xộn được định dạng lại tức thì:

Trước (lộn xộn)
int main(void) {
int x = 5;
int* p = &x;
  if (x > 0) {
      *p = x + 1;
return *p;
  }
return 0;
}
Sau (clang-format -i)
int main(void) {
    int x = 5;
    int *p = &x;
    if (x > 0) {
        *p = x + 1;
        return *p;
    }
    return 0;
}

Đổi cấu hình ở trên (giống các dòng trong .clang-format) và xem mã được định dạng lại tức thì. Lưu ý: đây là bản demo tối giản chỉ minh hoạ thụt lề & căn con trỏ - clang-format thật phân tích cú pháp đầy đủ và làm được nhiều hơn.

Đừng định dạng bằng tay - để công cụ làm ở mọi khâu, từ lúc gõ code, lúc commit, tới lúc CI tự động:

cac-lenh

clang-format -i main.c util.c        # dinh dang TAI CHO (ghi de file)
clang-format --dry-run --Werror *.c  # CI: bao loi neu chua dung dinh dang
clang-format --style=file main.c     # dung file .clang-format o du an
  • Editor: bật “format on save” (VS Code, vim, CLion…) - format khi lưu.
  • Git: pre-commit hook hoặc git-clang-format để format phần thay đổi.
  • CI: clang-format --dry-run --Werror chặn code chưa định dạng.

Vài điều để clang-format giúp bạn thay vì gây vướng - định dạng nhất quán là cổng đầu tiên trong sáu cổng chất lượng của dự án xuất xưởng:

chua-mot-vung

// clang-format off
int bang[3][3] = {
    {1, 0, 0},
    {0, 1, 0},     // can cot bang tay -> giu nguyen
    {0, 0, 1},
};
// clang-format on

Định dạng tách khỏi logic

Khi áp clang-format lần đầu cho codebase cũ, hãy làm trong một commit RIÊNG chỉ-định-dạng. Trộn nó với thay đổi logic sẽ tạo diff khổng lồ không review nổi. Thêm hash commit đó vào .git-blame-ignore-revs để git blame nhảy qua.
Chạy trên máy bạn - kỹ năng thật cần gõ lệnh thật

Định dạng một file lộn xộn bằng clang-format, viết file .clang-format, và chạy kiểm tra kiểu CI.

$ unzip 05-clang-format.zip && cd 05-clang-format   # tai zip bang nut o tren
$ clang-format messy.c            # xem ket qua (chua ghi)
$ clang-format --dry-run --Werror messy.c   # kiem tra kieu CI -> bao loi
$ clang-format -i messy.c         # dinh dang tai cho
$ git diff messy.c
Xem từng file (3) ↡

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

Nó chỉ định dạng phần TRÌNH BÀY: khoảng trắng, thụt lề, ngắt dòng, vị trí ngoặc, căn con trỏ. Nó KHÔNG đổi logic, không đổi tên biến, không sửa bug. Chạy clang-format trên code đúng sẽ cho code đúng, chỉ gọn gàng hơn.

Bài Phân tích tĩnh (clang-tidy) →

Một preset làm điểm xuất phát: LLVM, Google, Mozilla, WebKit, GNU, Microsoft. Bạn chọn một rồi override vài tuỳ chọn (IndentWidth, ColumnLimit…). Quan trọng là cả team dùng CHUNG một style để không tranh cãi.

Có. Nên làm trong MỘT commit RIÊNG chỉ-định-dạng, tách khỏi thay đổi logic - để dễ review (bỏ qua được) và blame. Nhiều team thêm hash commit đó vào .git-blame-ignore-revs để git blame nhảy qua nó.

Bọc bằng // clang-format off// clang-format on. Dùng cho những chỗ bạn cố ý căn tay - ví dụ một bảng dữ liệu, ma trận, hay danh sách hằng số căn cột cho dễ đọc.

Chạy clang-format --dry-run --Werror (báo lỗi nếu file chưa đúng định dạng) trong pipeline; CI fail → người gửi phải format lại. Hoặc dùng pre-commit hook / git-clang-format để tự format phần thay đổi trước khi commit.

Bài Gỡ lỗi, kiểm thử & CI →

IndentWidth = số cột cho mỗi cấp thụt lề. UseTab = dùng ký tự TAB hay dấu cách. Quy ước an toàn nhất: dấu cách (UseTab: Never) với IndentWidth cố định (2 hoặc 4) - tránh lẫn lộn tab/space gây lệch hiển thị giữa các editor.

Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.

Tick những điều em tự tin làm được sau khi học bài này. 0/6

Trả lời vài câu để chắc rằng em đã nắm bài.

Câu 1/3 Điểm: 0

clang-format thay đổi điều gì trong mã nguồn?

  1. 1

    Đổi căn con trỏ

    Trong công cụ ở Bước 3, đổi PointerAlignment từ Right sang Left. Dòng “int *p” trở thành gì?

    Hoàn thành khi: int* p” - PointerAlignment Left dán * vào kiểu; Right dán vào tên biến; Middle để giữa.

  2. 2

    Đặt .clang-format ở đâu

    File .clang-format nên đặt ở đâu để áp cho cả dự án?

    Hoàn thành khi: Ở thư mục GỐC dự án. clang-format tìm ngược lên cây thư mục từ file đang format đến khi gặp .clang-format.

  3. 3

    Định dạng hay kiểm tra

    Lệnh nào ĐỊNH DẠNG tại chỗ, lệnh nào chỉ KIỂM TRA (dùng trong CI)?

    Hoàn thành khi: clang-format -i : định dạng & ghi đè file. clang-format --dry-run --Werror : chỉ kiểm tra, fail nếu chưa đúng.

  4. 4

    Phạm vi của formatter

    clang-format có sửa được một bug logic (vd quên break trong switch) không? Vì sao?

    Hoàn thành khi: Không. Nó chỉ trình bày lại code; không hiểu/đổi ngữ nghĩa. Bug logic cần test/clang-tidy/người.

  5. 5

    Commit riêng

    Vì sao nên định dạng lại codebase trong một commit riêng, không trộn với sửa logic?

    Hoàn thành khi: Để diff của thay đổi logic không bị lẫn trong hàng nghìn dòng đổi-khoảng-trắng → dễ review, dễ git blame.

  6. 6

    Chừa một bảng

    Bạn có một bảng hằng số căn cột bằng tay, không muốn clang-format phá. Dùng gì?

    Hoàn thành khi: // clang-format off// clang-format on bao quanh đoạn đó.