C'de bir fonksiyonun içindeki statik değişken


119

Ne yazdırılacak? 6 6 veya 6 7? Ve neden?

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

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

54
Denenecek sorun nedir?
Andrew

12
Bunu yazıp kendiniz görmeyi denediniz mi?
wilhelmtell

21
Nedenini anlamak istiyorum.
Vadiklk

7
@Vadiklk "Neden" ile başlayarak soru sor
Andrey

1
ideone.com/t9Bbe Ne bekliyorsunuz? Sonuç beklentilerinize uymuyor mu? Neden sonucunu bekliyordun?
eckes

Yanıtlar:


187

Burada iki konu var, ömür ve kapsam.

Değişkenin kapsamı, değişken adının görülebildiği yerdir. Burada x yalnızca foo () işlevi içinde görülebilir.

Bir değişkenin yaşam süresi, var olduğu dönemdir. Eğer x, static anahtar sözcüğü olmadan tanımlandıysa, yaşam süresi foo () girişinden foo () dönüşüne kadar olacaktır; bu nedenle her aramada 5 olarak yeniden başlatılır.

Statik anahtar sözcüğü, bir değişkenin ömrünü programın yaşam süresine uzatır; Örneğin, başlatma yalnızca bir kez ve bir kez gerçekleşir ve sonra değişken - ne olursa olsun - gelecekteki tüm foo () çağrılarında değerini korur.


15
@devanl, evet öyleyiz.
orion elenzil

1
Simple and logical :)
Dimitar Vukman

Hangi senaryolarda bir değişkeni bir fonksiyonun içinde statik olarak ilan etmemiz gerekiyor? Bunu daha önce kullanmadığım için merak mı ediyorsunuz?
Akay

teşekkürler derdim ama bunların hepsi sayfanın en başında yanıtlandı. insanların sadece kendi kodlarını çalıştırmaması beni güldürüyor. xD
Puddle

Bu cevap yanlış. Özyinelemeli fonksiyonları düşündüğünüz an, burada açıklanan tanımlar davranışı açıklamaz!
Philip Couling

53

Çıkış : 6 7

Nedeni : statik değişken yalnızca bir kez başlatılır (otomatik değişkenin aksine) ve statik değişkenin daha fazla tanımı çalışma sırasında atlanır. Ve manuel olarak başlatılmazsa, otomatik olarak 0 değeri ile başlatılır. Yani,

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

derleyici, işlev her girildiğinde statik değişken başlatmanın gerçekleşmeyeceğini düzenler


10

Bu, aşağıdaki programa sahip olmakla aynıdır:

static int x = 5;

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

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

Statik anahtar kelimenin o programda yaptığı tek şey, derleyiciye (esasen) 'hey, burada başka kimsenin erişmesini istemediğim bir değişkenim var, onun var olduğunu kimseye söylemeyin' demesidir.

Bir yöntemin içinde, static anahtar sözcüğü derleyiciye yukarıdakiyle aynı şeyi söyler, ancak aynı zamanda 'kimseye bunun bu işlevin dışında olduğunu söylemeyin, yalnızca bu işlev içinde erişilebilir olmalıdır'.

Umarım bu yardımcı olur


13
Aslında aynı değil. X'te hala kapsam sorunu var. Bu örnekte, xesas olarak dürtebilir ve futz yapabilirsiniz ; küreseldir. Orijinal örnekte xlocal to foo idi, sadece bu bloğun içindeyken görülebilir, ki bu genellikle tercih edilir: eğer foo xöngörülebilir ve görünür yollarla sürdürmek için varsa , o zaman başkalarının onu dürtmesine izin vermek genellikle tehlikelidir. Kapsamda tutmanın bir başka yararı foo() da foo()taşınabilirliğini koruyor.
user2149140

2
@ user2149140 'bunun bu işlevin dışında var olduğunu kimseye söylemeyin, yalnızca bu işlev içinde erişilebilir olmalıdır'
DCShannon

3
Değişkenin bildirildiği yer nedeniyle kapsam sorununu ele almış olsanız da, yaşam süresinden ziyade kapsamı etkileyen statik tanımı yanlış görünüyor.
DCShannon

1
@Chameleon Soru olarak etiketlendi c, bu nedenle bu bağlamda, örneğiniz küresel kapsamda yasadışı olacaktır. (C küreseller için sabit başlatıcılar gerektirir, C ++ gerektirmez).
Richard J. Ross III

5

