Không tĩnh có nghĩa là gì trong C?


1137

Tôi đã thấy từ staticđược sử dụng ở những nơi khác nhau trong mã C; đây có giống như một hàm / lớp tĩnh trong C # (nơi thực hiện được chia sẻ giữa các đối tượng) không?



15
Lý do để loại bỏ "trong một chương trình C" từ cuối tiêu đề, @Lundin là gì? Nó hơi dư thừa khi có thẻ c , nhưng nó cho phép tôi xem phân loại nhanh hơn mà không cần kiểm tra các thẻ. Sự dư thừa này rất thoải mái khi tôi tiếp cận câu hỏi từ một hướng có thể chứa các câu hỏi về các ngôn ngữ khác, ví dụ như tìm kiếm tĩnh hoặc tìm kiếm của Google.
Palec

5
@Palec Có chính sách SO rằng các mục có trong danh sách thẻ là không cần thiết trong tiêu đề. Trang web sẽ tự động nối C vào trang web thực tế. Google cho "C static" đưa ra câu trả lời này là lượt truy cập hàng đầu. Lý do tại sao điều này đã được thay đổi là vì câu hỏi này bây giờ là một phần của Câu hỏi thường gặp về ngôn ngữ SO C và tất cả các bài đăng được thêm vào đều được đánh bóng một chút.
Lundin

1
@Lundin Tôi thích giữ "C" trong tiêu đề, vì SO chỉ thêm một thẻ vào tiêu đề (phổ biến nhất?). Điều gì sẽ xảy ra nếu một ngày nào đó "cú pháp" đạt được nhiều câu hỏi hơn C (vì đó là một ngôn ngữ chéo)? Tôi muốn sử dụng các hành vi rõ ràng :-) Edit: ah nhưng có một câu hỏi meta nói cách khác: meta.stackexchange.com/questions/19190/...
Ciro Santilli冠状病毒审查六四事件法轮功

Câu trả lời:


1519
  1. Một biến tĩnh bên trong một hàm giữ giá trị của nó giữa các lệnh.
  2. Một biến toàn cục tĩnh hoặc một hàm chỉ được "nhìn thấy" trong tệp mà nó được khai báo trong

(1) là chủ đề xa lạ hơn nếu bạn là người mới, vì vậy đây là một ví dụ:

#include <stdio.h>

void foo()
{
    int a = 10;
    static int sa = 10;

    a += 5;
    sa += 5;

    printf("a = %d, sa = %d\n", a, sa);
}


int main()
{
    int i;

    for (i = 0; i < 10; ++i)
        foo();
}

Bản in này:

a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60

Điều này hữu ích cho các trường hợp trong đó một hàm cần giữ một số trạng thái giữa các lệnh và bạn không muốn sử dụng các biến toàn cục. Tuy nhiên, hãy cẩn thận, tính năng này nên được sử dụng một cách tiết kiệm - nó làm cho mã của bạn không an toàn cho chuỗi và khó hiểu hơn.

(2) Được sử dụng rộng rãi như một tính năng "kiểm soát truy cập". Nếu bạn có tệp .c triển khai một số chức năng, nó thường chỉ hiển thị một vài chức năng "công khai" cho người dùng. Các chức năng còn lại của nó nên được thực hiện staticđể người dùng không thể truy cập chúng. Đây là đóng gói, một thực hành tốt.

Trích dẫn Wikipedia :

Trong ngôn ngữ lập trình C, static được sử dụng với các biến và hàm toàn cục để đặt phạm vi của chúng thành tệp chứa. Trong các biến cục bộ, static được sử dụng để lưu trữ biến trong bộ nhớ được cấp phát tĩnh thay vì bộ nhớ được cấp phát tự động. Mặc dù ngôn ngữ không ra lệnh thực hiện một trong hai loại bộ nhớ, bộ nhớ được cấp phát tĩnh thường được dành riêng trong phân đoạn dữ liệu của chương trình vào thời gian biên dịch, trong khi bộ nhớ được cấp phát tự động thường được triển khai như một ngăn xếp cuộc gọi tạm thời.

Và để trả lời câu hỏi thứ hai của bạn, nó không giống như trong C #.

Tuy nhiên, trong C ++, staticcũng được sử dụng để xác định các thuộc tính lớp (được chia sẻ giữa tất cả các đối tượng của cùng một lớp) và các phương thức. Trong C không có lớp, vì vậy tính năng này không liên quan.


