Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

tableau global et taille de l'exécutable.

5 réponses
Avatar
GurneyH
Bonjour.

Je me trouve =E0 nouveau en face d'un bout de code qui =E0 un comportement
qui me surprend.

#include <stdio.h>

int tab[1000] =3D {0};
int main(void)
{
int i;
for(i =3D 0; i < 1000; i++)
printf("%d\n", tab[i]);

return 0;
}

ce code compil=E9 sous Windows mingw, GCC sous Linux ne pose pas de
probl=E8mes.
Mais sous MAC et compil=E9 avec GCC la taille de l'ex=E9cutable gonfle si
j'augmente la taille du tableau!
Ce qui me surprend c'est que si on se contente de la d=E9finition
globale
int tab[1000];
(donc sans initialisation explicite), la taille de l'ex=E9cutable ne
bouge pas.

j'avais compris qu'en globale ou en statique, les d=E9finitions
suivantes =E9taient =E9quivalentes.
int tab[1000] =3D {0};
int tab[1000];
Il semble que je me trompe.

Si quelqu'un pouvait m'=E9clairer.

Merci d'avance.

en esp=E9rant avoir =E9t=E9 suffisamment clair (pas =E9vident)

5 réponses

Avatar
Samuel DEVULDER
GurneyH a écrit :

(Gurney Halleck?? :) )

Mais sous MAC et compilé avec GCC la taille de l'exécutable gonfle si
j'augmente la taille du tableau!
Ce qui me surprend c'est que si on se contente de la définition
globale
int tab[1000];
(donc sans initialisation explicite), la taille de l'exécutable ne
bouge pas.



Regarde l'allure du code assembleur.

Dans le code assembleur il y a au moins 2 types de sections. Le ".BSS"
et le ".DATA". Le ".BSS" contient les variables statiques non
initialisées, et les ".DATA" celle qui sont initialisées trucs
initialisés. Les sections ".BSS" ne prennent quasiment pas de place dans
les ".OBJ" ou l'exe final car l'initialisation est prédéfinie (à zero).
En revanche ".DATA" occupe la meme place dans l'exe qu'en mémoire.

A tous les coups sous windows, il y a une optimisation qui dit qu'une
variable statique initialisée à 0 se retrouve en BSS, alors que sur mac
ce tableau reste dans la section DATA. C'est probablement ce qui
explique ce que tu observes.

Pour en avoir le coeur net, demande au compilateur de te produire le
code assembleur et regarde dans quelle section ton tableau figure. Il
est aussi possible de faire cela avec l'outil nm sur le ".OBJ" ou l'exe
non strippé.

Pour info, j'ai fait l'expérience sous windows:

Si la variable "a" est initialisée à 0

$ echo "int a = 0;" >t.c; gcc -c -g t.c -o t.o; nm t.o
00000000 b .bss
00000000 d .data
00000000 N .stab
00000000 N .stabstr
00000000 t .text
00000000 B _a

... et pourtant elle atterrit dans la section BSS (dernière ligne
ci-dessus).

La même initialisée à 1:

$ echo "int a = 1;" >t.c ; gcc -c -g t.c -o t.o; nm t.o
00000000 b .bss
00000000 d .data
00000000 N .stab
00000000 N .stabstr
00000000 t .text
00000000 D _a

... elle atterrit dans le section DATA.

sam.
Avatar
-ed-
On 1 juin, 06:36, GurneyH wrote:
Bonjour.

Je me trouve à nouveau en face d'un bout de code qui à un comportemen t
qui me surprend.

#include <stdio.h>

int tab[1000] = {0};
int main(void)
{
    int i;
    for(i = 0; i < 1000; i++)
        printf("%dn", tab[i]);

    return 0;

}

ce code compilé sous Windows mingw, GCC sous Linux ne pose pas de
problèmes.
Mais sous MAC et compilé avec GCC la taille de l'exécutable gonfle si
j'augmente la taille du tableau!
Ce qui me surprend c'est que si on se contente de la définition
globale
int tab[1000];
(donc sans initialisation explicite), la taille de l'exécutable ne
bouge pas.

j'avais compris qu'en globale ou en statique, les définitions
suivantes étaient équivalentes.
int tab[1000] = {0};
int tab[1000];
Il semble que je me trompe.



Oui, c'est possible. La manière d'initialiser le tableau est plus ou
moins optimisée, ça dépend du compilateur et peut être de ses rég lages
(-O etc.).

La Loi dit que le tableau appartenant à la zone statique, il est
initialisé à 0 par défaut. Une implémentation peut très bien choi sir :

1 - d'initialiser toute la zone 'statique' à 0 de manière dynamique
(avec une boucle)
2 - ne ne rien faire pour le tableau puisque la zone est déjà à 0.

Mais une autre implémentation peut aussi construire une image de la
zone statique à initialiser au démarrage, et ce sous la forme d'une
tableau 'en dur' dans le code (d'où une augmentation de la taille du
code).

