it-swarm-tr.com

C ve C ++ 'da statik değişkenler nerede saklanır?

Yürütülebilir bir dosyanın hangi segmentinde (.BSS, .DATA, diğer) statik değişkenler saklanır, böylece isim çarpışması olmaz? Örneğin:


foo.c:                         bar.c:
static int foo = 1;            static int foo = 10;
void fooTest() {               void barTest() {
  static int bar = 2;            static int bar = 20;
  foo++;                         foo++;
  bar++;                         bar++;
  printf("%d,%d", foo, bar);     printf("%d, %d", foo, bar);
}                              }

Her iki dosyayı da derlersem ve fooTest () ve barTest'i tekrar tekrar çağıran bir ana bilgisayara bağlarsam, printf ifadeleri bağımsız olarak artar. Foo ve bar değişkenleri çeviri biriminde bulunduğundan, mantıklı geliyor.

Fakat depo nerede tahsis edilir?

Açık olmak gerekirse, varsayım, ELF formatında bir dosya çıktısı alabilecek bir alet zincirine sahip olduğunuzdur. Böylece, ( inanıyorum bu statik değişkenler için çalıştırılabilir dosyada bir miktar alan olduğuna sahip olduğuna inanıyorum.
Tartışma amaçları için GCC araç zincirini kullandığımızı varsayalım.

162
Benoit

Statiklerinizin nereye gittiğine sıfır başlatılmış olup olmadığına bağlıdır. sıfır başlatılmış statik veriler giriyor . BSS (Sembolle Başlayan Blok) , non sıfır başlatılmış veriler giriyor . VERİ

119
Don Neufeld

Bir program belleğe yüklendiğinde, farklı segmentlerde düzenlenir. Segmentlerden biri VERİ segmenti. Veri segmenti ayrıca iki bölüme ayrılmıştır:

Başlatılan veri segmenti: Tüm global, statik ve sabit veriler burada depolanır.
Başlatılmamış veri segmenti (BSS): Başlatılmamış tüm veriler bu segmentte saklanır.

İşte bu kavramı açıklamak için bir şema:

enter image description here


bu kavramları açıklayan çok iyi bir link:

http://www.inf.udec.cl/~leo/teoX.pdf

102
karn

Aslında, bir değişken Tuple'dır (depolama, kapsam, tür, adres, değer):

storage     :   where is it stored, for example data, stack, heap...
scope       :   who can see us, for example global, local...
type        :   what is our type, for example int, int*...
address     :   where are we located
value       :   what is our value

Yerel kapsam, tanımlandığı yere bağlı olarak çeviri birimi (kaynak dosya), işlev veya blok için yerel anlamına gelebilir. Değişkeni birden fazla işleve görünür hale getirmek için, kesinlikle, sırasıyla sırasıyla başlatılıp başlatılmamasına bağlı olarak, DATA veya BSS alanında olması gerekir. Daha sonra kaynak dosyadaki tüm işlevlere veya işlevlere göre kapsamlanır.

31
yogeesh

Verilerin depolanma yeri uygulamaya bağlı olacaktır.

Ancak, statik 'in anlamı "iç bağlantı" dır. Bu nedenle, sembol derleme birimine (foo.c, bar.c) dahilidir ve bu derleme biriminin dışına atılamaz. Yani, isim çarpışması olamaz.

21
Seb Rose

Bir çarpışma olacağına inanmıyorum. Dosya seviyesinde statik kullanımı (dış fonksiyonlar) değişkeni geçerli derleme birimine (dosya) yerel olarak işaretler. Geçerli dosyanın dışında hiçbir zaman görünmez, bu nedenle hiçbir zaman bir adı olması gerekmez.

Bir işlevin içinde statik kullanmak farklıdır - değişken yalnızca işleve görünür, sadece bu işleve yapılan çağrılar arasında değeri korunur.

Aslında statik, nerede olduğuna bağlı olarak iki farklı şey yapar. Bununla birlikte, diğer durumlarda, ad alanı çakışmalarını önlemek için değişkenin görünürlüğünü sınırlar,

Bunu söyledikten sonra, başlangıç ​​değişkenini kullanma eğiliminde olan DATA'da depolanacağına inanıyorum. BSS ilk olarak, başlatılmamış değişkenleri tutan bayt-set- <bir şey> için duruyordu.

13
paxdiablo

Kendinizi nasıl bulursunuz objdump -Sr

Neyin olup bittiğini gerçekten anlamak için, bağlayıcı yer değiştirmeyi anlamalısınız. Buna hiç dokunmadıysanız, önce bu yazıyı okuyarak düşünün.

Linux x86-64 ELF örneğini kendimiz görmek için analiz edelim:

#include <stdio.h>

int f() {
    static int i = 1;
    i++;
    return i;
}

int main() {
    printf("%d\n", f());
    printf("%d\n", f());
    return 0;
}

İle derleyin:

gcc -ggdb -c main.c

Kodu şu şekilde açın:

objdump -Sr main.o
  • -S kodu orijinal kaynak ile karıştırılarak kodu çözer
  • -r yer değiştirme bilgilerini gösterir

f öğesinin ayrışmasının içinde görüyoruz:

 static int i = 1;
 i++;
4:  8b 05 00 00 00 00       mov    0x0(%rip),%eax        # a <f+0xa>
        6: R_X86_64_PC32    .data-0x4

ve .data-0x4, .data bölümünün ilk baytına gideceğini söylüyor.

-0x4 orada çünkü RIP göreceli adreslemeyi kullanıyoruz, bu nedenle talimatlarda %rip ve R_X86_64_PC32.

RIP gereklidir, çünkü aşağıdaki komutunu işaret eder, 00 00 00 00 komutundan 4 byte sonra başlar, ki bunlar yeniden yerleştirilecektir. Bunu daha ayrıntılı olarak açıkladım: https://stackoverflow.com/a/30515926/895245

Daha sonra, kaynağı i = 1 olarak değiştirirsek ve aynı analizi yaparsak, şu sonucu çıkarırız:

  • static int i = 0 devam ediyor .bss
  • static int i = 1 devam ediyor .data

"küresel ve statik" alanda :)

