Bài 36.126 phút đọc
Bốn giai đoạn và cách xem từng cái
Sau bài này bạn sẽ làm được
- Kể bốn giai đoạn và tệp ra của từng giai đoạn
- Dùng -E, -S, -c, -save-temps để xem file trung gian
- Dùng -v, -###, -H để hiểu gcc gọi gì
- In mọi macro tiền định nghĩa bằng -dM
Một lệnh gcc main.c -o mainthật ra chạy bốn chương trình nối tiếp nhau. Hiểu bốn giai đoạn đó cho bạn công cụ để trả lời "vì sao nó không dịch được" ở đúng bước gây lỗi, thay vì đoán mò.
#Bốn giai đoạn
Tiền xử lý (cpp)
Xử lý mọi dòng bắt đầu bằng#: dán nội dung#include, thay#define, chọn nhánh#if. Ra: một tệp.ichỉ còn C thuần, không còn chỉ thị tiền xử lý.Biên dịch (cc1)
Dịch C thành assembly cho kiến trúc đích. Đây là bước làm phần lớn công việc: phân tích cú pháp, kiểm tra kiểu, tối ưu. Ra: một tệp.s.Hợp dịch (as)
Dịch assembly thành mã máy, đóng vào tệp đối tượng. Ra: một tệp.o, có mã máy nhưng địa chỉ các hàm ngoài còn để trống.Liên kết (ld)
Ghép các tệp.ovà thư viện lại, điền các địa chỉ còn trống, tạo tệp thực thi. Ra: chương trình chạy được.
main.c
| cpp (tien xu ly) -E dung o day
main.i
| cc1 (bien dich) -S dung o day
main.s
| as (hop dich) -c dung o day
main.o
| ld (lien ket) (mac dinh chay het)
main.exe#Xem từng bước
vd.c
#include <stdio.h>
#define BINH_PHUONG(x) ((x) * (x))
int tong_binh_phuong(int n) {
int s = 0;
for (int i = 1; i <= n; ++i) s += BINH_PHUONG(i);
return s;
}
int main(void) {
printf("%d\n", tong_binh_phuong(5));
return 0;
}terminal
# -E: xem macro DA duoc thay chua
gcc -std=c11 -E vd.c | grep -A3 'tong_binh_phuong(int'
int tong_binh_phuong(int n) {
int s = 0;
for (int i = 1; i <= n; ++i) s += ((i) * (i));
return s;terminal
# -S: xem assembly. Dem so lenh nhan va cong trong vong lap
gcc -std=c11 -O0 -S vd.c -o vd.s
grep -c 'imull\|addl' vd.s
3
#save-temps
terminal
# Giu TAT CA file trung gian trong mot lan dich
gcc -std=c11 -save-temps -c vd.c -o vd.o
ls vd.i vd.s vd.o
vd.i vd.o vd.s
#gcc gọi gì
terminal
# -v: in moi chuong trinh gcc goi, kem duong dan day du
gcc -std=c11 vd.c -o vd.exe -v 2>&1 | grep -E 'cc1|GNU C'
.../libexec/gcc/x86_64-w64-mingw32/8.1.0/cc1.exe GNU C11 (...MinGW-W64...) version 8.1.0 (x86_64-w64-mingw32)
Trình điều khiển (driver)
gcc không phải là trình biên dịch. Nó là một trình điều khiển: nó nhìn phần mở rộng tệp và các cờ, rồi lần lượt gọi cpp, cc1, as, ld với đúng đối số. Trình biên dịch C thật sự là cc1.#Xem macro và include
terminal
# In MOI macro tien dinh nghia cua trinh bien dich
gcc -std=c11 -dM -E - < /dev/null | wc -l
353
gcc -std=c11 -dM -E - < /dev/null | grep -E '__STDC_VERSION__|__GNUC__|__STDC__'
#define __GNUC__ 8 #define __STDC_VERSION__ 201112L #define __STDC__ 1
terminal
# -H: in cay include, moi dau cham la mot muc long sau
gcc -std=c11 -H -E vd.c -o /dev/null 2>&1 | head -3
. .../include/stdio.h .. .../include/crtdefs.h ... .../include/_mingw.h
Tự làm thử
- Chạy
gcc -Etrên một tệp có macro và tìm chỗ macro được thay. - Viết một macro thiếu ngoặc, dùng
-Eđể thấy nó sai thế nào. - Dùng
-save-tempsvà mở tệp.s, tìm tên một hàm bạn viết. - Chạy
gcc -vvà tìm đường dẫn tớicc1trên máy bạn. - In mọi macro tiền định nghĩa và đếm bao nhiêu cái bắt đầu bằng
__STDC. - Dùng
-Hđể đếmstdio.hkéo theo bao nhiêu header trên máy bạ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
- Bốn giai đoạn: tiền xử lý, biên dịch, hợp dịch, liên kết; ra lần lượt
.i,.s,.o, thực thi. -E,-S,-cdừng gcc ở ba điểm; không có cờ thì chạy hết tới liên kết.-save-tempschạy hết nhưng giữ lại mọi tệp trung gian.gcclà trình điều khiển gọicc1,as,ld;-vvà-###cho thấy nó gọi gì.-dM -Ein mọi macro tiền định nghĩa,-Hin cây include.