Sự khác biệt giữa hàm “tĩnh” và “nội tuyến tĩnh” là gì?


123

IMO đều làm cho chức năng chỉ có phạm vi của đơn vị dịch.

Sự khác biệt giữa hàm "tĩnh" và "nội tuyến tĩnh" là gì?

Tại sao nên inlineđặt trong tệp tiêu đề, không phải trong .ctệp?

Câu trả lời:


109

inlinehướng dẫn trình biên dịch cố gắng nhúng nội dung hàm vào mã gọi thay vì thực hiện một lệnh gọi thực.

Đối với các chức năng nhỏ được gọi thường xuyên có thể tạo ra sự khác biệt lớn về hiệu suất.

Tuy nhiên, đây chỉ là "gợi ý" và trình biên dịch có thể bỏ qua nó, và hầu hết các trình biên dịch sẽ cố gắng "nội tuyến" ngay cả khi từ khóa không được sử dụng, như một phần của tối ưu hóa, nếu có thể.

ví dụ:

static int Inc(int i) {return i+1};
.... // some code
int i;
.... // some more code
for (i=0; i<999999; i = Inc(i)) {/*do something here*/};

Vòng lặp chặt chẽ này sẽ thực hiện một lệnh gọi hàm trên mỗi lần lặp, và nội dung hàm thực sự ít hơn đáng kể so với mã mà trình biên dịch cần đặt để thực hiện lệnh gọi. inlinevề cơ bản sẽ hướng dẫn trình biên dịch chuyển đổi mã trên thành một mã tương đương với:

 int i;
 ....
 for (i=0; i<999999; i = i+1) { /* do something here */};

Bỏ qua lệnh gọi hàm thực và quay lại

Rõ ràng đây là một ví dụ để chỉ ra quan điểm, không phải là một đoạn mã thực sự.

staticđề cập đến phạm vi. Trong C, điều đó có nghĩa là hàm / biến chỉ có thể được sử dụng trong cùng một đơn vị dịch.


4
Không, staticđề cập đến phạm vi. Trong C, điều đó có nghĩa là hàm / biến chỉ có thể được sử dụng trong cùng một đơn vị dịch.
littleadv 14/10/11

8
Điều quan trọng cần lưu ý là mã được khai báo là nội tuyến nằm trong tiêu đề, nơi mã nguồn bình thường (không phải mẫu) không thể đi vào tiêu đề mà không gây ra nhiều lỗi xác định lại. Vì vậy, ngay cả khi tuyên bố điều gì đó inline, ngay cả khi chọn nhóm biên dịch không inline nó, vẫn còn là một bội số-redefinition tránh hành vi tiêu chuẩn bắt buộc mà đá trong.
VoidStar

13
@VoidStar Trên thực tế static(có hoặc không inline) có thể ở tiêu đề hoàn toàn tốt, không hiểu lý do gì mà không. Templates là dành cho C ++, câu hỏi này là về C.
littleadv

2
@littleadv: lý do chính cho việc đưa định nghĩa chức năng trong các tập tin tiêu đề là để làm cho họ inlinable, vì vậy đánh dấu chúng một cách rõ ràng inlinelà phong cách tốt, imo
Christoph

9
Trên thực tế inlinekhông hướng dẫn trình biên dịch thực hiện bất kỳ nỗ lực nào trong nội tuyến. Nó chỉ cho phép lập trình viên bao gồm phần thân hàm trong nhiều đơn vị dịch mà không vi phạm ODR. Một tác dụng phụ của việc này là nó làm cho nó có thể cho trình biên dịch, khi nó sẽ nội tuyến chức năng, để thực sự làm điều này.
Ruslan

96

Theo mặc định, định nghĩa nội dòng chỉ hợp lệ trong đơn vị dịch hiện tại.

Nếu lớp lưu trữ là extern , định danh có liên kết bên ngoài và định nghĩa nội tuyến cũng cung cấp định nghĩa bên ngoài.

