Domanda

In quale segmento (.BSS, .DATA, altro) di un file eseguibile sono memorizzate le variabili statiche in modo che non abbiano collisioni di nomi?Per esempio:


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);
}                              }

Se compilo entrambi i file e li collego a un main che chiama ripetutamente fooTest() e barTest, le istruzioni printf aumentano in modo indipendente.Ha senso poiché le variabili foo e bar sono locali nell'unità di traduzione.

Ma dove viene allocato lo spazio di archiviazione?

Per essere chiari, il presupposto è che tu abbia una toolchain che generi un file in formato ELF.Quindi, io credere quello lì ha essere uno spazio riservato nel file eseguibile per quelle variabili statiche.
Ai fini della discussione, supponiamo di utilizzare la toolchain GCC.

È stato utile?

Soluzione

Dove vanno le tue statistiche dipende da se lo sono inizializzato con zero o no. inizializzato con zero entrano i dati statici .BSS (Blocco iniziato da simbolo), non inizializzato con zero entrano i dati .DATI

Altri suggerimenti

Quando un programma viene caricato in memoria, è organizzato in diversi segmenti.Uno dei segmenti è Segmento DATI.Il segmento Dati è ulteriormente suddiviso in due parti:

Segmento dati inizializzato: Tutti i dati globali, statici e costanti vengono memorizzati qui.
Segmento dati non inizializzato (BSS): Tutti i dati non inizializzati vengono archiviati in questo segmento.

Ecco uno schema per spiegare questo concetto:

enter image description here


ecco un ottimo collegamento che spiega questi concetti:

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

In effetti, una variabile è una tupla (memoria, ambito, tipo, indirizzo, valore):

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

L'ambito locale potrebbe significare locale per l'unità di traduzione (file sorgente), per la funzione o per il blocco a seconda di dove è definito.Per rendere la variabile visibile a più di una funzione, deve sicuramente trovarsi nell'area DATA o BSS (a seconda che sia inizializzata esplicitamente o meno, rispettivamente).Viene quindi ambito di conseguenza a tutte le funzioni o alle funzioni all'interno del file sorgente.

La posizione di archiviazione dei dati dipenderà dall'implementazione.

Tuttavia, il significato di statico è "collegamento interno".Quindi il simbolo è interno all'unità di compilazione (foo.c, bar.c) e non è possibile fare riferimento al di fuori di tale unità di compilazione.Quindi non possono esserci collisioni di nomi.

Non credo che ci sarà una collisione.L'uso di static a livello di file (funzioni esterne) contrassegna la variabile come locale rispetto all'unità di compilazione corrente (file).Non è mai visibile al di fuori del file corrente, quindi non deve mai avere un nome.

L'uso di static all'interno di una funzione è diverso: la variabile è visibile solo alla funzione, è solo che il suo valore viene preservato durante le chiamate a quella funzione.

In effetti, lo statico fa due cose diverse a seconda di dove si trova.In altri casi, tuttavia, limita la visibilità della variabile per evitare conflitti nello spazio dei nomi,

Detto questo, credo che verrebbe memorizzato in DATA che tende ad avere una variabile inizializzata.Originariamente il BSS stava per byte-set-<qualcosa> che conteneva variabili che non erano inizializzate.

Come trovarlo da solo objdump -Sr

Per capire effettivamente cosa sta succedendo, è necessario comprendere il riposizionamento del linker.Se non l'hai mai toccato, considera leggendo prima questo post.

Analizziamo un esempio ELF Linux x86-64 per vederlo da soli:

#include <stdio.h>

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

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

Compila con:

gcc -ggdb -c main.c

Decompilare il codice con:

objdump -Sr main.o
  • -S decompila il codice con il sorgente originale mescolato
  • -r mostra le informazioni sul trasferimento

All'interno della decompilazione di f vediamo:

 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

e il .data-0x4 dice che andrà al primo byte del .data segmento.