c ++ 'da birkaç bellek alanı var

  • yığın
  • ücretsiz mağaza
  • yığın
  • genel ve statik
  • const

bakınız burada sorunuzun ayrıntılı cevabı için

9
ugasoft

Kullandığınız platforma ve derleyiciye bağlıdır. Bazı derleyiciler doğrudan kod segmentinde saklanır. Statik değişkenlere her zaman yalnızca geçerli çeviri birimine erişilebilir ve adlar dışa aktarılmaz, bu nedenle ad çarpışmalarının nedeni hiç olmaz.

6
trotterdylan

Bir derleme biriminde bildirilen veriler, bu dosyaların çıktısının .BSS veya .Data'larına gidecektir. BSS'de başlatılmamış veriler, DATA'da bağlantısız.

Statik ve global veriler arasındaki fark, sembol bilgisinin dosyaya dahil edilmesiyle gelir. Derleyiciler sembol bilgisini içerme eğilimindedir, fakat sadece global bilgiyi işaretler.

Bağlayıcı bu bilgilere saygı duyuyor. Statik değişkenler için sembol bilgileri atılır veya karıştırılır, böylece statik değişkenlere yine de bir şekilde başvuru yapılabilir (hata ayıklama veya sembol seçenekleriyle). Her iki durumda da, bağlayıcı önce yerel referansları çözmediğinden derleme birimleri etkilenemez.

5
itj

Bu nasıl (anlaşılması kolay):

stack, heap and static data

3
Yousha Aleayoub

Bunu objdump ve gdb ile denedim, işte elde ettiğim sonuç:

(gdb) disas fooTest
Dump of assembler code for function fooTest:
   0x000000000040052d <+0>: Push   %rbp
   0x000000000040052e <+1>: mov    %rsp,%rbp
   0x0000000000400531 <+4>: mov    0x200b09(%rip),%eax        # 0x601040 <foo>
   0x0000000000400537 <+10>:    add    $0x1,%eax
   0x000000000040053a <+13>:    mov    %eax,0x200b00(%rip)        # 0x601040 <foo>
   0x0000000000400540 <+19>:    mov    0x200afe(%rip),%eax        # 0x601044 <bar.2180>
   0x0000000000400546 <+25>:    add    $0x1,%eax
   0x0000000000400549 <+28>:    mov    %eax,0x200af5(%rip)        # 0x601044 <bar.2180>
   0x000000000040054f <+34>:    mov    0x200aef(%rip),%edx        # 0x601044 <bar.2180>
   0x0000000000400555 <+40>:    mov    0x200ae5(%rip),%eax        # 0x601040 <foo>
   0x000000000040055b <+46>:    mov    %eax,%esi
   0x000000000040055d <+48>:    mov    $0x400654,%edi
   0x0000000000400562 <+53>:    mov    $0x0,%eax
   0x0000000000400567 <+58>:    callq  0x400410 <[email protected]>
   0x000000000040056c <+63>:    pop    %rbp
   0x000000000040056d <+64>:    retq   
