Biến tĩnh bên trong một hàm trong C


119

Những gì sẽ được in ra? 6 6 hoặc 6 7? Và tại sao?

void foo()
{
    static int x = 5;
    x++;
    printf("%d", x);
}

int main()
{
    foo();
    foo();
    return 0;
}

54
Có vấn đề gì để thử?
Andrew

12
Bạn đã thử gõ cái này vào và tự mình xem?
wilhelmtell

20
Tôi muốn hiểu tại sao.
Vadiklk

7
@Vadiklk nên đặt câu hỏi bắt đầu bằng "Tại sao"
Andrey

1
Ideone.com/t9Bbe Bạn mong đợi điều gì? Kết quả không phù hợp với sự khám phá của bạn? Tại sao bạn mong đợi kết quả của bạn?
eckes

Câu trả lời:


186

Có hai vấn đề ở đây, thời gian tồn tại và phạm vi.

Phạm vi của biến là nơi tên biến có thể được nhìn thấy. Ở đây, x chỉ hiển thị bên trong hàm foo ().

Thời gian tồn tại của một biến là khoảng thời gian mà nó tồn tại. Nếu x được định nghĩa mà không có từ khóa static, thời gian tồn tại sẽ là từ giá trị nhập vào foo () cho đến khi trả về từ foo (); vì vậy nó sẽ được khởi tạo lại thành 5 trong mỗi cuộc gọi.

Từ khóa static có tác dụng kéo dài thời gian tồn tại của một biến thành thời gian tồn tại của chương trình; ví dụ: việc khởi tạo chỉ xảy ra một lần và một lần duy nhất và sau đó biến vẫn giữ nguyên giá trị của nó - bất kể nó là gì - qua tất cả các lần gọi foo () trong tương lai.


15
@devanl, đúng vậy.
orion elenzil

1
Đơn giản và logic :)
Dimitar Vukman

trong những trường hợp nào chúng ta cần khai báo một biến là tĩnh bên trong một hàm ?, chỉ tò mò muốn biết vì tôi chưa sử dụng nó trước đây?
Akay

tôi muốn nói lời cảm ơn, nhưng tất cả điều này đã được trả lời ở phần đầu của trang. nó làm tôi buồn cười rằng mọi người không chỉ chạy mã của riêng họ. xD
Puddle

Câu trả lời này là sai. Thời điểm bạn nghĩ về các hàm đệ quy, các định nghĩa như được mô tả ở đây không giải thích hành vi!
Philip Couling

52

Đầu ra : 6 7

Lý do : biến tĩnh chỉ được khởi tạo một lần (không giống như biến tự động) và định nghĩa thêm về biến tĩnh sẽ bị bỏ qua trong thời gian chạy. Và nếu nó không được khởi tạo theo cách thủ công, nó sẽ được khởi tạo bằng giá trị 0 tự động. Vì thế,

void foo() {
    static int x = 5; // assigns value of 5 only once
    x++;
    printf("%d", x);
}

int main() {
    foo(); // x = 6
    foo(); // x = 7
    return 0;
}

10

6 7

trình biên dịch sắp xếp việc khởi tạo biến tĩnh không xảy ra mỗi khi hàm được nhập


10

Điều đó cũng giống như có chương trình sau:

static int x = 5;

void foo()
{
    x++;
    printf("%d", x);
}

int main()
{
     foo();
     foo();
     return 0;
}

Tất cả những gì mà từ khóa static thực hiện trong chương trình đó là nó nói với trình biên dịch (về cơ bản) 'này, tôi có một biến ở đây mà tôi không muốn bất kỳ ai khác truy cập, đừng nói với bất kỳ ai khác rằng nó tồn tại'.

Bên trong một phương thức, từ khóa static sẽ thông báo cho trình biên dịch giống như ở trên, ngoài ra, 'đừng nói với bất kỳ ai rằng nó tồn tại bên ngoài hàm này, nó chỉ có thể truy cập được bên trong hàm này'.