IL -0x4 è lì perché stiamo usando l'indirizzamento relativo RIP, quindi il file %rip nelle istruzioni e R_X86_64_PC32.

È necessario perché RIP punta al file seguente istruzione, che inizia 4 byte dopo 00 00 00 00 che è ciò che verrà trasferito.L'ho spiegato più dettagliatamente in: https://stackoverflow.com/a/30515926/895245

Quindi, se modifichiamo la fonte in i = 1 e facciamo la stessa analisi, concludiamo che:

  • static int i = 0 continua .bss
  • static int i = 1 continua .data

nell'area "globale e statica" :)

ci sono diverse aree di memoria in C++

  • mucchio
  • negozio gratuito
  • pila
  • globale e statico
  • cost

Vedere Qui per una risposta dettagliata alla tua domanda

Dipende dalla piattaforma e dal compilatore che stai utilizzando.Alcuni compilatori memorizzano direttamente nel segmento di codice.Le variabili statiche sono sempre accessibili solo all'unità di traduzione corrente e i nomi non vengono esportati, quindi non si verificano mai collisioni tra i nomi.

I dati dichiarati in un'unità di compilazione andranno nell'output .BSS o .Data di quel file.Dati inizializzati in BSS, non inizializzati in DATA.

La differenza tra dati statici e globali sta nell'inclusione di informazioni sui simboli nel file.I compilatori tendono a includere le informazioni sui simboli ma contrassegnano solo le informazioni globali come tali.

Il linker rispetta queste informazioni.Le informazioni sui simboli per le variabili statiche vengono scartate o alterate in modo che sia ancora possibile fare riferimento alle variabili statiche in qualche modo (con opzioni di debug o simboli).In nessuno dei due casi le unità di compilazione possono essere influenzate poiché il linker risolve prima i riferimenti locali.

variabile statica memorizzata nel segmento dati o nel segmento codice come menzionato prima.
Puoi essere certo che non verrà allocato nello stack o nell'heap.
Da allora non vi è alcun rischio di collisione static La parola chiave definisce l'ambito della variabile come file o funzione, in caso di collisione c'è un compilatore/linker di cui avvisarti.
Un bel esempio

Bene, questa domanda è un po' troppo vecchia, ma poiché nessuno indica alcuna informazione utile:Controlla il post di "mohit12379" che spiega l'archivio di variabili statiche con lo stesso nome nella tabella dei simboli:http://www.geekinterview.com/question_details/24745

L'ho provato con objdump e gdb, ecco il risultato che ottengo:

(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 <printf@plt>
   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 <printf@plt>
   0x00000000004005ad <+63>:    pop    %rbp
   0x00000000004005ae <+64>:    retq   
End of assembler dump.

ecco il risultato objdump

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

Quindi, vale a dire, le tue quattro variabili si trovano nell'evento della sezione dati con lo stesso nome, ma con offset diverso.

Ecco come (facile da capire):

stack, heap and static data

La risposta potrebbe dipendere molto dal compilatore, quindi probabilmente vorrai modificare la tua domanda (voglio dire, anche la nozione di segmenti non è imposta da ISO C né ISO C++).Ad esempio, su Windows un eseguibile non contiene nomi di simboli.Un 'foo' avrebbe un offset 0x100, l'altro forse 0x2B0, e il codice di entrambe le unità di traduzione viene compilato conoscendo gli offset per il "loro" foo.

verranno entrambi archiviati in modo indipendente, tuttavia se vuoi renderlo chiaro ad altri sviluppatori potresti volerli racchiudere negli spazi dei nomi.

sai già che è memorizzato in bss (blocco iniziale per simbolo) indicato anche come segmento di dati non inizializzato o nel segmento di dati inizializzato.

facciamo un semplice esempio

void main(void)
{
static int i;
}

la variabile statica sopra non è inizializzata, quindi va al segmento dati non inizializzato (bss).

void main(void)
{
static int i=10;
}

e ovviamente è inizializzato per 10, quindi va al segmento dati inizializzato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top