End of assembler dump.

(gdb) disas barTest
Dump of assembler code for function barTest:
   0x000000000040056e <+0>: Push   %rbp
   0x000000000040056f <+1>: mov    %rsp,%rbp
   0x0000000000400572 <+4>: mov    0x200ad0(%rip),%eax        # 0x601048 <foo>
   0x0000000000400578 <+10>:    add    $0x1,%eax
   0x000000000040057b <+13>:    mov    %eax,0x200ac7(%rip)        # 0x601048 <foo>
   0x0000000000400581 <+19>:    mov    0x200ac5(%rip),%eax        # 0x60104c <bar.2180>
   0x0000000000400587 <+25>:    add    $0x1,%eax
   0x000000000040058a <+28>:    mov    %eax,0x200abc(%rip)        # 0x60104c <bar.2180>
   0x0000000000400590 <+34>:    mov    0x200ab6(%rip),%edx        # 0x60104c <bar.2180>
   0x0000000000400596 <+40>:    mov    0x200aac(%rip),%eax        # 0x601048 <foo>
   0x000000000040059c <+46>:    mov    %eax,%esi
   0x000000000040059e <+48>:    mov    $0x40065c,%edi
   0x00000000004005a3 <+53>:    mov    $0x0,%eax
   0x00000000004005a8 <+58>:    callq  0x400410 <[email protected]>
   0x00000000004005ad <+63>:    pop    %rbp
   0x00000000004005ae <+64>:    retq   
End of assembler dump.

objdump sonucu burada

Disassembly of section .data:

0000000000601030 <__data_start>:
    ...

0000000000601038 <__dso_handle>:
    ...

0000000000601040 <foo>:
  601040:   01 00                   add    %eax,(%rax)
    ...

0000000000601044 <bar.2180>:
  601044:   02 00                   add    (%rax),%al
    ...

0000000000601048 <foo>:
  601048:   0a 00                   or     (%rax),%al
    ...

000000000060104c <bar.2180>:
  60104c:   14 00                   adc    $0x0,%al

Yani, dört değişkeniniz veri bölümünde olaya aynı isimde fakat farklı bir kayma ile yerleştirilmiş.

2
Dan

Bu soru biraz fazla eski ama kimse faydalı bir bilgiyi göstermediği için: "mohit12379" yazısını kontrol edin ve sembol tablosunda aynı isimdeki statik değişkenlerin deposunu açıklayın: http: //www.geekinterview. com/question_details/24745

2
lukmac

daha önce de belirtildiği gibi veri segmentinde veya kod segmentinde depolanan statik değişken.
Yığına veya yığına tahsis edilmeyeceğinden emin olabilirsiniz.
static anahtar sözcüğü bir dosya ya da işlev olarak değişkenin kapsamını tanımladığından, çarpışma durumunda sizi uyaracak bir derleyici/bağlayıcı varsa, çarpışma riski yoktur.
Güzel bir örnek

2
Ilya

Cevap, derleyiciye çok iyi bağlı olabilir, bu nedenle muhtemelen sorunuzu düzenlemek isteyebilirsiniz (Yani, segmentler kavramı bile ISO C ve ISO C++ tarafından zorunlu değildir). Örneğin, Windows'ta bir çalıştırılabilir sembol adları taşımamaktadır. Bir 'foo' 0x100 ofset, diğeri 0x2B0 ofset olur ve her iki çeviri biriminin kodu da "onların" foo için ofsetlerini bilerek derlenir.

1
MSalters

her ikisi de bağımsız olarak depolanacaklardır, ancak diğer geliştiricilere açıklık getirmek isterseniz, bunları ad alanlarına sarmak isteyebilirsiniz.

0
Robert Gould