Bir işlevin içindeki statik bir değişkenin, programınız çalıştığı sürece bir ömrü vardır. İşleviniz her çağrıldığında ve işleviniz geri döndüğünde serbest bırakıldığında tahsis edilmeyecektir.


Bunun "global" bir değişken olduğunu söylemek ve sonra EXCEPT ona erişemezsin demek bir tezattır. Küresel, her yerden erişilebilir demektir. Bu durumda bir statik İÇİNDE işlev olan her yerde erişilemez. Başkalarının da belirttiği gibi, OP'deki mesele kapsam ve ömürle ilgilidir. Lütfen insanları 'global' terimini kullanmakla ve onları değişkenin kapsamı konusunda yanıltmakla karıştırmayın.
ChuckB

@ChuckB: Doğru. Onu düzeltti. 6 yıl oldu. Önceki cevabım 6 yıl önceki algıya sahipti!
Donotalo

5

Çıktı: 6,7

neden

Beyanı xiçeride, fooancak x=5başlatma dışında gerçekleşiyor foo!

Burada anlamamız gereken şey şudur:

static int x = 5;

ile aynı değil

static int x;
x = 5;

Diğer cevaplar burada önemli kelimeleri, kapsam ve yaşam süresini kullanmış ve kapsamının xişlevdeki bildirimi noktasından işlevin foosonuna kadar olduğuna işaret etmiştir foo. Örneğin, bildirimi işlevin sonuna taşıyarak kontrol ettim ve bu x, x++;ifadede bildirilmemiş hale getiriyor .

Dolayısıyla, static int xifadenin (kapsam) kısmı, onu okuduğunuz yerde , işlevin İÇİNDE bir yerde ve yalnızca oradan itibaren geçerlidir, işlevin içinde değil.

Bununla birlikte x = 5ifadesinin (ömrü) bir parçası olan değişken başlatma ve oluyor dışında program yükleme parçası olarak işlev. Değişken x, 5programın ne zaman yüklendiğinin bir değeriyle doğar .

Bunu yorumlardan birinde okudum: " Ayrıca, bu gerçekten kafa karıştırıcı kısma değinmiyor, bu da başlatıcının sonraki aramalarda atlanması gerçeğidir. " Tüm aramalarda atlanır. Değişkenin ilklendirilmesi uygun fonksiyon kodunun dışındadır.

5'in değeri teorik olarak foo'nun çağrılıp çağrılmadığına bakılmaksızın ayarlanır, ancak bir derleyici işlevi herhangi bir yerde çağırmazsanız işlevi dışarıda optimize edebilir. Foo çağrılmadan önce 5'in değeri değişkenin içinde olmalıdır.

İçinde foo, ifadenin static int x = 5;herhangi bir kod üretmesi olası değildir.

Bir programıma xbir işlev koyduğumda adres kullanımlarını buldum foove daha sonra (doğru) programı tekrar çalıştırırsam aynı konumun kullanılacağını tahmin ettim. Aşağıdaki kısmi ekran görüntüsü x, değere 5ilk çağrıdan bile önce sahip olduğunu gösterir foo.

İlk foo çağrısından önce Kırılma Noktası


2

Çıktı olacak 6 7. Statik bir değişken (bir işlevin içinde olsun ya da olmasın), o çeviri birimindeki herhangi bir işlev yürütülmeden önce tam olarak bir kez başlatılır. Bundan sonra, değiştirilene kadar değerini korur.


1
Statiğin işlev çağrılmadan önce başlatıldığından ve işlevin ilk çağrılmasından önce olmadığından emin misiniz?
Jesse Pepper

@JessePepper: En azından bellek hizmet veriyorsa, bu C ++ 98/03 veya C ++ 11 hakkında konuşmanıza bağlı. C ++ 98 / 03'te yukarıda açıklandığı gibi olduğuna inanıyorum. C ++ 11'de, iş parçacığı bunu yapmayı esasen imkansız kılar, bu nedenle başlatma, işleve ilk girişte yapılır.
Jerry Coffin

2
Aslında yanılıyorsun galiba. C ++ 11 öncesi bile, işlev çağrıldığında yalnızca başlatıldığını düşünüyorum. Bu, statik başlatma bağımlılığı sorununa ortak bir çözüm için önemlidir.
Jesse Biber

2

Vadiklk,