Nếu là lớp lưu trữ static, định danh có liên kết nội bộ và định nghĩa nội tuyến là ẩn trong các đơn vị dịch khác.

Nếu lớp lưu trữ không được chỉ định, định nghĩa nội tuyến chỉ hiển thị trong đơn vị dịch hiện tại, nhưng mã định danh vẫn có liên kết bên ngoài và định nghĩa bên ngoài phải được cung cấp trong một đơn vị dịch khác. Trình biên dịch có thể tự do sử dụng định nghĩa nội tuyến hoặc định nghĩa bên ngoài nếu hàm được gọi trong đơn vị dịch hiện tại.

Vì trình biên dịch có thể tự do nội tuyến (và không nội tuyến) bất kỳ hàm nào có định nghĩa hiển thị trong đơn vị dịch hiện tại (và, nhờ tối ưu hóa thời gian liên kết, ngay cả trong các đơn vị dịch khác nhau, mặc dù tiêu chuẩn C không thực sự giải thích đó), đối với hầu hết các mục đích thực tế, không có sự khác biệt giữa staticstatic inline định nghĩa hàm định nghĩa.

Các inlinespecifier (như registerstorage class) chỉ là một trình biên dịch gợi ý, và trình biên dịch được tự do hoàn toàn bỏ qua nó. Các trình biên dịch không tối ưu hóa tuân thủ tiêu chuẩn chỉ phải tôn trọng các tác dụng phụ của chúng và việc tối ưu hóa các trình biên dịch sẽ thực hiện những tối ưu hóa này có hoặc không có gợi ý rõ ràng.

inlineregisterkhông phải là vô dụng, tuy nhiên, khi họ chỉ thị cho trình biên dịch để ném lỗi khi các lập trình viên viết code mà sẽ làm cho tối ưu hóa không thể: Một bên ngoài inlineđịnh nghĩa không thể định danh tài liệu tham khảo với liên kết nội bộ (vì đây sẽ là không có sẵn trong một đơn vị dịch thuật khác nhau) hoặc xác định các biến cục bộ có thể sửa đổi với thời lượng lưu trữ tĩnh (vì những biến này sẽ không chia sẻ trạng thái qua các đơn vị dịch) và bạn không thể lấy địa chỉ củaregister các biến-đủ điều kiện.

Cá nhân tôi cũng sử dụng quy ước để đánh dấu staticcác định nghĩa hàm trong tiêu đề inline, vì lý do chính để đưa các định nghĩa hàm vào tệp tiêu đề là làm cho chúng có thể nhập được.

Nói chung, tôi chỉ sử dụng static inlinecác static constđịnh nghĩa hàm và đối tượng ngoàiextern khai báo trong tiêu đề.

Tôi chưa bao giờ viết một inlinehàm với một lớp lưu trữ khác với static.


8
Đây là câu trả lời chính xác. Bất kỳ câu trả lời nào nói về inlinenhư thể nó thực sự được áp dụng cho nội tuyến đều gây hiểu lầm và được cho là không chính xác. Không có trình biên dịch hiện đại nào sử dụng nó như một gợi ý để nội dòng hoặc yêu cầu nó để cho phép nội dòng của một hàm.
Tyg13

1
ủng hộ cho "sử dụng quy ước để đánh dấu các định nghĩa hàm tĩnh trong dòng tiêu đề".
John Z. Li

Tôi đã đọc toàn bộ câu trả lời của bạn và tôi vẫn không hiểu sự khác biệt về ngữ nghĩa giữa staticstatic inline. Cả hai đều làm cho định nghĩa vô hình đối với các đơn vị dịch khác. Vì vậy, những gì sẽ là một lý do hợp lý để viết static inlinethay vì static?
user541686

21

Từ kinh nghiệm của tôi với GCC, tôi biết điều đó staticstatic inlinekhác ở cách trình biên dịch đưa ra cảnh báo về các chức năng không sử dụng. Chính xác hơn khi bạn khai báo statichàm và nó không được sử dụng trong đơn vị dịch hiện tại thì trình biên dịch sẽ đưa ra cảnh báo về hàm không sử dụng, nhưng bạn có thể ngăn cảnh báo đó bằng cách thay đổi nó thànhstatic inline .

