Từ khóa 'tĩnh' trong C có hai nghĩa cơ bản khác nhau.
Phạm vi giới hạn
Trong ngữ cảnh này, 'static' kết hợp với 'extern' để kiểm soát phạm vi của một biến hoặc tên hàm. Tĩnh làm cho tên biến hoặc tên hàm chỉ khả dụng trong một đơn vị biên dịch duy nhất và chỉ khả dụng đối với mã tồn tại sau khai báo / định nghĩa trong văn bản đơn vị biên dịch.
Giới hạn này tự nó thực sự chỉ có nghĩa là một cái gì đó nếu và chỉ khi bạn có nhiều hơn một đơn vị biên dịch trong dự án của bạn. Nếu bạn chỉ có một đơn vị biên dịch, thì nó vẫn thực hiện mọi thứ nhưng những hiệu ứng đó chủ yếu là vô nghĩa (trừ khi bạn thích đào sâu vào các tệp đối tượng để đọc những gì trình biên dịch tạo ra.)
Như đã lưu ý, từ khóa này trong ngữ cảnh này kết hợp với từ khóa 'extern', điều này ngược lại - bằng cách làm cho tên biến hoặc tên hàm có thể liên kết với cùng tên được tìm thấy trong các đơn vị biên dịch khác. Vì vậy, bạn có thể xem 'tĩnh' khi yêu cầu biến hoặc tên được tìm thấy trong đơn vị biên dịch hiện tại, trong khi 'extern' cho phép liên kết đơn vị biên dịch chéo.
Trọn đời tĩnh
Thời gian tồn tại tĩnh có nghĩa là biến tồn tại trong suốt thời lượng của chương trình (tuy nhiên dài như vậy.) Khi bạn sử dụng 'static' để khai báo / định nghĩa một biến trong một hàm, điều đó có nghĩa là biến đó được tạo ra trước khi sử dụng lần đầu tiên ( điều đó có nghĩa là, mỗi lần tôi trải nghiệm nó, biến đó được tạo trước khi main () bắt đầu) và không bị hủy sau đó. Ngay cả khi thực hiện chức năng đã hoàn thành và nó sẽ trả về cho người gọi nó. Và giống như các biến số trọn đời tĩnh được khai báo bên ngoài các hàm, chúng được khởi tạo cùng một lúc - trước khi hàm main () bắt đầu - đến một số không ngữ nghĩa (nếu không cung cấp khởi tạo) hoặc cho một giá trị rõ ràng được chỉ định, nếu được cung cấp.
Điều này khác với các biến chức năng loại 'tự động', được tạo mới (hoặc, nếu như mới) mỗi khi chức năng được nhập và sau đó bị hủy (hoặc, nếu như chúng bị phá hủy) khi chức năng thoát.
Không giống như tác động của việc áp dụng 'tĩnh' đối với định nghĩa biến ngoài hàm, tác động trực tiếp đến phạm vi của nó, khai báo một biến hàm (trong thân hàm, rõ ràng) là 'tĩnh' không ảnh hưởng đến phạm vi của nó. Phạm vi được xác định bởi thực tế là nó đã được xác định trong một thân hàm. Các biến số trọn đời tĩnh được xác định trong các hàm có cùng phạm vi với các biến 'tự động' khác được xác định trong các thân hàm - phạm vi hàm.
Tóm lược
Vì vậy, từ khóa 'tĩnh' có các ngữ cảnh khác nhau với số lượng "ý nghĩa rất khác nhau". Lý do nó được sử dụng theo hai cách, như thế này, là để tránh sử dụng từ khóa khác. (Có một cuộc thảo luận dài về nó.) Có cảm giác rằng các lập trình viên có thể chịu đựng được việc sử dụng và giá trị của việc tránh một từ khóa khác trong ngôn ngữ là quan trọng hơn (so với các đối số khác.)
(Tất cả các biến được khai báo bên ngoài hàm đều có thời gian tồn tại tĩnh và không cần từ khóa 'tĩnh' để biến điều đó thành sự thật. Vì vậy, loại từ khóa này đã giải phóng từ khóa được sử dụng ở đó để có nghĩa hoàn toàn khác: 'chỉ hiển thị trong một phần tổng hợp duy nhất đơn vị. 'Đó là một hack, thuộc loại.)
Lưu ý cụ thể
biến động tĩnh không dấu char PORTB @ 0x06;
Từ 'tĩnh' ở đây nên được hiểu là có nghĩa là trình liên kết sẽ không khớp với nhiều lần xuất hiện của PORTB có thể được tìm thấy trong nhiều đơn vị biên dịch (giả sử mã của bạn có nhiều hơn một.)
Nó sử dụng cú pháp đặc biệt (không di động) để chỉ định "vị trí" (hoặc giá trị số của nhãn thường là địa chỉ) của PORTB. Vì vậy, trình liên kết được cung cấp địa chỉ và không cần tìm địa chỉ cho nó. Nếu bạn có hai đơn vị biên dịch sử dụng dòng này, dù sao thì chúng cũng sẽ chỉ đến cùng một vị trí. Vì vậy, không cần phải dán nhãn 'extern', ở đây.
Nếu họ sử dụng 'extern' thì nó có thể gây ra vấn đề. Trình liên kết sau đó sẽ có thể thấy (và sẽ cố gắng khớp) nhiều tham chiếu đến PORTB được tìm thấy trong nhiều phần tổng hợp. Nếu tất cả trong số họ chỉ định một địa chỉ như thế này và các địa chỉ KHÔNG giống nhau vì một lý do nào đó [nhầm lẫn?], Thì nó phải làm gì? Than phiền? Hoặc là? (Về mặt kỹ thuật, với 'extern', quy tắc ngón tay cái sẽ là chỉ có MỘT đơn vị biên dịch sẽ chỉ định giá trị và các đơn vị khác không nên.)
Thật dễ dàng để gắn nhãn là 'tĩnh', tránh làm cho trình liên kết lo lắng về xung đột và chỉ đơn giản là đổ lỗi cho bất kỳ sai lầm nào đối với các địa chỉ khớp sai khi bất kỳ ai thay đổi địa chỉ thành địa chỉ không nên.
Dù bằng cách nào, biến được coi là có 'vòng đời tĩnh'. (Và 'không ổn định'.)
Một tuyên bố không phải là một định nghĩa , nhưng tất cả các định nghĩa là khai báo
Trong C, một định nghĩa tạo ra một đối tượng. Nó cũng tuyên bố nó. Nhưng một tuyên bố không thường (xem ghi chú dưới đây) tạo ra một đối tượng.
Sau đây là định nghĩa và khai báo:
static int a;
static int a = 7;
extern int b = 5;
extern int f() { return 10; }
Dưới đây không phải là định nghĩa, mà chỉ là khai báo:
extern int b;
extern int f();
Lưu ý rằng các khai báo không tạo ra một đối tượng thực tế. Họ chỉ khai báo các chi tiết về nó, mà trình biên dịch có thể sử dụng để giúp tạo mã chính xác và cung cấp các thông báo lỗi và cảnh báo, nếu phù hợp.
Ở trên, tôi nói "thông thường", khuyên nhủ. Trong một số trường hợp, một khai báo có thể tạo ra một đối tượng và do đó được trình liên kết quảng bá (không bao giờ bởi trình biên dịch.) Vì vậy, ngay cả trong trường hợp hiếm hoi này, trình biên dịch C vẫn cho rằng khai báo chỉ là khai báo. Đây là giai đoạn liên kết thực hiện bất kỳ chương trình khuyến mãi cần thiết nào của một số tuyên bố. Hãy ghi nhớ điều này một cách cẩn thận.
Trong các ví dụ trên, hóa ra chỉ có khai báo cho một "extern int b;" trong tất cả các đơn vị biên dịch được liên kết, sau đó trình liên kết chịu trách nhiệm tạo ra một định nghĩa. Hãy lưu ý rằng đây là một sự kiện thời gian liên kết. Trình biên dịch hoàn toàn không biết, trong quá trình biên dịch. Nó chỉ có thể được xác định tại thời điểm liên kết, nếu một tuyên bố loại này hầu hết được quảng bá.
Trình biên dịch nhận thức được rằng "static int a;" không thể được thúc đẩy bởi trình liên kết tại thời gian liên kết, vì vậy đây thực sự là một định nghĩa tại thời gian biên dịch .