Neden ...? Nedeni, statik değişkenin yalnızca bir kez başlatılması ve tüm program boyunca değerini korumasıdır. işlev çağrıları arasında statik değişken kullanabileceğiniz anlamına gelir. ayrıca "bir işlevin kaç kez çağrıldığını" saymak için de kullanılabilir

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

ve cevap beklediğiniz gibi 5 4 3 2 1 değil 5 5 5 5 5 5 .... (sonsuz döngü). yine, neden statik değişkenin bir kez başlatılmasıdır, bir dahaki sefere main () çağrıldığında, programda zaten başlatıldığından 5'e başlatılmayacaktır.Yani değeri değiştirebiliriz ama yeniden başlatamayız. Statik değişken böyle çalışır.

veya depolamaya göre düşünebilirsiniz: statik değişkenler bir programın Veri Bölümünde depolanır ve Veri Bölümünde depolanan değişkenler bir kez başlatılır. ve başlatmadan önce BSS bölümünde tutulurlar.

Buna karşılık, Otomatik (yerel) değişkenler Yığın üzerinde saklanır ve yığındaki tüm değişkenler, işlev yeni FAR (işlev etkinleştirme kaydı) olarak çağrıldığında bunun için her zaman yeniden başlatılır.

tamam, daha fazla anlayış için, yukarıdaki örneği "statik" olmadan yapın ve çıktının ne olacağını size bildirin. Bu, bu ikisi arasındaki farkı anlamanızı sağlar.

Teşekkürler Javed


1

Let sadece okumuş Statik Değişkenler Wikipedia article ...

Statik yerel değişkenler: Bir işlev içinde statik olarak bildirilen değişkenler, otomatik yerel değişkenlerle aynı kapsama sahipken statik olarak ayrılır. Dolayısıyla, bir çağrı sırasında işlevin statik yerel değişkenlerine koyduğu değerler, işlev yeniden çağrıldığında hala mevcut olacaktır.


5
Bu korkunç! "bir işlev içinde statik olarak bildirilen değişkenler durağan olarak ayrılır" - ne anlama geldiğini bilmiyorsanız hiçbir şey açıklamaz!

@Blank: Peki, ikinci cümlenin bunun için olduğunu düşündüm. Sanırım haklısın, daha iyi ifade edilmeli.
Andrew White

Ayrıca, bu gerçekten kafa karıştıran kısma değinmez, bu da başlatıcının sonraki çağrılarda atlandığı gerçeğidir.
Tom Auger

statik olarak ayrılmış, yığın veya yığın olmadığı anlamına gelir.
Bukalemun

1

Kolayca test edildiği gibi 6 7 yazdırılacak ve bunun nedeni fooşudur: İlk çağrıldığında, x statik değişkeni 5 olarak başlatılır. Daha sonra 6'ya yükseltilir ve yazdırılır.

Şimdi bir sonraki çağrı için foo. Program statik değişken başlatmayı atlar ve bunun yerine en son x'e atanan 6 değerini kullanır. Uygulama normal şekilde ilerler ve size 7 değerini verir.


1
6 7

x, yalnızca foo () 'dan görülebilen global bir değişkendir. 5, kodun .data bölümünde saklandığı şekliyle başlangıç ​​değeridir. Sonraki herhangi bir değişiklik önceki değerin üzerine yazılır. İşlev gövdesinde üretilen bir atama kodu yoktur.


1

6 ve 7 Statik değişken yalnızca bir kez yapıldığından, So 5 ++ 1. çağrıda 6 olur 6 ++ 2. çağrıda 7 olur Not - 2. çağrı gerçekleştiğinde x değeri 5 yerine 6 alır çünkü x statik değişkendir.


0

En azından C ++ 11'de, yerel bir statik değişkeni başlatmak için kullanılan ifade bir 'constexpr' değilse (derleyici tarafından değerlendirilemez), o zaman ilk işlev çağrısı sırasında başlatma yapılmalıdır. En basit örnek, yerel statik değişkeni başlatmak için doğrudan bir parametre kullanmaktır. Bu nedenle derleyicinin, çağrının ilk olup olmadığını tahmin etmek için kod vermesi gerekir, bu da yerel bir mantıksal değişken gerektirir. Böyle bir örneği derledim ve montaj kodunu görerek bunun doğru olduğunu kontrol ettim. Örnek şu şekilde olabilir:

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

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

Tabii ki, ifade 'constexpr' olduğunda, bu gerekli değildir ve değişken, çıktı derleme kodunda derleyici tarafından saklanan bir değer kullanılarak program yükünde başlatılabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.