Suy nghĩ về bộ tiền xử lý C là trình biên dịch rất đơn giản, để dịch một tệp mà bộ xử lý tiền xử lý C thực hiện một vài giai đoạn.
- phân tích từ vựng - Nhóm chuỗi các ký tự tạo thành đơn vị tiền xử lý dịch thành chuỗi có một ý nghĩa xác định (tokens) trong ngôn ngữ tiền xử lý.
- Phân tích cú pháp - Nhóm các thẻ của đơn vị dịch tiền xử lý thành các cấu trúc cú pháp được xây dựng theo ngữ pháp tiền xử lý ngôn ngữ.
- Tạo mã - Dịch tất cả các tệp tạo thành đơn vị dịch tiền xử lý thành một tệp có chứa chỉ dẫn 'thuần' C.
Nói đúng ra, các giai đoạn dịch nêu tại §5.1.1.2 của C Standard (ISO/IEC 9899:201x) liên quan đến tiền xử lý là giai đoạn 3 và giai đoạn 4. Giai đoạn 3 tương ứng gần như chính xác để phân tích từ vựng trong khi giai đoạn 4 là về thế hệ mã.
Phân tích cú pháp (phân tích cú pháp) dường như bị thiếu trong ảnh đó. Thật vậy, ngữ pháp tiền xử lý C đơn giản đến nỗi các trình tiền xử lý/trình biên dịch thực sự thực hiện nó cùng với phân tích từ vựng.
Nếu giai đoạn phân tích cú pháp kết thúc thành công - tức là tất cả các câu lệnh trong đơn vị dịch tiền xử lý là hợp pháp theo ngữ pháp tiền xử lý - việc tạo mã có thể diễn ra và tất cả các chỉ thị tiền xử lý được thực hiện.
Thực thi một chỉ thị tiền xử lý có nghĩa là chuyển đổi tệp nguồn theo ngữ nghĩa của nó và sau đó loại bỏ chỉ thị khỏi tệp nguồn.
Ngữ nghĩa cho mỗi chỉ thị tiền xử lý được quy định trong §6.10.1-6.10.9 của tiêu chuẩn C.
Quay lại chương trình mẫu của bạn, 2 tệp bạn đã cung cấp, tức là a.h
và b.h
, được xử lý theo khái niệm như sau.
Lexical Analysis - Mỗi tiền xử lý thẻ cá nhân được giới hạn bởi một '{' trên bên trái và một '}' ở bên phải.
a.h
{#}{if} {1}
{#}{include} {"b.h"}
b.h
{#}{endif}
Giai đoạn này được thực hiện mà không có lỗi và kết quả của nó, trình tự các thẻ tiền xử lý, được chuyển sang giai đoạn tiếp theo: Phân tích cú pháp.
Phân tích cú pháp
Một nguồn gốc dự kiến cho ah được đưa ra dưới đây
preprocessing-file →
group →
group-part →
if-section →
if-group endif-line →
if-group #endif new-line →
…
và rõ ràng là nội dung của ah không thể được bắt nguồn từ ngữ pháp tiền xử lý - trong thực tế, chấm dứt #endif
bị thiếu - và do đó a.h
không chính xác về cú pháp. Đây chính là điều mà trình biên dịch của bạn cho bạn biết khi viết
a.h:1:0: error: unterminated #if
Điều gì đó tương tự cũng xảy ra cho b.h
; lý luận ngược, các #endif
chỉ có thể xuất phát từ nguyên tắc
if-section →
if-group elif-groups[opt] else-group[opt] endif-line
Điều này có nghĩa các nội dung tập tin nên được bắt nguồn từ một trong 3 nhóm sau
# if constant-expression new-line group[opt]
# ifdef identifier new-line group[opt]
# ifndef identifier new-line group[opt]
Vì nó không phải là trường hợp, bởi vì b.h
không chứa # if/# ifdef/# ifndef
nhưng chỉ có một dòng #endif
, một lần nữa nội dung của b.h
không chính xác về cú pháp và trình biên dịch của bạn cho bạn biết về cách này
In file included from a.h:2:0:
b.h:1:2: error: #endif without #if
Code Generation
Tất nhiên, kể từ khi chương trình của bạn là lexically âm thanh nhưng cú pháp không đúng, giai đoạn này không bao giờ được thực hiện.
Ngay cả khi đặt '# if' vào một tệp và' # endif' tương ứng trong một tệp khác là hợp pháp, IMHO sẽ là một ý tưởng tồi. –