Tôi hi vọng cái này giúp được


13
Chà, nó không thực sự giống nhau. Vẫn còn vấn đề về phạm vi trên X. Trong ví dụ này, bạn có thể chọc ngoáy và tương lai với xtrong main; nó là toàn cầu. Trong ví dụ ban đầu xlà cục bộ đối với foo, chỉ hiển thị khi ở bên trong khối đó, điều này nói chung là thích hợp hơn: nếu foo tồn tại để duy trì xtheo những cách có thể dự đoán và nhìn thấy được, thì việc để người khác chọc phá nó nói chung là nguy hiểm. Như một lợi ích khác của việc giữ nó trong phạm vi foo() Nó cũng giữ cho foo()tính di động.
user2149140

2
@ user2149140 'không nói cho ai biết rằng đây tồn tại bên ngoài của chức năng này, nó chỉ nên được truy cập bên trong chức năng này'
DCShannon

3
Mặc dù bạn đã giải quyết vấn đề về phạm vi do biến được khai báo ở đâu, nhưng việc mô tả static như ảnh hưởng đến phạm vi, thay vì thời gian tồn tại, dường như không chính xác.
DCShannon

1
@Chameleon Câu hỏi được gắn thẻ là c, vì vậy trong ngữ cảnh này, ví dụ của bạn sẽ là bất hợp pháp ở phạm vi toàn cầu. (C yêu cầu bộ khởi tạo liên tục cho hình cầu, C ++ thì không).
Richard J. Ross III,

5

Một biến tĩnh bên trong một hàm có tuổi thọ miễn là chương trình của bạn chạy. Nó sẽ không được cấp phát mỗi khi hàm của bạn được gọi và được phân bổ khi hàm của bạn trả về.


Nói điều này giống như một biến "toàn cầu" và sau đó nói NGOẠI TRỪ bạn không thể truy cập nó là một oxymoron. Toàn cầu có nghĩa là có thể truy cập ở mọi nơi. Mà trong trường hợp này của hàm INSIDE tĩnh, nó KHÔNG thể truy cập được ở mọi nơi. Vấn đề trong OP như những người khác đã lưu ý là về phạm vi và thời gian tồn tại. Xin đừng nhầm lẫn với mọi người khi sử dụng thuật ngữ 'toàn cầu' và gây hiểu lầm cho chúng về phạm vi của biến.
ChuckB

@ChuckB: Đúng. Đã sửa nó. Đã 6 năm rồi. Câu trả lời trước đây của tôi có nhận thức của 6 năm trước!
Donotalo

5

Sản lượng: 6,7

Lý do

Khai báo của xlà bên trong foonhưng x=5khởi tạo diễn ra bên ngoài của foo!

Điều chúng ta cần hiểu ở đây là

static int x = 5;

không giống như

static int x;
x = 5;

Các câu trả lời khác đã sử dụng các từ quan trọng ở đây, phạm vi và thời gian tồn tại, và chỉ ra rằng phạm vi xlà từ điểm khai báo của nó trong hàm foođến cuối hàm foo. Ví dụ: tôi đã kiểm tra bằng cách di chuyển khai báo đến cuối hàm và điều đó làm cho câu lệnh xkhông được khai x++;báo.

Vì vậy, phần static int x(phạm vi) của câu lệnh thực sự áp dụng ở nơi bạn đọc nó, ở đâu đó BÊN TRONG của hàm và chỉ từ đó trở đi, không phải bên trên nó bên trong hàm.

Tuy nhiên, phần x = 5(thời gian tồn tại) của câu lệnh là khởi tạo biến và diễn ra BÊN NGOÀI của hàm như một phần của quá trình tải chương trình. Biến xđược sinh ra với giá trị là 5khi chương trình tải.