Vì vậy, tôi có xu hướng nghĩ rằng staticnên được sử dụng trong các đơn vị dịch và được hưởng lợi từ trình biên dịch kiểm tra bổ sung để tìm các chức năng không sử dụng. Và static inlinenên được sử dụng trong các tệp tiêu đề để cung cấp các chức năng có thể được xếp trong hàng (do không có liên kết bên ngoài) mà không đưa ra cảnh báo.

Thật không may, tôi không thể tìm thấy bất kỳ bằng chứng nào cho logic đó. Ngay cả từ tài liệu GCC, tôi cũng không thể kết luận điều đó inlinengăn cản các cảnh báo chức năng không sử dụng. Tôi đánh giá cao nếu ai đó sẽ chia sẻ liên kết đến mô tả về điều đó.


1
Mmm, vẫn có warning: unused function 'function' [clang-diagnostic-unused-function]một static inlinechức năng khi xây dựng với clang-tidy(v8.0.1), được sử dụng trong một đơn vị dịch khác. Nhưng chắc chắn, đây là một trong những lời giải thích và lý do tốt nhất cho việc kết hợp static& inline!
DrumM

6

Trong C, static có nghĩa là hàm hoặc biến bạn xác định chỉ có thể được sử dụng trong tệp này (tức là đơn vị biên dịch)

Vì vậy, static inlinecó nghĩa là hàm nội tuyến chỉ có thể được sử dụng trong tệp này.

BIÊN TẬP:

Đơn vị biên dịch phải là Đơn vị dịch


2
Hay nói một cách hoa mỹ: nó có liên kết nội bộ.
K-ball

@AlokSave: Có sự khác biệt giữa đơn vị biên dịchđơn vị dịch không? Nếu vậy, cái nào thích hợp hơn trong ngữ cảnh của ngôn ngữ C ++?
Legends2k

Tôi tin the compile unitlà một cái gì đó tôi đã viết trong báo lỗi, không có những điều như vậy, thuật ngữ thực tế làtranslation unit
shengy

Câu trả lời của bạn chưa hoàn chỉnh vì nó chủ yếu được sử dụng trong các tệp tiêu đề, trên các đơn vị dịch.
DrumM

5

Một điểm khác biệt không phải ở cấp độ ngôn ngữ mà là cấp độ triển khai phổ biến: các phiên bản nhất định của gcc sẽ xóa các static inlinehàm không được tham chiếu khỏi đầu ra theo mặc định, nhưng sẽ giữ các statichàm đơn giản ngay cả khi không được tham chiếu. Tôi không chắc phiên bản này áp dụng cho những phiên bản nào, nhưng từ quan điểm thực tế, nó có nghĩa là bạn nên luôn sử dụng inlinecho các statichàm trong tiêu đề.


Điều gì về việc sử dụng inlinetrong định nghĩa? Bạn cũng ngụ ý không sử dụng nó cho các externchức năng?
new_perl 14/10/11

Điều này có còn đúng với phiên bản GCC gần đây không? Câu trả lời của bạn sẽ thú vị hơn rất nhiều nếu bạn lấy ví dụ và liệt kê phiên bản GCC nào thực hiện điều đó.
Z boson

@Zboson: Tôi không có sẵn thông tin đó và không có thời gian để thiết lập và thử nghiệm nhiều phiên bản gcc vào lúc này, nhưng tôi đồng ý rằng đó sẽ là thông tin hữu ích nếu có. Bạn có thể tìm thấy thời điểm gcc lần đầu tiên bắt đầu tối ưu hóa các hàm / đối tượng tĩnh không được sử dụng bằng cách xem lịch sử attribute((used))và việc sử dụng nó để cho phép asm tham chiếu đến các statichàm và dữ liệu không được tham chiếu khác .
R .. GitHub NGỪNG TRỢ GIÚP ICE
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.