179
Pax, OP không biết về tĩnh, vì vậy bạn đề nghị đưa anh ta vào sự khác biệt giữa các đơn vị biên dịch và tệp? :-)
Eli Bendersky

138
Một đơn vị biên dịch là một tệp duy nhất trình biên dịch nhìn thấy. Tệp .c của bạn có thể bao gồm các tệp .c khác, nhưng sau khi bộ tiền xử lý sắp xếp bao gồm, trình biên dịch cuối cùng chỉ thấy một "đơn vị biên dịch" duy nhất.
Eli Bendersky

81
@robUK: trình biên dịch thậm chí không biết về các tệp .h - chúng được kết hợp thành các tệp .c trong bộ xử lý trước. Vì vậy, có bạn có thể nói rằng tệp .c, với tất cả các tiêu đề được bao gồm trong nó, là một đơn vị biên dịch duy nhất.
Eli Bendersky

6
@TonyD có thể nó khó hiểu, nhưng đó là cách biên dịch hoạt động. Nó thường có thể là một .cvà một loạt các tệp tiêu đề, nhưng ma quỷ luôn ở trong những gì không điển hình.
peterph

7
@TonyD Trình biên dịch không biên dịch. Bộ tiền xử lý không tiền xử lý. Gọi toolchain là 'trình biên dịch' sẽ không thay đổi nó là gì hoặc nó làm gì.
Miles Rout

231

Có một cách sử dụng nữa không được đề cập ở đây và đó là một phần của khai báo kiểu mảng như là một đối số cho hàm:

int someFunction(char arg[static 10])
{
    ...
}

Trong ngữ cảnh này, điều này xác định rằng các đối số được truyền cho hàm này phải là một mảng kiểu charcó ít nhất 10 phần tử trong đó. Để biết thêm thông tin xem câu hỏi của tôi ở đây .


3
Tôi không nghĩ rằng C có đối số mảng? Linus Torvalds giận dữ về những người làm việc này.
suprjami

13
@jamieb: C không có đối số mảng, nhưng cụ thể này có nghĩa là cú pháp rằng hy vọng chức năng arg[0]thông qua để arg[9]có giá trị (mà cũng có nghĩa là chức năng không chấp nhận một con trỏ null). Trình biên dịch có thể sử dụng thông tin này bằng cách nào đó để tối ưu hóa và các máy phân tích tĩnh có thể sử dụng thông tin này để đảm bảo rằng hàm không bao giờ được đưa ra một con trỏ null (hoặc nếu nó có thể cho biết, một mảng có ít phần tử hơn chỉ định).
dreamlax

19
@Qix - Đây là một ý nghĩa quá tải mới được đưa ra statictrong C99. Nó đã hơn một thập kỷ rưỡi tuổi, nhưng không phải tất cả các nhà văn biên dịch đều chấp nhận tất cả các tính năng của C99 - vì vậy, C99 nói chung vẫn chưa được biết đến.
Happy Green Kid Naps

@suprjami Tôi không chắc chắn 100% ý của bạn là "đối số mảng" , nhưng nếu bạn muốn nói int arr[n];thì đó là một VLA (mảng có độ dài thay đổi) , được thêm vào trong C99. Đó có phải ý của bạn?
RastaJedi

170

Câu trả lời ngắn ... nó phụ thuộc.

  1. Các biến cục bộ được xác định tĩnh không làm mất giá trị giữa các lệnh gọi hàm. Nói cách khác, chúng là các biến toàn cục, nhưng nằm trong phạm vi hàm cục bộ mà chúng được định nghĩa.

  2. Các biến toàn cục tĩnh không hiển thị bên ngoài tệp C mà chúng được xác định.

  3. Các hàm tĩnh không hiển thị bên ngoài tệp C mà chúng được xác định trong.


8
Vậy "hàm tĩnh" và "hàm riêng" có nghĩa là giống nhau không? Tương tự như vậy là "biến toàn cục tĩnh" và "biến toàn cục riêng" giống nhau?
dùng1599964

40
Đây là về C. Không có tư nhân / công khai tại C.
chris

19
@ user1599964 mặc dù không có privatetrong C, nhưng sự tương tự của bạn là tốt: tĩnh làm cho mọi thứ "riêng tư" với một tệp nhất định. Và các tệp trong C thường ánh xạ tới các lớp trong C ++.
Ciro Santilli 冠状 病毒 审查 事件

