web-dev-qa-db-fra.com

Où les variables statiques sont-elles stockées en C et C ++?

Dans quel segment (.BSS, .DATA, autre) d'un fichier exécutable, des variables statiques sont-elles stockées de manière à éviter toute collision de noms? Par exemple:


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

Si je compile les deux fichiers et que je les lie à un élément principal qui appelle fooTest () et barTest à plusieurs reprises, les instructions printf sont incrémentées indépendamment. Cela a du sens puisque les variables foo et bar sont locales à l'unité de traduction.

Mais où est le stockage alloué?

Pour être clair, l’hypothèse est que vous avez une chaîne d’outils capable de générer un fichier au format ELF. Ainsi, je pense qu'il y a a un espace réservé dans le fichier exécutable pour ces variables statiques.
À des fins de discussion, supposons que nous utilisions la chaîne d’outils GCC.

162
Benoit

Où va votre statique dépend si elles sont initialisées à zéro ou non. initialisé à zéro des données statiques entrent dans . BSS (Bloc lancé par symbole) , non les données initialisées à zéro entrent dans . DATA

119
Don Neufeld

Lorsqu'un programme est chargé en mémoire, il est organisé en différents segments. L'un des segment est Segment de données. Le segment de données est en outre divisé en deux parties:

Segment de données initialisé: Toutes les données globales, statiques et constantes sont stockées ici.
Segment de données non initialisé (BSS): Toutes les données non initialisées sont stockées dans ce segment.

Voici un schéma pour expliquer ce concept:

enter image description here


Voici un très bon lien expliquant ces concepts:

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

102
karn

En fait, une variable est Tuple (stockage, étendue, type, adresse, valeur):

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

La portée locale peut signifier locale pour l'unité de traduction (fichier source), la fonction ou le bloc, selon l'endroit où elle est définie. Pour rendre la variable visible pour plus d’une fonction, il faut qu’elle soit dans la zone DATA ou dans la zone BSS (selon qu’elle a été initialisée explicitement ou non, respectivement). Il est ensuite défini en conséquence sur toutes les fonctions ou toutes les fonctions du fichier source.

31
yogeesh

L'emplacement de stockage des données dépendra de la mise en œuvre.

Cependant, la signification de statique est "lien interne". Ainsi, le symbole est interne à l'unité de compilation (foo.c, bar.c) et ne peut pas être référencé en dehors de cette unité de compilation. Donc, il ne peut y avoir de collision de noms.

21
Seb Rose

Je ne crois pas qu'il y aura une collision. L'utilisation de static au niveau du fichier (en dehors des fonctions) marque la variable comme étant locale à l'unité de compilation actuelle (fichier). Il n'est jamais visible en dehors du fichier actuel, il n'est donc jamais nécessaire d'avoir un nom.

Utiliser statique dans une fonction est différent - la variable n'est visible que par la fonction, mais sa valeur est préservée pour tous les appels à cette fonction.

En effet, statique fait deux choses différentes selon l'endroit où il se trouve. Dans d’autres cas cependant, cela limite la visibilité de la variable pour éviter les conflits d’espace de noms,

Cela dit, je pense qu’il serait stocké dans DATA qui tend à avoir une variable initialisée. Le BSS représentait à l'origine byte-set- <quelque chose> qui contenait des variables non initialisées.

13
paxdiablo

Comment le trouver vous-même avec objdump -Sr

Pour comprendre réellement ce qui se passe, vous devez comprendre la relocalisation de l'éditeur de liens. Si vous n'avez jamais touché à cela, considérez en lisant ce post en premier .

Analysons un exemple ELF Linux x86-64 pour le voir nous-mêmes:

#include <stdio.h>

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

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

Compiler avec:

gcc -ggdb -c main.c

Décompiler le code avec:

objdump -Sr main.o
  • -S décompile le code en mélangeant la source d'origine
  • -r affiche les informations de relocalisation

Dans la décompilation de f, nous voyons:

 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

et le .data-0x4 indique qu'il passera au premier octet du segment .data.

Le -0x4 existe parce que nous utilisons un adressage relatif RIP, donc le %rip de l'instruction et R_X86_64_PC32.

Il est obligatoire car RIP pointe sur l'instruction suivante , qui commence 4 octets après le 00 00 00 00 qui est ce qui sera déplacé. J'ai expliqué cela plus en détail à: https://stackoverflow.com/a/30515926/895245

Ensuite, si nous modifions le code source en i = 1 et procédons à la même analyse, nous concluons que:

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

dans la zone "globale et statique" :)

il y a plusieurs zones de mémoire en C++

  • tas
  • magasin gratuit
  • empiler
  • global et statique
  • const

voir ici pour une réponse détaillée à votre question

9
ugasoft

Cela dépend de la plate-forme et du compilateur que vous utilisez. Certains compilateurs stockent directement dans le segment de code. Les variables statiques ne sont toujours accessibles qu'à l'unité de traduction actuelle et les noms ne sont pas exportés; les collisions de noms de raison ne se produisent donc jamais.

6
trotterdylan

Les données déclarées dans une unité de compilation iront dans le fichier .BSS ou .Data de la sortie de ce fichier. Données initialisées dans BSS, non initialisées dans DATA.

La différence entre les données statiques et globales réside dans l'inclusion d'informations de symbole dans le fichier. Les compilateurs ont tendance à inclure les informations de symbole mais à ne marquer que les informations globales en tant que telles.

L'éditeur de liens respecte ces informations. Les informations de symbole pour les variables statiques sont soit supprimées, soit tronquées, de sorte que les variables statiques peuvent toujours être référencées de quelque manière que ce soit (avec les options de débogage ou de symbole). Dans aucun cas, les unités de compilation ne sont affectées car l'éditeur de liens résout d'abord les références locales.

5
itj

Voici comment (facile à comprendre):

stack, heap and static data

3
Yousha Aleayoub

J'ai essayé avec objdump et gdb, voici le résultat obtenu:

(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.

voici le résultat 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

Donc, cela signifie que vos quatre variables sont situées dans l'événement de section de données du même nom, mais avec un décalage différent.

2
Dan

Eh bien, cette question est un peu trop ancienne, mais puisque personne ne signale d’informations utiles: Consultez l’article "mohit12379" expliquant le magasin de variables statiques portant le même nom dans la table des symboles: http: //www.geekinterview. com/question_details/24745

2
lukmac

variable statique stockée dans un segment de données ou un segment de code, comme mentionné précédemment.
Vous pouvez être sûr qu'il ne sera pas alloué sur pile ou tas.
Il n'y a aucun risque de collision puisque le mot clé static définit la portée de la variable comme étant un fichier ou une fonction. En cas de collision, un compilateur/éditeur de liens vous avertit.
A Nice exemple

2
Ilya

La réponse peut très bien dépendre du compilateur, vous voudrez donc probablement éditer votre question (je veux dire, même la notion de segments n'est pas prescrite par ISO C ni ISO C++). Par exemple, sous Windows, un exécutable ne porte pas de noms de symbole. Un "toto" serait compensé 0x100, un autre peut-être 0x2B0 et le code des deux unités de traduction est compilé en connaissant les décalages de "leur" toto.

1
MSalters

ils seront tous les deux stockés indépendamment. Toutefois, si vous souhaitez que les autres développeurs en soient conscients, vous pouvez les placer dans des espaces de noms.

0
Robert Gould