Bài 6 · Nâng cao · 20 phút· Cập nhật 11/06/2026
Phân tích tĩnh (clang-tidy)
Biên soạn bởi Nguyễn Anh Tuấn
Phân tích tĩnh (static analysis) cho C: cảnh báo -Wall -Wextra -Werror, clang-tidy với nhóm check bugprone/readability, cppcheck - bắt lỗi trước khi chạy.
Phân tích tĩnh đọc mã nguồn để tìm lỗi trước khi chạy - biến chưa khởi tạo, rò rỉ, so sánh nhầm, code chết. Lớp rẻ nhất và phải bật đầu tiên là cảnh báo của chính trình biên dịch:
canh-bao-trinh-dich
gcc -std=c17 -Wall -Wextra -Werror main.c -o app
# -Wall -Wextra : bat (gan) toan bo canh bao
# -Werror : coi canh bao la LOI -> buoc phai sua
# Day la "phan tich tinh mien phi" - dung bo qua canh bao! - ▸Tĩnh = tìm lỗi qua đọc mã, KHÔNG chạy chương trình (khác test).
- ▸Lớp 1 (miễn phí): -Wall -Wextra -Werror của gcc/clang.
- ▸Lớp 2 (sâu hơn): clang-tidy, cppcheck - hàng trăm check.
clang-tidy có hàng trăm check, tên dạng nhom-ten. Chọn nhóm nào bằng file .clang-tidy:
.clang-tidy
# Tat het (-*) roi bat lai cac nhom muon dung:
# clang-analyzer (dong chay: null-deref, leak...), bugprone (mau de sinh loi),
# performance (cham khong can thiet), readability (kho doc).
# LUU Y: dung dat comment # BEN TRONG khoi "Checks: >" - YAML coi do la
# van ban, "Checks" thanh rac va TAT CA check bi tat trong im lang.
Checks: >
-*,
clang-analyzer-*,
bugprone-*,
performance-*,
readability-*
WarningsAsErrors: 'clang-analyzer-*,bugprone-*' # coi nhom nay la loi - ▸bugprone-* · performance-* · modernize-* · readability-* · cert-* · clang-analyzer-*.
- ▸Checks: dùng glob; -* tắt hết rồi bật lại cho gọn.
- ▸WarningsAsErrors: ép một số nhóm thành lỗi (chặn merge).
Đoạn code dưới có vài vấn đề cài sẵn. Bật/tắt từng nhóm check và xem clang-tidy báo dòng nào:
#include <stdlib.h> #include <stdio.h> int main(void) { int x; printf("%d\n", x); ⚠ int *p = malloc(sizeof(int)); *p = 42; ⚠ int y = 0; if (y = 1) { ⚠ y = y + 86400; ⚠ } if (y > 10) ⚠ printf("lon\n"); free(p); return 0; } Tắt một nhóm → các cảnh báo thuộc nhóm đó biến mất (đúng cách Checks: hoạt động). Lưu ý: đây là bản demo vài luật - clang-tidy thật có hàng trăm check và phân tích sâu hơn nhiều.
clang-tidy dựng AST thật nên cần biết cách biên dịch (cờ -I, -std…):
chay-clang-tidy
# Cach 1: truyen co sau dau --
clang-tidy main.c -- -Iinclude -std=c17
# Cach 2 (du an CMake): sinh compile_commands.json roi chay ca du an
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
run-clang-tidy -p build
clang-tidy --fix main.c -- -std=c17 # tu sua mot so check - ▸compile_commands.json (CMake sinh) cho clang-tidy biết cờ của từng file.
- ▸run-clang-tidy chạy song song trên cả dự án.
- ▸--fix tự sửa các check có “fix-it”; CI: fail nếu còn cảnh báo.
Không công cụ tĩnh nào hoàn hảo. Khi chắc chắn một cảnh báo là báo nhầm, tắt cục bộ - đừng tắt toàn cục:
nolint
int x = ham_dac_biet(); // NOLINT(clang-analyzer-core.uninitialized)
// NOLINTNEXTLINE
int y = mot_macro_la(1, 2); Kỷ luật
Chạy clang-tidy trên một file có lỗi cài sẵn, viết .clang-tidy chọn nhóm check, và dùng --fix để tự sửa.
$ unzip 06-clang-tidy.zip && cd 06-clang-tidy # tai zip bang nut o tren $ clang-tidy buggy.c -- -std=c17 # xem cac canh bao $ cat .clang-tidy # cau hinh nhom check $ clang-tidy --fix buggy.c -- -std=c17 # tu sua mot so check $ gcc -Wall -Wextra buggy.c -o app # so voi canh bao trinh dich
Xem từng file (3) ↡
Câu hỏi thường gặp
Phân tích tĩnh đọc MÃ NGUỒN (không chạy) để tìm các mẫu dễ sai: biến chưa khởi tạo, rò rỉ, so sánh nhầm, code chết… Test thì CHẠY chương trình với dữ liệu cụ thể. Hai cái bổ sung nhau: tĩnh bắt lỗi sớm và rộng; test xác nhận hành vi đúng.
Bài Gỡ lỗi, kiểm thử & CI →Cảnh báo trình biên dịch là lớp phòng thủ đầu, gần như miễn phí - luôn bật -Wall -Wextra (và -Werror để buộc sửa). clang-tidy đi xa hơn: hàng trăm check chuyên sâu theo nhóm, một số dùng clang static analyzer phân tích theo từng đường chạy (path-sensitive) để bắt null-deref, use-after-free…
Tên check có dạng nhom-ten. Nhóm phổ biến: bugprone-* (mẫu dễ sinh lỗi), performance-* (chậm không cần thiết), readability-* (khó đọc), modernize-* (cú pháp cũ), cert-* (chuẩn an toàn CERT), và clang-analyzer-* (phân tích luồng chạy sâu). Bạn chọn nhóm nào bằng Checks.
Vì nó dựng AST thật - phải biết các cờ (-I include, -std, -D define). Cung cấp qua dấu -- (clang-tidy f.c -- -Iinclude) hoặc, tốt hơn, qua compile_commands.json mà CMake sinh ra (-DCMAKE_EXPORT_COMPILE_COMMANDS=ON).
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
Phân tích tĩnh khác kiểm thử (test) ở điểm cốt lõi nào?
- 1
Tắt một nhóm
Trong công cụ ở Bước 3, tắt nhóm
readability-*. Cảnh báo nào biến mất? Còn lại mấy cái?Hoàn thành khi: Số ma thuật (86400) và thiếu
{}biến mất → còn 3 (uninitialized, null-deref, gán-trong-if). - 2
Cảnh báo rẻ nhất
Lớp phân tích tĩnh “miễn phí”, nên bật đầu tiên trong mọi dự án là gì?
Hoàn thành khi: Cảnh báo trình biên dịch:
-Wall -Wextra(và-Werrorđể buộc sửa). - 3
Đọc tên check
Check tên
bugprone-assignment-in-if-conditionthuộc nhóm nào, bắt lỗi gì?Hoàn thành khi: Nhóm
bugprone; bắt việc dùng=(gán) thay vì==trong điều kiệnif. - 4
compile_commands.json
Vì sao clang-tidy cần
compile_commands.json(hoặc cờ sau--)? Sinh nó bằng CMake thế nào?Hoàn thành khi: Nó dựng AST thật nên cần cờ biên dịch (
-I,-std,-D). Sinh:cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON. - 5
False positive
Một dòng bị báo nhầm. Tắt cảnh báo CHỈ cho dòng đó bằng cách nào?
Hoàn thành khi: Thêm
// NOLINT(hoặc// NOLINT(ten-check)) cuối dòng, hoặc// NOLINTNEXTLINEphía trên. - 6
Tĩnh hay test
Phân tích tĩnh có thay thế được unit test không? Vì sao?
Hoàn thành khi: Không. Tĩnh tìm mẫu dễ sai mà không chạy; test xác nhận HÀNH VI đúng với dữ liệu thật. Cần cả hai.