67

Ví dụ phạm vi biến đa tệp

Ở đây tôi minh họa cách tĩnh ảnh hưởng đến phạm vi định nghĩa hàm trên nhiều tệp.

AC

#include <stdio.h>

/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
/programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
*/
/*int i = 0;*/

/* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
/*int i;*/

/* OK: extern. Will use the one in main. */
extern int i;

/* OK: only visible to this file. */
static int si = 0;

void a() {
    i++;
    si++;
    puts("a()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

C chính

#include <stdio.h>

int i = 0;
static int si = 0;

void a();    

void m() {
    i++;
    si++;
    puts("m()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

int main() {
    m();
    m();
    a();
    a();
    return 0;
}

GitHub ngược dòng .

Biên dịch và chạy:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o

Đầu ra:

m()
i = 1
si = 1

m()
i = 2
si = 2

a()
i = 3
si = 1

a()
i = 4
si = 2

Diễn dịch

  • Có hai biến riêng biệt si, một biến cho mỗi tệp
  • có một biến được chia sẻ duy nhất cho i

Như thường lệ, phạm vi càng nhỏ thì càng tốt, vì vậy luôn luôn khai báo các biến staticnếu bạn có thể.

Trong lập trình C, các tệp thường được sử dụng để đại diện cho "các lớp" và staticcác biến đại diện cho các thành viên tĩnh riêng của lớp.

Những tiêu chuẩn nói về nó

C99 N1256 dự thảo 6.7.1 "Trình xác định lớp lưu trữ" nói rằng đó staticlà "trình xác định lớp lưu trữ".

6.2.2 / 3 "Liên kết của định danh" cho biết staticngụ ý internal linkage:

Nếu khai báo của một định danh phạm vi tệp cho một đối tượng hoặc một hàm chứa định danh lớp lưu trữ tĩnh, thì định danh có liên kết bên trong.

và 6.2.2 / 2 nói rằng internal linkagehành xử như trong ví dụ của chúng tôi:

Trong tập hợp các đơn vị dịch thuật và thư viện cấu thành toàn bộ chương trình, mỗi khai báo của một mã định danh cụ thể có liên kết bên ngoài biểu thị cùng một đối tượng hoặc chức năng. Trong một đơn vị dịch thuật, mỗi khai báo của một định danh có liên kết bên trong biểu thị cùng một đối tượng hoặc chức năng.

trong đó "đơn vị dịch là một tệp nguồn sau khi tiền xử lý.

GCC triển khai nó như thế nào cho ELF (Linux)?

Với sự STB_LOCALràng buộc.

Nếu chúng tôi biên dịch:

int i = 0;
static int si = 0;

và tháo rời bảng biểu tượng với:

readelf -s main.o

đầu ra chứa:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 si
 10: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 i

vì vậy sự ràng buộc là sự khác biệt đáng kể duy nhất giữa chúng. Valuechỉ là phần bù của họ vào .bssphần này, vì vậy chúng tôi hy vọng nó sẽ khác.

STB_LOCALđược ghi lại trên thông số ELF tại http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :

Các biểu tượng cục bộ không thể nhìn thấy bên ngoài tệp đối tượng chứa định nghĩa của chúng. Các ký hiệu cục bộ cùng tên có thể tồn tại trong nhiều tệp mà không can thiệp lẫn nhau

mà làm cho nó một sự lựa chọn hoàn hảo để đại diện static.

Các biến không có tĩnh là STB_GLOBALvà thông số kỹ thuật cho biết:

Khi trình soạn thảo liên kết kết hợp một số tệp đối tượng có thể định vị lại, nó không cho phép nhiều định nghĩa của các ký hiệu STB_GLOBAL có cùng tên.

phù hợp với các lỗi liên kết trên nhiều định nghĩa không tĩnh.

Nếu chúng tôi tăng cường tối ưu hóa với -O3,si biểu tượng sẽ bị xóa hoàn toàn khỏi bảng biểu tượng: không thể sử dụng nó từ bên ngoài. TODO tại sao giữ các biến tĩnh trên bảng ký hiệu khi không có tối ưu hóa? Chúng có thể được sử dụng cho bất cứ điều gì? Có lẽ để gỡ lỗi.

Xem thêm

Không gian tên ẩn danh C ++

Trong C ++, bạn có thể muốn sử dụng các không gian tên ẩn danh thay vì tĩnh, điều này đạt được hiệu ứng tương tự, nhưng ẩn thêm các định nghĩa kiểu: Không gian tên ẩn danh / ẩn danh so với các hàm tĩnh


39

Nó phụ thuộc:

int foo()
{
   static int x;
   return ++x;
}

Hàm sẽ trả về 1, 2, 3, v.v ... --- biến không nằm trên ngăn xếp.

AC:

static int foo()
{
}

Nó có nghĩa là chức năng này chỉ có phạm vi trong tệp này. Vì vậy, ac và bc có thể có foo()s khác nhau và foo không được tiếp xúc với các đối tượng được chia sẻ. Vì vậy, nếu bạn đã xác định foo trong ac, bạn không thể truy cập nó từ b.choặc từ bất kỳ nơi nào khác.

Trong hầu hết các thư viện C, tất cả các hàm "riêng tư" đều tĩnh và hầu hết "công khai" thì không.


18
+1 để đề cập đến x không phải trên stack hoặc heap. Đó là trên không gian bộ nhớ tĩnh.
Gob00st

1
@ Không gian bộ nhớ tĩnh? bạn có nghĩa là "Phân đoạn dữ liệu" ...?
Yousha Aleayoub

24

Mọi người cứ nói rằng 'tĩnh' trong C có hai nghĩa. Tôi cung cấp một cách khác để xem nó mang lại cho nó một ý nghĩa duy nhất:

  • Áp dụng 'tĩnh' cho một vật phẩm buộc vật phẩm đó phải có hai thuộc tính: (a) Nó không hiển thị ngoài phạm vi hiện tại; (b) Nó dai dẳng.

Lý do dường như có hai ý nghĩa là, trong C, mọi mục mà 'tĩnh' có thể được áp dụng đã có một trong hai thuộc tính này , vì vậy có vẻ như như việc sử dụng cụ thể đó chỉ liên quan đến mục đích khác.

Ví dụ, hãy xem xét các biến. Các biến được khai báo bên ngoài các hàm đã có tính bền vững (trong phân đoạn dữ liệu), do đó, việc áp dụng 'tĩnh' chỉ có thể làm cho chúng không hiển thị bên ngoài phạm vi hiện tại (đơn vị biên dịch). Ngược lại, các biến được khai báo bên trong các hàm đã không nhìn thấy được bên ngoài phạm vi (hàm) hiện tại, do đó, việc áp dụng 'tĩnh' chỉ có thể khiến chúng tồn tại lâu.

Áp dụng 'tĩnh' cho các chức năng cũng giống như áp dụng nó cho các biến toàn cục - mã nhất thiết phải tồn tại (ít nhất là trong ngôn ngữ), do đó chỉ có thể thay đổi mức độ hiển thị.

LƯU Ý: Những nhận xét này chỉ áp dụng cho C. Trong C ++, áp dụng 'tĩnh' cho các phương thức lớp thực sự mang lại cho từ khóa một ý nghĩa khác. Tương tự cho phần mở rộng đối số mảng C99.


(A) của bạn là dư thừa tốt nhất. Không có biến nào được nhìn thấy bên ngoài phạm vi của nó. Đó chỉ đơn giản là định nghĩa về phạm vi. Ý bạn là gì được gọi là liên kết trong Tiêu chuẩn C. staticcung cấp liên kết nội bộ cho một định danh.
Jens

16

Từ Wikipedia:

Trong ngôn ngữ lập trình C, static được sử dụng với các biến và hàm toàn cục để đặt phạm vi của chúng thành tệp chứa. Trong các biến cục bộ, static được sử dụng để lưu trữ biến trong bộ nhớ được cấp phát tĩnh thay vì bộ nhớ được cấp phát tự động. Mặc dù ngôn ngữ không ra lệnh thực hiện một trong hai loại bộ nhớ, bộ nhớ được cấp phát tĩnh thường được dành riêng trong phân đoạn dữ liệu của chương trình vào thời gian biên dịch, trong khi bộ nhớ được cấp phát tự động thường được triển khai như một ngăn xếp cuộc gọi tạm thời.


16

static có nghĩa là những thứ khác nhau trong bối cảnh khác nhau.

  1. Bạn có thể khai báo một biến tĩnh trong hàm C. Biến này chỉ hiển thị trong hàm tuy nhiên nó hoạt động như một toàn cục ở chỗ nó chỉ được khởi tạo một lần và nó vẫn giữ nguyên giá trị của nó. Trong ví dụ này, mỗi khi bạn gọi foo()nó sẽ in số ngày càng tăng. Biến tĩnh chỉ được khởi tạo một lần.

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
  2. Một cách sử dụng tĩnh khác là khi bạn triển khai một hàm hoặc biến toàn cục trong tệp .c nhưng không muốn biểu tượng của nó hiển thị bên ngoài .objtệp được tạo bởi tệp. ví dụ

    static void foo() { ... }

8

Nếu bạn khai báo một biến trong hàm tĩnh, giá trị của nó sẽ không được lưu trên ngăn xếp lệnh gọi hàm và vẫn sẽ khả dụng khi bạn gọi lại hàm.

Nếu bạn khai báo một biến toàn cục tĩnh, phạm vi của nó sẽ bị giới hạn trong tệp mà bạn đã khai báo. Điều này là an toàn hơn một chút so với toàn cầu thông thường có thể được đọc và sửa đổi trong toàn bộ chương trình của bạn.


8

Tôi ghét phải trả lời một câu hỏi cũ, nhưng tôi không nghĩ có ai đã đề cập đến cách K & R giải thích nó trong phần A4.1 của "Ngôn ngữ lập trình C".

Nói tóm lại, từ tĩnh được sử dụng với hai nghĩa:

  1. Tĩnh là một trong hai lớp lưu trữ (lớp kia là tự động). Một đối tượng tĩnh giữ giá trị của nó giữa các yêu cầu. Các đối tượng được khai báo bên ngoài tất cả các khối luôn tĩnh và không thể được thực hiện tự động.
  2. Nhưng, khi static từ khóa (nhấn mạnh lớn vào nó được sử dụng trong mã làm từ khóa) được sử dụng với một khai báo, nó cung cấp cho liên kết nội bộ đối tượng đó để nó chỉ có thể được sử dụng trong đơn vị dịch thuật đó. Nhưng nếu từ khóa được sử dụng trong một hàm, nó sẽ thay đổi lớp lưu trữ của đối tượng (dù sao đối tượng sẽ chỉ hiển thị trong hàm đó). Đối lập với tĩnh là externtừ khóa, cung cấp cho một liên kết bên ngoài đối tượng.

Peter Van Der Linden đưa ra hai ý nghĩa này trong "Lập trình C chuyên gia":

  • Bên trong một chức năng, giữ lại giá trị của nó giữa các cuộc gọi.
  • Ở cấp độ chức năng, chỉ hiển thị trong tập tin này.

Có một lớp lưu trữ thứ ba, đăng ký . Một số người cũng tạo ra trường hợp cho một lớp lưu trữ thứ tư, được phân bổ , cho việc lưu trữ được trả về bởi malloc và bạn bè.
Jens

@Jens 'register' chỉ là một gợi ý cho trình biên dịch; đăng ký lưu trữ không thể được thực thi từ bên trong nguồn C. Vì vậy, tôi sẽ không coi nó là một lớp lưu trữ.
GermanNerd

1
@GermanNerd Tôi sợ Tiêu chuẩn ISO C không đồng ý với quan điểm của bạn, vì nó rõ ràng tạo ra registermột công cụ xác định lớp lưu trữ (C99 6.7.1 Trình xác định lớp lưu trữ). Và nó không chỉ là một gợi ý, ví dụ bạn không thể áp dụng địa chỉ của toán tử &trên một đối tượng với lớp lưu trữ registerbất kể trình biên dịch có phân bổ một thanh ghi hay không.
Jens

@Jens Cảm ơn đã nhắc nhở tôi về &. Tôi có thể đã làm quá nhiều C ++ ..... Dù sao đi nữa, trong khi 'register' là một công cụ xác định lớp lưu trữ, trong thực tế, trình biên dịch có thể sẽ tạo cùng một mã máy cho công cụ xác định 'tự động' vô dụng như đối với 'đăng ký' 'chuyên gia. Vì vậy, điều duy nhất còn lại là hạn chế mức mã nguồn không thể lấy địa chỉ. BTW, cuộc thảo luận nhỏ này đã khiến tôi tìm thấy một lỗi trong Netbeans; kể từ bản cập nhật mới nhất của tôi, nó mặc định là chuỗi công cụ g ++ trong các dự án C mới!
GermanNerd 17/07/19

6

Trong C, static có hai nghĩa, tùy thuộc vào phạm vi sử dụng của nó. Trong phạm vi toàn cầu, khi một đối tượng được khai báo ở cấp tệp, điều đó có nghĩa là đối tượng đó chỉ hiển thị trong tệp đó.

Ở bất kỳ phạm vi nào khác, nó khai báo một đối tượng sẽ giữ giá trị của nó giữa các thời điểm khác nhau mà phạm vi cụ thể được nhập. Ví dụ: nếu một int được hủy trong một thủ tục:

void procedure(void)
{
   static int i = 0;

   i++;
}

giá trị của 'i' được khởi tạo về 0 trong lần gọi đầu tiên tới thủ tục và giá trị được giữ lại mỗi lần tiếp theo thủ tục được gọi. nếu 'i' được in, nó sẽ tạo ra một chuỗi 0, 1, 2, 3, ...


5

Điều quan trọng cần lưu ý là các biến tĩnh trong các hàm được khởi tạo ở lần nhập đầu tiên vào hàm đó và tồn tại ngay cả sau khi cuộc gọi của chúng kết thúc; trong trường hợp các hàm đệ quy, biến tĩnh chỉ được khởi tạo một lần và vẫn tồn tại trên tất cả các cuộc gọi đệ quy và ngay cả sau khi cuộc gọi của hàm kết thúc.

Nếu biến đã được tạo bên ngoài hàm, điều đó có nghĩa là lập trình viên chỉ có thể sử dụng biến trong tệp nguồn mà biến đã được khai báo.


5

Nếu bạn khai báo điều này trong một mytest.ctệp:

static int my_variable;

Sau đó, biến này chỉ có thể được nhìn thấy từ tập tin này. Các biến không thể được xuất bất cứ nơi nào khác.

Nếu bạn khai báo bên trong một hàm, giá trị của biến sẽ giữ giá trị của nó mỗi khi hàm được gọi.

Một chức năng tĩnh không thể được xuất từ ​​bên ngoài tệp. Vì vậy, trong một *.ctệp, bạn đang ẩn các hàm và các biến nếu bạn khai báo chúng tĩnh.


4

Các biến tĩnh trong C có thời gian tồn tại của chương trình.

Nếu được định nghĩa trong một hàm, chúng có phạm vi cục bộ, tức là chúng chỉ có thể được truy cập bên trong các hàm đó. Giá trị của các biến tĩnh được bảo toàn giữa các lệnh gọi hàm.

Ví dụ:

void function()
{
    static int var = 1;
    var++;
    printf("%d", var);
}

int main()
{
    function(); // Call 1
    function(); // Call 2
}

Trong chương trình trên, var được lưu trữ trong phân đoạn dữ liệu. Cuộc đời của nó là toàn bộ chương trình C.

Sau chức năng gọi 1, vartrở thành 2. Sau chức năng gọi 2, vartrở thành 3.

Giá trị của varkhông bị phá hủy giữa các lệnh gọi hàm.

Nếu varcó giữa biến không tĩnh và biến cục bộ, nó sẽ được lưu trữ trong phân đoạn ngăn xếp trong chương trình C. Vì khung ngăn xếp của hàm bị hủy sau khi hàm trả về, giá trị của varcũng bị hủy.

Các biến tĩnh khởi tạo được lưu trữ trong phân đoạn dữ liệu của chương trình C trong khi các biến chưa được khởi tạo được lưu trữ trong phân đoạn BSS.

Một thông tin khác về tĩnh: Nếu một biến là toàn cục và tĩnh, nó có thời gian tồn tại của chương trình C, nhưng nó có phạm vi tệp. Nó chỉ được nhìn thấy trong tập tin đó.

Để thử điều này:

file1.c

static int x;

int main()
{
    printf("Accessing in same file%d", x):
}

file2.c

    extern int x;
    func()
    {
        printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
    }

run gcc -c file1.c

gcc -c file2.c

Bây giờ hãy thử liên kết chúng bằng cách sử dụng:

gcc -o output file1.o file2.o

Nó sẽ đưa ra một lỗi liên kết vì x có phạm vi tệp của file1.c và trình liên kết sẽ không thể giải quyết tham chiếu đến biến x được sử dụng trong file2.c.

Người giới thiệu:

  1. http://en.wikipedia.org/wiki/Translation_unit_(programming)
  2. http://en.wikipedia.org/wiki/Call_stack

Tôi hiểu rằng dữ liệu là liên tục, có nghĩa là nó sẽ không bị mất sau mỗi lần gọi hàm, nhưng tại sao không static int var = 1;thay đổi giá trị trở lại mỗi lần
Eames

3

Biến tĩnh là một biến đặc biệt mà bạn có thể sử dụng trong một hàm và nó lưu dữ liệu giữa các cuộc gọi và nó không xóa nó giữa các cuộc gọi. Ví dụ:

void func(){
    static int count; // If you don't declare its value, the value automatically initializes to zero
    printf("%d, ", count);
    ++count;
}

void main(){
    while(true){
        func();
    }
}

Đầu ra:

0, 1, 2, 3, 4, 5, ...


Bạn có thể thay thế printf("%d, ", count); count++;bằng `printf ("% d, ", đếm ++) (không quan trọng: P).
RastaJedi

2

Một giá trị biến tĩnh vẫn tồn tại giữa các lệnh gọi và phạm vi chức năng khác nhau được giới hạn ở khối cục bộ, một var tĩnh luôn khởi tạo với giá trị 0


2

Có 2 trường hợp:

(1) Các biến cục bộ được khai báo static: Được phân bổ trong phân đoạn dữ liệu thay vì ngăn xếp. Giá trị của nó được giữ lại khi bạn gọi lại hàm.

(2) Các biến hoặc hàm toàn cục được khai báo static: Vô hình bên ngoài đơn vị biên dịch (tức là các ký hiệu cục bộ trong bảng ký hiệu trong khi liên kết).


1

Biến tĩnh có đặc tính bảo toàn giá trị của chúng ngay cả sau khi chúng nằm ngoài phạm vi của chúng! Do đó, biến tĩnh bảo toàn giá trị trước đó trong phạm vi trước của chúng và không được khởi tạo lại trong phạm vi mới.

Nhìn vào ví dụ này - Một biến int tĩnh vẫn còn trong bộ nhớ trong khi chương trình đang chạy. Một biến bình thường hoặc tự động bị hủy khi một hàm gọi nơi biến được khai báo kết thúc.

#include<stdio.h> 
int fun() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("%d ", fun()); 
  printf("%d ", fun()); 
  return 0; 
}

Điều này sẽ xuất ra: 1 2

Khi 1 ở lại trong bộ nhớ vì nó được khai báo là tĩnh

Biến tĩnh (như biến toàn cục) được khởi tạo là 0 nếu không được khởi tạo rõ ràng. Ví dụ trong chương trình dưới đây, giá trị của x được in là 0, trong khi giá trị của y là thứ gì đó rác. Xem điều này để biết thêm chi tiết.

#include <stdio.h> 
int main() 
{ 
    static int x; 
    int y; 
    printf("%d \n %d", x, y); 
}

Điều này sẽ xuất ra: 0 [some_garbage_value]

Đây là những vấn đề chính tôi thấy không được giải thích ở trên cho một người mới!


-1

Trong lập trình C, staticlà một từ khóa dành riêng kiểm soát cả đời cũng như khả năng hiển thị. Nếu chúng ta khai báo một biến là tĩnh bên trong một hàm thì nó sẽ chỉ hiển thị trong suốt hàm đó. Trong cách sử dụng này, thời gian tồn tại của biến tĩnh này sẽ bắt đầu khi một hàm gọi và nó sẽ hủy sau khi thực hiện hàm đó. bạn có thể xem ví dụ sau:

#include<stdio.h> 
int counterFunction() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("First Counter Output = %d\n", counterFunction()); 
  printf("Second Counter Output = %d ", counterFunction()); 
  return 0; 
}

Chương trình trên sẽ cung cấp cho chúng tôi Kết quả này:

First Counter Output = 1 
Second Counter Output = 1 

Bởi vì ngay khi chúng ta gọi hàm, nó sẽ khởi tạo count = 0. Và trong khi chúng ta thực thi counterFunctionnó sẽ phá hủy biến đếm.


2
> Chương trình trên sẽ cung cấp cho chúng tôi Đầu ra này: Đầu ra bộ đếm đầu tiên = 1 Đầu ra bộ đếm thứ hai = 1 <Không đúng. Biến tĩnh chỉ được khởi tạo một lần. Vì vậy, đầu ra sẽ là 1, sau đó 2, và như vậy.
GermanNerd
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.