Tôi đã đọc điều này trong một trong các nhận xét: " Ngoài ra, điều này không giải quyết phần thực sự khó hiểu, đó là thực tế là trình khởi tạo bị bỏ qua trong các cuộc gọi tiếp theo. " Nó bị bỏ qua trên tất cả các cuộc gọi. Việc khởi tạo biến nằm ngoài mã hàm thích hợp.

Về mặt lý thuyết, giá trị của 5 được đặt bất kể foo có được gọi hay không, mặc dù trình biên dịch có thể tối ưu hóa hàm đi nếu bạn không gọi nó ở bất kỳ đâu. Giá trị của 5 phải nằm trong biến trước khi foo được gọi.

Bên trong foo, câu lệnh static int x = 5;không có khả năng tạo ra bất kỳ mã nào.

Tôi tìm thấy địa chỉ được xsử dụng khi tôi đặt một hàm foovào một chương trình của mình và sau đó (chính xác) đoán rằng vị trí tương tự sẽ được sử dụng nếu tôi chạy lại chương trình. Ảnh chụp một phần màn hình bên dưới cho thấy xcó giá trị 5ngay cả trước cuộc gọi đầu tiên tới foo.

Điểm ngắt trước khi gọi foo đầu tiên


2

Đầu ra sẽ là 6 7. Một biến tĩnh (cho dù bên trong một hàm hay không) được khởi tạo chính xác một lần, trước khi bất kỳ hàm nào trong đơn vị dịch đó thực thi. Sau đó, nó vẫn giữ nguyên giá trị cho đến khi được sửa đổi.


1
Bạn có chắc chắn rằng static được khởi tạo trước khi hàm được gọi chứ không phải khi gọi hàm đầu tiên?
Jesse Pepper

@JessePepper: Ít nhất nếu bộ nhớ phục vụ, điều này phụ thuộc vào việc bạn đang nói về C ++ 98/03 hay C ++ 11. Trong C ++ 98/03, tôi tin rằng nó như được mô tả ở trên. Trong C ++ 11, phân luồng làm cho điều đó về cơ bản là không thể thực hiện được, do đó, việc khởi tạo được thực hiện khi nhập hàm đầu tiên.
Jerry Coffin,

2
Tôi nghĩ rằng bạn thực sự sai. Tôi nghĩ rằng ngay cả trước C ++ 11 nó cũng chỉ được khởi tạo khi hàm được gọi. Điều này rất quan trọng đối với một giải pháp chung cho vấn đề phụ thuộc khởi tạo tĩnh.
Jesse Pepper,

2

Vadiklk,

Tại sao ...? Lý do là biến static chỉ được khởi tạo một lần và duy trì giá trị của nó trong suốt chương trình. nghĩa là, bạn có thể sử dụng biến tĩnh giữa các lần gọi hàm. nó cũng có thể được sử dụng để đếm "bao nhiêu lần một hàm được gọi"

main()
{
   static int var = 5;
   printf("%d ",var--);
   if(var)
      main();
} 

và câu trả lời là 5 4 3 2 1 chứ không phải 5 5 5 5 5 5 .... (vòng lặp vô hạn) như bạn đang mong đợi. Một lần nữa, lý do là biến static được khởi tạo một lần, khi lần sau gọi hàm main () thì nó sẽ không được khởi tạo thành 5 vì nó đã được khởi tạo sẵn trong chương trình nên ta có thể thay đổi giá trị nhưng không thể khởi động lại. Đó là cách hoạt động của biến tĩnh.

hoặc bạn có thể coi là theo bộ lưu trữ: các biến tĩnh được lưu trữ trên Phần dữ liệu của một chương trình và các biến được lưu trữ trong Phần dữ liệu được khởi tạo một lần. và trước khi khởi tạo chúng được giữ trong phần BSS.

Đến lượt nó, các biến Tự động (cục bộ) được lưu trữ trên Stack và tất cả các biến trên ngăn xếp được khởi động lại mọi lúc khi hàm được gọi là FAR mới (bản ghi kích hoạt hàm) được tạo cho điều đó.

