Bài 56.226 phút đọc
Quy ước viết mã
Sau bài này bạn sẽ làm được
- Viết tệp .clang-format cho dự án
- Đưa định dạng vào hệ thống dựng và vào tích hợp liên tục
- Biết những quy ước nào thật sự ảnh hưởng tới lỗi
- Định dạng lại một dự án cũ mà không làm hỏng lịch sử
Tranh luận về vị trí dấu ngoặc nhọn là cách tốn thời gian đội nhóm hiệu quả nhất từng được phát minh. Cách chữa không phải là thắng cuộc tranh luận, mà là giao việc đó cho một chương trình rồi không bao giờ nhắc lại.
#Thứ không nên tranh cãi
| Quyết định | Có ảnh hưởng tới lỗi không | Nên làm gì |
|---|---|---|
| Vị trí dấu ngoặc nhọn | Không | Để công cụ quyết định |
| Bốn dấu cách hay tab | Không, nếu nhất quán | Để công cụ quyết định |
| Giới hạn độ dài dòng | Không | Để công cụ quyết định |
| Khoảng trắng quanh toán tử | Không | Để công cụ quyết định |
| Con trỏ dính tên hay dính kiểu | Không | Để công cụ quyết định |
| Luôn dùng ngoặc nhọn cho if | CÓ | Bắt buộc, xem mục cuối |
| Khai báo biến ở đâu | CÓ | Sát chỗ dùng |
| Một câu lệnh mỗi dòng | CÓ | Bắt buộc |
| Gán trong điều kiện | CÓ | Cấm, hoặc bắt ngoặc kép |
#clang-format
xau.c, trước khi định dạng
#include <stdio.h>
#include <stdlib.h>
struct Diem{int x;int y;};
typedef struct{char ten[32];int tuoi;double diem;}SinhVien;
int tong( int *a,int n ){int s=0;for(int i=0;i<n;i++){s+=a[i];}return s;}
static void in( SinhVien *sv,int n )
{
for(int i=0;i<n;i++)
{
if(sv[i].diem>=8.0){printf("%s: gioi\n",sv[i].ten);}
else if(sv[i].diem>=6.5) printf("%s: kha\n",sv[i].ten);
else {
printf("%s: trung binh\n",sv[i].ten);
}
}
}
int main(void){SinhVien sv[]={{"an",20,8.5},{"binh",21,7.0},{"chi",20,5.5}};
int a[]={1,2,3,4,5};
printf("%d\n",tong(a,5));in(sv,3);return 0;}terminal
clang-format --version
clang-format version 15.0.0
# -i sua tai cho, khong co -i thi in ra man hinh
clang-format -i dep.c
dep.c, sau khi định dạng
#include <stdio.h>
#include <stdlib.h>
struct Diem {
int x;
int y;
};
typedef struct {
char ten[32];
int tuoi;
double diem;
} SinhVien;
int tong(int *a, int n) {
int s = 0;
for (int i = 0; i < n; i++) {
s += a[i];
}
return s;
}
static void in(SinhVien *sv, int n) {
for (int i = 0; i < n; i++) {
if (sv[i].diem >= 8.0) {
printf("%s: gioi\n", sv[i].ten);
} else if (sv[i].diem >= 6.5)
printf("%s: kha\n", sv[i].ten);
else {
printf("%s: trung binh\n", sv[i].ten);
}
}
}
int main(void) {
SinhVien sv[] = {{"an", 20, 8.5}, {"binh", 21, 7.0}, {"chi", 20, 5.5}};
int a[] = {1, 2, 3, 4, 5};
printf("%d\n", tong(a, 5));
in(sv, 3);
return 0;
}#Tệp cấu hình
.clang-format, đặt ở gốc dự án
---
Language: Cpp # C dung chung bo may voi C++
BasedOnStyle: LLVM # nen tang, roi ghi de ben duoi
IndentWidth: 4
ColumnLimit: 88
AlignConsecutiveDeclarations: true # can le cot ten bien
AlignConsecutiveAssignments: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
BreakBeforeBraces: Attach # ngoac mo cung dong
PointerAlignment: Right # char *s, khong phai char* s
SpaceAfterCStyleCast: false
SortIncludes: true| BasedOnStyle | Nguồn gốc | Đặc điểm nổi bật |
|---|---|---|
| LLVM | Dự án LLVM | 2 dấu cách, 80 cột, ngoặc cùng dòng |
| Hướng dẫn của Google | 2 dấu cách, 80 cột, gần LLVM | |
| GNU | Dự án GNU | Ngoặc xuống dòng và thụt vào, ít người thích |
| Mozilla | Firefox | 2 dấu cách, ngoặc hàm xuống dòng |
| WebKit | Safari | 4 dấu cách, ngoặc hàm xuống dòng |
| Microsoft | Visual Studio | 4 dấu cách, ngoặc luôn xuống dòng |
#EditorConfig
.editorconfig, đặt ở gốc dự án
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
[*.{c,h}]
indent_size = 4
max_line_length = 88
[Makefile]
indent_style = tab # Make BAT BUOC dung tab
[*.{yml,yaml,json}]
indent_size = 2
[*.md]
trim_trailing_whitespace = false # hai dau cach cuoi dong = xuong dong#Đưa vào quy trình
Makefile
SRC := $(shell find src include -name '*.c' -o -name '*.h')
.PHONY: dinh-dang kiem-dinh-dang
dinh-dang:
clang-format -i $(SRC)
kiem-dinh-dang:
clang-format --dry-run -Werror $(SRC)
kiem-tra: kiem-dinh-dang canh-bao kiem-thu.github/workflows/kiem-tra.yml
name: kiem tra
on: [push, pull_request]
jobs:
dinh-dang:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: cai clang-format
run: sudo apt-get update && sudo apt-get install -y clang-format
- name: kiem tra dinh dang
run: clang-format --dry-run -Werror $(git ls-files '*.c' '*.h')
dung-va-kiem-thu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make kiem-tra#Quy ước thật sự gây lỗi
Tự làm thử
- Viết một tệp
.clang-formatcho dự án của bạn, bắt đầu từ--dump-configcủa LLVM. - Chạy
clang-format --dry-run -Werrortrên dự án của bạn và đếm số tệp không đạt. - Thêm mục tiêu
dinh-dangvàkiem-dinh-dangvào Makefile. - Định dạng lại toàn bộ dự án trong một commit riêng và thiết lập
.git-blame-ignore-revs. - Viết
.editorconfigvà.gitattributescho dự án. - Tái hiện lỗi
goto failbằng một hàm nhỏ và xác nhận-Wmisleading-indentationbắt được nó.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Định dạng là chuyện thẩm mỹ; giao cho
clang-formatvà không tranh cãi nữa. clang-formatkhông bao giờ đổi ý nghĩa mã, nên định dạng lại là thao tác an toàn; nó cũng không tự thêm ngoặc nhọn.--dry-run -Werrorlà chế độ kiểm tra dùng cho CI.- Định dạng lại dự án cũ trong một commit riêng, kèm
.git-blame-ignore-revs. - Bốn quy ước có ảnh hưởng thật: luôn dùng ngoặc nhọn, khai báo sát chỗ dùng, một lệnh mỗi dòng, và không gán ẩn trong điều kiện.