#include "stdio.h"
#define PI 3.14
int main()
{
printf("PI: %f \n\r",PI);
return 0;
}
Source 1: Chương trình in ra số PI
Compiling là quá trình compiler làm việc để tạo
ra file thực thi *.exe[1] từ source code[1] của bạn. Quá
trình compiling có thể chia ra thành 3 giai đoạn chính: preprocessing, compilation
và linking.
Giai đoạn 1: Preprocessing
Trong mỗi compiler
đều có một C preprocessor, nhiệm vụ của C preprocessor là làm thay đổi source
code của bạn trước khi compilation. Vậy source code bị
thay đổi như thế nào?
Thực tế, compiler không thể thấy tất cả các câu lệnh được đặt sau giấu #,
có nghĩa là các câu lệnh #include<stdio.h>, #define PI 3.14 đối với compiler chỉ giống như những comment và bị nó bỏ qua.
Vậy nên nhiệm vụ của C processor là phải thay đổi source code sao cho compiler có thể thấy và hiểu được những câu lệnh đặt sau giấu #.
Tức là, C processor sẽ duyệt qua tất cả các source file, khi gặp câu lệnh #include<stdio.h> nó sẽ copy nội dung của file stdio.h để thay thế vào vị trí dòng lệnh #include<stdio.h> trong source file đó.
Tương tự, câu lệnh printf(“PI: %f \n”,PI); cũng sẽ bị chuyển về dạng printf(“PI: %f \n”,3.14); vì compiler không thể thấy được câu lệnh #define PI 3.14.
#include "stdio.h"
int main()
{
printf("PI: %f \n\r",3.14);
return 0;
}
Source 2: Chương trình in ra số PI sau bước
Preprocessing
Như mô tả ở Source 2 ta thấy dòng lệnh #define PI 3.14 bị bỏ đi, giá trị PI sẽ được thấy trực tiếp tại câu lệnh printf("PI: %f \n\r",3.14); .
Source 2 là tất cả những gì compiler thấy khi tương ứng với những gì lập trình viên thấy ở Source 1.
Source 2 là tất cả những gì compiler thấy khi tương ứng với những gì lập trình viên thấy ở Source 1.
Vậy:
Nhiệm vụ của bước Preprocessing là thay đổi source code để compiler có thể thấy
được tất cả những gì lập trình viên muốn thể hiện trong source code.
Giai đoạn 2: Compilation
Compilation là quá trình chuyển tất cả các file source code *.c thành các file *.o[1]
tương ứng. Oh! Bạn thắc mắc vì sao phải làm như thế?
Vì máy tính không thể hiểu
được các câu lệnh C bậc cao, nó chỉ có thể hiểu được các mã máy đơn giản [xem thêm về mã máy tại: Machine Language].
Một file *.o chính là kết quả của việc biên dịch một file *.c từ ngôn ngữ C sang mã máy để máy tính có thể hiểu được.
Một file *.o chính là kết quả của việc biên dịch một file *.c từ ngôn ngữ C sang mã máy để máy tính có thể hiểu được.
Example 1: Sau
khi compilation source file main.c bạn sẽ thu được object file main.o mà máy tính có thể hiểu được.
Vậy: Compilation
là quá trình biên dịch ngôn ngữ C sang ngôn ngữ máy để máy tính có thể hiểu được, kết quả
được lưu dưới dạng file *.o.
Máy tính đã có thể
hiểu file *.o nhưng chúng ta vẫn chưa thể chạy chương trình được, cần phải chuyển
các file *.o thành file *.exe.
Giai đoạn 3: Linking
Linking là việc tạo
ra file *.exe từ các file *.o và thư viện object.
Thư viện object là
gì, nó ở đâu ra?
Bộ phận thực hiện chức
năng linking gọi là linker, linker không chỉ liên kết các object file được tạo
ra từ source code của bạn mà nó còn liên kết tới các object file nằm ngoài
source code của bạn.
Example 2: Bạn vẫn có thể sử dụng hàm printf(“”);[2] trong hàm main cho dù bạn không định nghĩa hay khai báo hàm printf(“”);. Thực tế, hàm printf(“”); được định nghĩa trong một file object có tên là stdio.o và khai báo trong file stdio.h, cả hai file này được cung cấp bởi compiler[3], vậy nên khi bạn thực hiện #include <stdio.h> tức là bạn đã cho phép linker liên kết đến file stdio.o để có thể sử dụng được hàm printf(“”);.
Tương tự khi bạn
#include “function.h” tức là cho phép linker liên kết đến object function.o[3].
Vậy quá trình Linking
diễn ra như thế nào?
Quay lại Example 2, trong quá trình compilation, compiler sẽ biên dịch các file *.c thành các file mã máy *.o, khi biên dịch đến dòng lệnh sử dụng hàm printf(“”); compiler sẽ đánh
giấu dòng lệnh đó và bỏ qua nó[4]
để
tiếp tục biên dịch các câu lệnh tiếp theo.
Phải đợi đến quá
trình linking, khi đó linker sẽ kiểm tra tất cả các file object
được tạo ra từ source code của bạn để
tìm các dòng
lệnh mà compiler đã đánh giấu trước đó và thay thế nó bằng các
hàm tương ứng được định nghĩa trong các file object
được liên kết đến, trong Example 2
thì linker sẽ lấy hàm printf(“”); trong object stdio.o và thay thế vào dòng lệnh
được đánh giấu trong object main.o.
Quá trình tìm kiếm và thay thế này được gọi là Fixup.
Sau khi hoàn thành linking tạo ra file *.exe
chính là một chương trình
có thể thực thi được (Executable program). Quá trình compiler kết thúc.
---------------------------------------------------------------------------------------------------------------------------------
[1]: *.exe là một định dạng file trong đó exe viết tắt của
executable, tương tự *.o, o là viết tắt của object. Source code là tập hợp của nhiều source file.
[2]: printf(“\n”) là một hàm chứ không phải một câu lệnh trong ngôn ngữ C.
[3]: Các file object nào được cung cấp bởi compiler thì gọi
là file thư viện, stdio.o là object thư viện nhưng function.o không phải là object
thư viện.
[4]: Vì trong file *.c của bạn không định nghĩa hàm
printf(“”); nên compiler không thể dịch được nó sang mã máy trong khi compilation.
No comments:
Post a Comment