OK để hiểu thêm, hãy làm ví dụ trên mà không có "static" và cho bạn biết đầu ra sẽ là gì. Điều đó làm cho bạn hiểu sự khác biệt giữa hai điều này.

Cảm ơn Javed


1

Hãy chỉ đọc bài viết Wikipedia về Biến tĩnh ...

Biến cục bộ tĩnh: các biến được khai báo là tĩnh bên trong một hàm được cấp phát tĩnh trong khi có cùng phạm vi với các biến cục bộ tự động. Do đó, bất kỳ giá trị nào mà hàm đặt vào các biến cục bộ tĩnh của nó trong một lần gọi sẽ vẫn hiện diện khi hàm được gọi lại.


5
Đó là khủng khiếp! "các biến được khai báo là tĩnh bên trong một hàm được cấp phát tĩnh" - nó không giải thích gì, trừ khi bạn đã biết ý nghĩa của nó!

@Blank: tốt, đó là những gì tôi nghĩ câu thứ hai. Mặc dù tôi đoán bạn đúng, nó nên được nói tốt hơn.
Andrew White

Ngoài ra, điều này không giải quyết phần thực sự khó hiểu, đó là thực tế là trình khởi tạo bị bỏ qua trong các cuộc gọi tiếp theo.
Tom Auger

được phân bổ tĩnh có nghĩa là không có ngăn xếp, cũng không phải đống.
Chameleon

1

Bạn sẽ nhận được 6 7 được in, như được kiểm tra dễ dàng, và đây là lý do: Khi foo được gọi lần đầu tiên, biến tĩnh x được khởi tạo thành 5. Sau đó, nó được tăng lên thành 6 và được in.

Bây giờ cho cuộc gọi tiếp theo tới foo. Chương trình bỏ qua việc khởi tạo biến tĩnh và thay vào đó sử dụng giá trị 6 đã được gán cho x lần trước. Quá trình thực thi diễn ra bình thường, cho bạn giá trị 7.


1
6 7

x là một biến toàn cục chỉ hiển thị từ foo (). 5 là giá trị ban đầu của nó, như được lưu trữ trong phần .data của mã. Mọi sửa đổi tiếp theo sẽ ghi đè giá trị trước đó. Không có mã gán nào được tạo trong thân hàm.


1

6 và 7 Vì biến static intialise chỉ một lần, nên 5 ++ trở thành 6 ở lần gọi thứ nhất 6 ++ trở thành 7 ở lần gọi thứ hai Lưu ý-khi lần gọi thứ hai xảy ra, giá trị x là 6 thay vì 5 vì x là biến tĩnh.


0

Trong C ++ 11, ít nhất khi biểu thức được sử dụng để khởi tạo biến tĩnh cục bộ không phải là 'constexpr' (trình biên dịch không thể đánh giá), thì việc khởi tạo phải diễn ra trong lần gọi hàm đầu tiên. Ví dụ đơn giản nhất là sử dụng trực tiếp một tham số để intialize biến tĩnh cục bộ. Do đó, trình biên dịch phải phát ra mã để đoán xem cuộc gọi có phải là lệnh đầu tiên hay không, do đó yêu cầu một biến boolean cục bộ. Tôi đã biên dịch ví dụ như vậy và kiểm tra điều này có đúng không bằng cách xem mã lắp ráp. Ví dụ có thể như thế này:

void f( int p )
{
  static const int first_p = p ;
  cout << "first p == " << p << endl ;
}

void main()
{
   f(1); f(2); f(3);
}

tất nhiên, khi giải pháp là 'constexpr', thì điều này không bắt buộc và biến có thể được khởi tạo khi tải chương trình bằng cách sử dụng một giá trị được trình biên dịch lưu trữ trong mã hợp ngữ đầu ra.

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.