Il est probable que si on utilise autre chose que 0 dans
l'initialisation, c'est ce qui se fera dans les 2 cas.

De toutes façons, ces considérations n'ont pas grande importance pour
du code hébergé. Par contre, en embarqué, si les ressources sont
faibles, ça peut être gênant. Une initialisation dynamique pourrait
consommer moins de code (mais plus de temps).
Avatar
Antoine Leca
GurneyH écrivit :
Je me trouve à nouveau en face d'un bout de code qui à un comportement
qui me surprend.



OEQLC? Je n'ai pas vu dans le reste de ton message de bout de code dont
le comportement te surprennes, mais plutôt un problème de format
binaire. Pourrais-tu m'expliquer ce que je n'ai pas compris ?

int tab[1000] = {0};


[...]
ce code compilé sous Windows mingw, GCC sous Linux ne pose pas de
problèmes.
Mais sous MAC et compilé avec GCC la taille de l'exécutable gonfle si
j'augmente la taille du tableau!



Mais où est le problème du code ?


[ Citation réordonnée ]
j'avais compris qu'en globale ou en statique, les définitions
suivantes étaient équivalentes.
int tab[1000] = {0};
int tab[1000];
Il semble que je me trompe.



Non, tu ne trompes pas. « Équivalente » ne signifie pas « identique ».
Et moi je ne vois pas où le comportement du programme diffère.


Ce qui me surprend c'est que si on se contente de la définition
globale
int tab[1000];
(donc sans initialisation explicite), la taille de l'exécutable ne
bouge pas.



Hmm. Que dit size ? Que dis nm sur le symbole taille (est-il B ou D) ?


en espérant avoir été suffisamment clair (pas évident)



Oh, le problème est limpide, mais dépend complètement du format de
fichier binaire (Mach-O) utilisé par ta plateforme, et par conséquent
des outils utilisés (en particulier l'éditeur de liens).

D'abord, et c'est indépendant de Mach-O, il faut voir que la déclaration
int tab[1000];
est spéciale avec *nix: au lieu de définir un tableau de 1000 entiers
comme tu pourrais le penser, elle déclare l'existence d'un tableau d'au
moins 1000 entiers. Cela veut dire que si dans un autre fichier tu as
int tab[2000];
le programme va se lier correctement (surprenant, non?) et va réserver
de la place pour 2000 entiers, mais ton premier programme va se
comporter comme s'il n'y en avait que 1000... Cela s'appelle un COMMON,
cela vient de Fortran et de la préhistoire de l'informatique.

Par contre, la déclaration
int tab[1000]={0};
n'est PAS un common, et surtout EST une définition, donc il est interdit
d'avoir une autre initialisation dans un autre module (même si elle est
identique.) Le compilateur (le translateur, ici ccom) a le choix de dire
si l'objet est initialisé explicitement (.data et un millier de ".byte
0") ou implicitement (.bss + .space ou .lcomm, c'est pareil) : la
définition de l'interface binaire traditionnelle *nix dit que ce sera
équivalent (mais pas identique).

Évidemment, l'éditeur des liens est obligé de bosser derrière pour
rassembler les morceaux ; cela l'amène à faire des « optimisations » au
passage, qui dans ton cas peuvent apparaître comme sous-optimales...

Donc, un éditeur de liens *nix est prié de transformer les "commons" en
sous-sections (normalement au sein de la section .bss, mais parfois cela
va ailleurs, comme avec Mach-O) et d'allouer les symboles en fonction de
leur plus grande taille. Aussi, l'éditeur des liens peut aussi
transformer une zone .data pleine de 0 en zone .bss, cela fait partie
des optimisations classiques d'un éditeur de liens. Mais il peut aussi
décréter que la zone .bss sera effectivement initialisée au sein du
fichier binaire : une raison habituelle pour faire cela est «
d'économiser » le code qui remplit de zéro la zone .bss, ou pour des
modules qui vont en ROM et pour lesquels il est impossible d'initialiser
la mémoire avant de s´être assurer qu'elle existe effectivement...


Au final, je pense que c'est ce qui se passe dans ton cas, mais j'ignore
quelle est la raison qui pousse l'éditeur des liens d'Apple à forcer
l'incorporation de la section __bss dans le fichier a.out final :
je suppute que la liaison dynamique (qui est très spécifique) a quelque
chose à voir, mais je ne peux pas approfondir plus avec les informations
fournies.


Antoine
Avatar
candide
GurneyH a écrit :

(donc sans initialisation explicite), la taille de l'exécutable ne
bouge pas.

j'avais compris qu'en globale ou en statique, les définitions
suivantes étaient équivalentes.
int tab[1000] = {0};
int tab[1000];
Il semble que je me trompe.




Salut Gurney !


Du point de vue du C, la seule chose qui compte c'est le comportement au
moment de l'exécution :

5.1.2.3 Program execution
1 The semantic descriptions in this International Standard describe the
behavior of an abstract machine in which issues of optimization are
irrelevant.


