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

Ordre d'initialisation de variables non locales et types POD

2 réponses
Avatar
Patrick Mézard
Bonjour,

Cas 1 : chaîne de caractères const référencée par une autre globale
const dans un autre module.

a.h
----------------------
extern const char s[];
----------------------

a.cpp
----------------------
const char s[] = "valeur";
----------------------

b.cpp
----------------------
#include "a.h"

struct Test
{
const char* str;
};

static const Test tests[] =
{
{s},
};
----------------------

D'après ce que j'ai compris, l'ordre d'initialisation des variables
globales de type POD initialisées à partir de valeurs constantes entre
deux modules distincts est non-spécifié.

Ceci étant dit, est-ce que tests[] contiendra un élément pointant sur la
chaîne "valeur" ou pas ? J'ai un peu de mal à savoir ce qu'est la
"valeur" de la chaîne s : son contenu, son adresse, les deux ?
On pourrait imaginer que la zone mémoire de s[] soit réservée à l'avance
et que sa valeur fluctue de manière non spécifiée avant l'initialisation
de tests[]. En même temps, ce serait problématique pour les
bibliothèques dynamique, donc...

Que dit le standard à ce sujet ?
Si le comportement n'est pas spécifié, comment faites-vous pour règler
ce genre de trucs ? A coup de macro ? A coup de fonction globale ?


Cas 2 : utilisation d'une globale int par une autre

a.h
----------------------
extern const int a;
----------------------

a.cpp
----------------------
const int a = 1;
----------------------

b.cpp
----------------------
#include "a.h"

const int b = 2*a;
----------------------

On est clairement dans le cas non spécifié.
Encore une fois comment règlez vous ça ? A coup de macro (on perd le
typage) ? A coup de fonction globale qui renvoie une référence ?

Merci d'avance.

--
Patrick Mézard

2 réponses

Avatar
kanze
Patrick Mézard wrote:

Cas 1 : chaîne de caractères const référencée par une autre
globale const dans un autre module.

a.h
----------------------
extern const char s[];
----------------------

a.cpp
----------------------
const char s[] = "valeur";
----------------------

b.cpp
----------------------
#include "a.h"

struct Test
{
const char* str;
};

static const Test tests[] =
{
{s},
};
----------------------

D'après ce que j'ai compris, l'ordre d'initialisation des
variables globales de type POD initialisées à partir de
valeurs constantes entre deux modules distincts est
non-spécifié.


Oui et non. L'initialisation a lieu en trois phases :
l'initialisation à zéro, les initialisations statiques et les
initialisations dynamiques. Et c'est garanti qu'elles ont lieu
dans cet ordre. Ensuite, il y a des histoires d'ordre
d'initialisation dynamique, qui n'est effectivement pas
spécifié pour les objets définis dans des modules différentes,
ni du tout pour des objets qui sont des instanciations des
templates (membres statiques des classes templatées).

En ce qui concerne l'ordre des initialisations à l'intérieur des
deux premières phases... ce n'est pas spécifiées, mais ça n'a
aucune importance, puisque par définition, une initialisation ne
peut pas dépendre d'une autre. Et puisque toutes les
initialisatoins dans tes exemples sont des initialisations
statiques, il n'y a aucun problème.

Ceci étant dit, est-ce que tests[] contiendra un élément
pointant sur la chaîne "valeur" ou pas ?


tests[] contiendra un élément qui contient l'adresse de s. Et s
contiendra bien la chaîne "valeur".

J'ai un peu de mal à savoir ce qu'est la "valeur" de la chaîne
s : son contenu, son adresse, les deux ?


La valeur de s, c'est bien son contenu. Et le type de s, c'est
char const[7], et c'est un lvalue ; c-à-d, grosso modo, qu'il a
une adresse. Et puisqu'il a une durée de vie statique, cette
adresse est constante.

