2017-12-14 148 views
5

Gần đây tôi đã cố gắng đọc thêm mã nguồn mở C. Một mô hình chung mà tôi đã áp dụng trong các dự án sở thích của tôi là như sau: -các hàm nội dòng tĩnh trong một tệp tiêu đề

Trong các tệp C của tôi, tôi có các hàm tĩnh hoặc xuất. Chỉ các hàm được xuất khẩu mới được đặt trong tệp tiêu đề. Các biến toàn cầu chỉ được sử dụng trong phạm vi của một đối tượng cũng được sử dụng làm biến toàn cầu tĩnh.

Câu hỏi của tôi liên quan đến tính hữu ích và động lực của việc có các hàm "tĩnh nội tuyến" bên trong các tệp tiêu đề. Từ những gì tôi đọc trực tuyến, không sử dụng các từ khóa tĩnh gây ra một lỗi định nghĩa nhiều và đó là lý do không chỉ xác định chức năng như chỉ là "nội tuyến".

Tuy nhiên, điều này có nghĩa là chức năng này được xuất cho các đối tượng khác để sử dụng? Nếu có, thì tại sao không chỉ định nghĩa hàm này trong tệp C và xuất nó qua tệp tiêu đề? Nếu không, tại sao đặt điều này trong tệp tiêu đề thay vì chỉ có nó bên trong tệp C?

Có lý do nào đằng sau kiểu mã hóa này không? Tôi đang thiếu gì?

Một trong những ví dụ có thể được tìm thấy trong codebase git bên hashmap.h:

/* 
* Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code 
* for use in hash tables. Cryptographic hashes are supposed to have 
* uniform distribution, so in contrast to `memhash()`, this just copies 
* the first `sizeof(int)` bytes without shuffling any bits. Note that 
* the results will be different on big-endian and little-endian 
* platforms, so they should not be stored or transferred over the net. 
*/ 
static inline unsigned int sha1hash(const unsigned char *sha1) 
{ 
    /* 
    * Equivalent to 'return *(unsigned int *)sha1;', but safe on 
    * platforms that don't support unaligned reads. 
    */ 
    unsigned int hash; 
    memcpy(&hash, sha1, sizeof(hash)); 
    return hash; 
} 
+4

Xin vui lòng gửi một câu hỏi ngắn gọn đơn ủng hộ với các ví dụ, không phải một mà đòi hỏi phải xây dựng ưa thích " kiểu mã hóa ". – Fabulous

+0

Để bạn tham khảo: https://stackoverflow.com/help/mcve – Fabulous

+1

Bằng cách "xuất", bạn có nghĩa là "bên ngoài" không? Bạn có lẽ cụ thể hơn có nghĩa là "tuyên bố với một" extern' lưu trữ lớp specifier "? –

Trả lời

5

Một chức năng static inline là, trong thực tế, khả năng (nhưng không chắc chắn) là inlined bởi một số trình biên dịch tối ưu hóa tốt (ví dụ như bởi GCC khi được cấp -O2) tại hầu hết các trang web cuộc gọi của nó.

Nó được xác định trong tệp tiêu đề, vì nó có thể được gạch chân tại hầu hết các trang gọi (có thể là tất cả). Nếu đó chỉ là tuyên bố (và đơn giản là "xuất khẩu") các nội tuyến là khó có khả năng xảy ra (trừ khi bạn biên dịch và liên kết với link-time optimizations, aka LTO, cũng có thể, ví dụ như biên dịch và liên kết với gcc -flto -O2, và làm tăng rất nhiều thời gian xây dựng).

Trong thực tế, trình biên dịch cần phải biết phần thân của hàm để có thể nội tuyến nó. Vì vậy, một nơi thích hợp là xác định nó trong một số tệp tiêu đề chung (nếu không, nó có thể chỉ được inline trong cùng một đơn vị dịch thuật xác định nó, trừ khi bạn bật LTO), để mọi đơn vị dịch sẽ biết nội dung của hàm không thể in đó.

Được khai báo static để tránh nhiều định nghĩa (tại thời gian liên kết) trong trường hợp trình biên dịch không trực tiếp (ví dụ: khi bạn sử dụng địa chỉ của trình biên dịch). Trong thực tế, trong mã C99 hoặc C11 (ngoại trừ với LTO, mà tôi hiếm khi sử dụng), tôi sẽ luôn đặt các hàm ngắn mà tôi muốn được inline là static inline định nghĩa trong các tệp tiêu đề chung.

Đảm bảo hiểu cách thức và thời điểm hoạt động của C preprocessor. Lưu ý rằng bạn có thể về nguyên tắc (nhưng nó sẽ rất khó thực hành và phong cách kinh tởm) tránh định nghĩa một số hàm static inline trong một tệp tiêu đề chung và thay vào đó sao chép và dán định nghĩa giống hệt của nó trong nhiều tệp .c. (Tuy nhiên, điều đó có thể có ý nghĩa đối với được tạo.c tệp, ví dụ: nếu bạn thiết kế compiler emitting C code).

FYI LTO là thực tế thực hiện bởi trình biên dịch GCC gần đây bằng cách nhúng một số đại diện biên dịch nội bộ (một số GIMPLE) bên trong file đối tượng, và làm lại một số "biên soạn" bước - using the lto1 frontend - tại "liên kết" thời gian. Trong thực tế, toàn bộ chương trình gần như được biên dịch "hai lần".

(trên thực tế, tôi luôn tự hỏi tại sao Ủy ban tiêu chuẩn C đã không quyết định thay vào đó tất cả rõ ràng inline chức năng ở dạng tĩnh)

+1

Có lẽ đáng nói đến sự khác biệt với nội dung 'inline' của C++ là điều' tĩnh' (spoiler: Tôi cứ quên đi các chi tiết, vì vậy tôi sẽ rất biết ơn bất cứ ai đưa ra một bản tóm tắt tốt). –

Các vấn đề liên quan