Je ne vois pas ici en quoi les comportement diffèrent. Même si
effectivement, la taille d'un exécutable peut avoir une importance pour
le développeur.
Avatar
GurneyH
On 1 juin, 10:39, Antoine Leca wrote:
GurneyH écrivit :

> Je me trouve à nouveau en face d'un bout de code qui à un comportem ent
> qui me surprend.

OEQLC? Je n'ai pas vu dans le reste de ton message de bout de code dont
le comportement te surprennes, mais plutôt un problème de format
binaire. Pourrais-tu m'expliquer ce que je n'ai pas compris ?



> int tab[1000] = {0};
[...]
> ce code compilé sous Windows mingw, GCC sous Linux ne pose pas de
> problèmes.
> Mais sous MAC et compilé avec GCC la taille de l'exécutable gonfle si
> j'augmente la taille du tableau!

Mais où est le problème du code ?

[ Citation réordonnée ]

> j'avais compris qu'en globale ou en statique, les définitions
> suivantes étaient équivalentes.
> int tab[1000] = {0};
> int tab[1000];
> Il semble que je me trompe.

Non, tu ne trompes pas. « Équivalente » ne signifie pas « identiq ue ».
Et moi je ne vois pas où le comportement du programme diffère.

> Ce qui me surprend c'est que si on se contente de la définition
> globale
> int tab[1000];
> (donc sans initialisation explicite), la taille de l'exécutable ne
> bouge pas.

Hmm. Que dit size ? Que dis nm  sur le symbole taille (est-il B ou D) ?

> en espérant avoir été suffisamment clair (pas évident)

Oh, le problème est limpide, mais dépend complètement du format de
fichier binaire (Mach-O) utilisé par ta plateforme, et par conséquent
des outils utilisés (en particulier l'éditeur de liens).

D'abord, et c'est indépendant de Mach-O, il faut voir que la déclarat ion
        int tab[1000];
est spéciale avec *nix: au lieu de définir un tableau de 1000 entiers
comme tu pourrais le penser, elle déclare l'existence d'un tableau d'au
moins 1000 entiers. Cela veut dire que si dans un autre fichier tu as
        int tab[2000];
le programme va se lier correctement (surprenant, non?) et va réserver
de la place pour 2000 entiers, mais ton premier programme va se
comporter comme s'il n'y en avait que 1000... Cela s'appelle un COMMON,
cela vient de Fortran et de la préhistoire de l'informatique.

Par contre, la déclaration
        int tab[1000]={0};
n'est PAS un common, et surtout EST une définition, donc il est interdi t
d'avoir une autre initialisation dans un autre module (même si elle est
identique.) Le compilateur (le translateur, ici ccom) a le choix de dire
si l'objet est initialisé explicitement (.data et un millier de ".byte
0") ou implicitement (.bss + .space ou .lcomm, c'est pareil) : la
définition de l'interface binaire traditionnelle *nix dit que ce sera
équivalent (mais pas identique).

Évidemment, l'éditeur des liens est obligé de bosser derrière pou r
rassembler les morceaux ; cela l'amène à faire des « optimisations » au
passage, qui dans ton cas peuvent apparaître comme sous-optimales...

Donc, un éditeur de liens *nix est prié de transformer les "commons" en
sous-sections (normalement au sein de la section .bss, mais parfois cela
va ailleurs, comme avec Mach-O) et d'allouer les symboles en fonction de
leur plus grande taille. Aussi, l'éditeur des liens peut aussi
transformer une zone .data pleine de 0 en zone .bss, cela fait partie
des optimisations classiques d'un éditeur de liens. Mais il peut aussi
décréter que la zone .bss sera effectivement initialisée au sein du
fichier binaire : une raison habituelle pour faire cela est «
d'économiser » le code qui remplit de zéro la zone .bss, ou pour de s
modules qui vont en ROM et pour lesquels il est impossible d'initialiser
la mémoire avant de s´être assurer qu'elle existe effectivement...

Au final, je pense que c'est ce qui se passe dans ton cas, mais j'ignore
quelle est la raison qui pousse l'éditeur des liens d'Apple à forcer
l'incorporation de la section __bss dans le fichier a.out final :
je suppute que la liaison dynamique (qui est très spécifique) a quelq ue
chose à voir, mais je ne peux pas approfondir plus avec les information s
fournies.

Antoine



Merci pour les réponses.

Effectivement, ce n'est pas le bout de code qui me posait un problème,
c'est la différence de comportement entre
int tab[1000] = {0};
int tab[1000];

J'avais besoin qu'on me rappelle la nuance entre équivalent et
identique, ma question part effectivement de là.

D'un point de vue C, c'est vrai que les comportements ne sont pas
différents.
C'est un peu plus clair pour moi maintenant.

Pour les différents tests que vous me suggérez ça va être difficile ,
puisque je ne faisais que vous rapporter un comportement qu'on m'a
décrit sous MAC.

Il va me falloir un peu de temps pour digérer toutes les infos des
différentes réponses.

Merci à tous,
GurneyH