Le type de Test::str est char const* ; pour l'initialiser, il
faut un char const* (et non un char const[7]). Mais en
l'occurance, il y a une conversion implicite de char const[] en
char const*, qui s'applique lorsque le char const[] est un
lvalue, et qu'il faut un char const*. Le résultat de cette
conversion, c'est bien l'adresse du premier élément du tableau,
et puisque cette adresse est constante, c'est aussi une
expression constante (et donc, l'initialisation est statique).

On pourrait imaginer que la zone mémoire de s[] soit réservée
à l'avance et que sa valeur fluctue de manière non spécifiée
avant l'initialisation de tests[].


Avant ou après. En revanche, il ne peut pas fluctuer une fois
les initialisations statiques finies. Et il n'y a aucune façon
de voir sa valeur avant la fin des initialisations statiques.

En même temps, ce serait problématique pour les bibliothèques
dynamique, donc...


Pas vraiment. La norme ne dit rien sur les bibliothèques
dynamiques, mais dans la pratique, elles marchent exactement
comme des modules statiques à cet égard. C-à-d que les variables
statiques dont l'initialisation est une expression constante
seront toutes initialisées lors du chargement de la module,
avant l'exécution du code quelconque dans la module ;
conceptuellement, au moins, avant que la mémoire qui les
contient en soit mappé au processus.

Et en passant, je n'ai jamais vu un système qui supporte des
bibliothèques dynamiques. Malgré leur nom sous Windows, ce sont
des objets dynamiques (au sens du fichier objet).

Que dit le standard à ce sujet ?


En ce qui concerne les objets dynamiques, que c'est un
comportement indéfini de s'en servir:-). On sort de la norme,
pour entrer dans le domaine du comportement défini par
l'implémentation. Dans la pratique, toutes les implémentations
ont traitées les initialisations statiques exactement comme je
viens de décrire : en fait, les valeurs se trouvent dans
l'image disque qui est chargé en mémoire. Tout au début des
objets dynamiques, en revanche, il y a eu des implémentations
qui n'appelaient pas les initialisations dynamiques.

Si le comportement n'est pas spécifié, comment faites-vous
pour règler ce genre de trucs ? A coup de macro ? A coup de
fonction globale ?


Au moins en ce qui concerne ton exemple, le comportement est
bien spécifié.

Cas 2 : utilisation d'une globale int par une autre

a.h
----------------------
extern const int a;
----------------------

a.cpp
----------------------
const int a = 1;
----------------------

b.cpp
----------------------
#include "a.h"

const int b = 2*a;
----------------------

On est clairement dans le cas non spécifié.


Oui. Parce que l'initialisation n'est pas statique, mais
dynamique. (En fait, la norme, au moins dans la version 1998,
dit que c'est une initialisation statique, et donc que c'est le
problème du compilateur de le faire marcher. Mais aucun ne le
fait, et je crois que c'est une erreur dans la norme.)

Encore une fois comment règlez vous ça ? A coup de macro (on
perd le typage) ? A coup de fonction globale qui renvoie une
référence ?


Tout dépend des cas. Tout d'abord, pour des cas simples, comme
ici, on se passe du global, et on déclare une variable statique
dans l'en-tête :

a.h
----------------------
static int const a = 1 ;
----------------------

On le fait d'autant plus que comme ceci, la valeur peut servir
comme expression constante integrale, comme dimension d'un
tableau, par exemple.

Ensuite, pour tout ce qui n'est pas constante entière, dans la
mesure du possible, on évite les dépendances. (En fait, en
général, les variables à durée de vie doivent être assez rares.)
Comme ça, pas de problème. Quand on ne peut pas les éviter, en
revanche. on se rabat en général sur une variante du modèle de
singleton : en gros, c'est ta fonction qui renvoie la valeur.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Sylvain
kanze wrote on 16/08/2006 09:54:

Cas 2 : utilisation d'une globale int par une autre
On est clairement dans le cas non spécifié.


Oui. Parce que l'initialisation n'est pas statique, mais
dynamique. [...] c'est le problème du compilateur de le
faire marcher. Mais aucun ne le fait [...]


pour l'anecdote, VC le fait correctement - mais ça ne règle pas le fond.

Sylvain.