Đây có lẽ là một câu trả lời chi tiết hơn bạn muốn, nhưng tôi nghĩ rằng một lời giải thích hợp lý là hợp lý.
Trong C và C ++, một tệp nguồn được định nghĩa là một đơn vị dịch . Theo quy ước, các tệp tiêu đề giữ các khai báo hàm, định nghĩa kiểu và định nghĩa lớp. Việc triển khai chức năng thực tế nằm trong các đơn vị dịch thuật, tức là các tệp .cpp.
Ý tưởng đằng sau điều này là các hàm và các hàm thành viên lớp / struct được biên dịch và lắp ráp một lần, sau đó các hàm khác có thể gọi mã đó từ một nơi mà không tạo ra các bản sao. Các chức năng của bạn được khai báo là "bên ngoài" ngầm.
/* Function declaration, usually found in headers. */
/* Implicitly 'extern', i.e the symbol is visible everywhere, not just locally.*/
int add(int, int);
/* function body, or function definition. */
int add(int a, int b)
{
return a + b;
}
Nếu bạn muốn một hàm là cục bộ cho một đơn vị dịch thuật, bạn xác định nó là 'tĩnh'. Điều đó có nghĩa là gì? Điều đó có nghĩa là nếu bạn bao gồm các tệp nguồn với các hàm extern, bạn sẽ gặp các lỗi xác định lại, bởi vì trình biên dịch đi qua cùng một triển khai nhiều lần. Vì vậy, bạn muốn tất cả các đơn vị dịch thuật của bạn xem khai báo hàm nhưng không phải là thân hàm .
Vì vậy, làm thế nào để tất cả được nghiền với nhau vào cuối? Đó là công việc của linker. Một trình liên kết đọc tất cả các tệp đối tượng được tạo bởi giai đoạn trình biên dịch và giải quyết các ký hiệu. Như tôi đã nói trước đó, một biểu tượng chỉ là một cái tên. Ví dụ, tên của một biến hoặc một hàm. Khi các đơn vị dịch thuật gọi các hàm hoặc khai báo các kiểu không biết việc triển khai cho các hàm hoặc kiểu đó, các ký hiệu đó được cho là không được giải quyết. Trình liên kết giải quyết biểu tượng chưa được giải quyết bằng cách kết nối đơn vị dịch giữ biểu tượng không xác định cùng với biểu tượng có chứa triển khai. Phù. Điều này đúng với tất cả các biểu tượng có thể nhìn thấy bên ngoài, cho dù chúng được triển khai trong mã của bạn hay được cung cấp bởi một thư viện bổ sung. Một thư viện thực sự chỉ là một kho lưu trữ với mã có thể tái sử dụng.
Có hai ngoại lệ đáng chú ý. Đầu tiên, nếu bạn có một chức năng nhỏ, bạn có thể làm cho nó nội tuyến. Điều này có nghĩa là mã máy được tạo không tạo ra lệnh gọi hàm ngoài, nhưng được nối tại chỗ theo nghĩa đen. Vì chúng thường nhỏ, nên kích thước trên không thành vấn đề. Bạn có thể tưởng tượng họ tĩnh tại trong cách họ làm việc. Vì vậy, nó là an toàn để thực hiện các chức năng nội tuyến trong các tiêu đề. Các triển khai hàm bên trong một định nghĩa lớp hoặc cấu trúc cũng thường được trình biên dịch tự động nội tuyến.
Ngoại lệ khác là các mẫu. Vì trình biên dịch cần phải xem toàn bộ định nghĩa kiểu mẫu khi khởi tạo chúng, nên không thể tách rời việc thực hiện khỏi định nghĩa như với các hàm độc lập hoặc các lớp bình thường. Chà, có lẽ điều này là có thể ngay bây giờ, nhưng việc hỗ trợ trình biên dịch rộng rãi cho từ khóa "xuất khẩu" mất một thời gian dài. Vì vậy, không có sự hỗ trợ cho 'xuất khẩu', các đơn vị dịch thuật có được các bản sao địa phương của các loại và chức năng tạo khuôn mẫu tức thời, tương tự như cách các chức năng nội tuyến hoạt động. Với sự hỗ trợ cho 'xuất khẩu', đây không phải là trường hợp.
Đối với hai trường hợp ngoại lệ, một số người thấy nó "đẹp hơn" khi đặt việc triển khai các hàm nội tuyến, hàm templated và các kiểu templated trong các tệp .cpp, sau đó #inc loại trừ tệp .cpp. Cho dù đây là tiêu đề hay tệp nguồn không thực sự quan trọng; bộ tiền xử lý không quan tâm và chỉ là một quy ước.
Tóm tắt nhanh toàn bộ quá trình từ mã C ++ (một số tệp) và đến tệp thực thi cuối cùng:
- Bộ tiền xử lý được chạy, phân tích tất cả các lệnh bắt đầu bằng '#'. Chẳng hạn, lệnh #incoide kết hợp các tệp được bao gồm với kém hơn. Nó cũng thay thế macro và dán mã thông báo.
- Trình biên dịch thực tế chạy trên tệp văn bản trung gian sau giai đoạn tiền xử lý và phát ra mã trình biên dịch .
- Các lắp ráp chạy vào file lắp ráp và phát ra mã máy, điều này thường được gọi là một tập tin đối tượng và theo định dạng thực thi nhị phân của hệ thống tác trong câu hỏi. Ví dụ: Windows sử dụng PE (định dạng thực thi di động), trong khi Linux sử dụng định dạng ELF của Hệ thống Unix, với các phần mở rộng GNU. Ở giai đoạn này, các biểu tượng vẫn được đánh dấu là không xác định.
- Cuối cùng, trình liên kết được chạy. Tất cả các giai đoạn trước được chạy trên mỗi đơn vị dịch theo thứ tự. Tuy nhiên, giai đoạn trình liên kết hoạt động trên tất cả các tệp đối tượng được tạo bởi trình biên dịch chương trình. Trình liên kết giải quyết các biểu tượng và thực hiện nhiều phép thuật như tạo các phần và phân đoạn, phụ thuộc vào nền tảng đích và định dạng nhị phân. Các lập trình viên không bắt buộc phải biết điều này nói chung, nhưng nó chắc chắn sẽ giúp ích trong một số trường hợp.
Một lần nữa, điều này chắc chắn là nhiều hơn bạn yêu cầu, nhưng tôi hy vọng các chi tiết khó chịu sẽ giúp bạn nhìn thấy bức tranh lớn hơn.