Puisque vous savez déjà qu'il ne faut pas se fier à cette information sauf en cas de nécessité absolue, la voici. Mon observation générale à travers diverses chaînes d'outils (MSVC, gcc/ld, clang/llvm, etc) est que l'ordre dans lequel vos fichiers objets sont passés à l'éditeur de liens est l'ordre dans lequel ils seront initialisés.
Il existe des exceptions à cette règle, et je ne prétends pas les connaître toutes, mais voici celles que j'ai rencontrées moi-même :
1) Les versions de GCC antérieures à 4.7 initialisent en fait dans l'ordre inverse de la ligne de liaison. Ce billet dans GCC est le moment où le changement s'est produit, et il a cassé beaucoup de programmes qui dépendaient de l'ordre d'initialisation (y compris le mien !).
2) Dans GCC et Clang, l'utilisation de la fonction fonction constructeur priorité peut modifier l'ordre d'initialisation. Notez que cela ne s'applique qu'aux fonctions qui sont déclarées comme étant des "constructeurs" (c'est-à-dire qu'elles doivent être exécutées comme le serait un constructeur d'objet global). J'ai essayé d'utiliser les priorités de cette façon et j'ai constaté que même avec la plus haute priorité sur une fonction de constructeur, tous les constructeurs sans priorité (par exemple, les objets globaux normaux, les fonctions de construction sans priorité) seront initialisés. primero . En d'autres termes, la priorité n'est relative qu'aux autres fonctions prioritaires, mais les véritables citoyens de première classe sont ceux qui ne sont pas prioritaires. Pour aggraver les choses, cette règle est effectivement l'inverse dans GCC avant la version 4.7 en raison du point (1) ci-dessus.
3) Sous Windows, il existe une fonction de point d'entrée de bibliothèque partagée (DLL) très soignée et utile appelée DllMain() qui, s'il est défini, sera exécuté avec le paramètre "fdwReason" égal à DLL_PROCESS_ATTACH directement après que toutes les données globales aient été initialisées et que antes de l'application consommatrice a la possibilité d'appeler n'importe quelle fonction de la DLL. C'est extrêmement utile dans certains cas, et il est absolument nécessaire de faire appel à des fonctions de la DLL. n'est pas Il n'existe pas de comportement analogue à celui-ci sur d'autres plateformes avec GCC ou Clang avec C ou C++. Ce qui s'en rapproche le plus est la création d'une fonction de constructeur avec priorité (voir le point (2) ci-dessus), ce qui n'est absolument pas la même chose et ne fonctionnera pas pour la plupart des cas d'utilisation pour lesquels DllMain() fonctionne.
4) Si vous utilisez CMake pour générer vos systèmes de construction, ce que je fais souvent, j'ai constaté que l'ordre des fichiers sources d'entrée sera l'ordre des fichiers objets résultants donnés à l'éditeur de liens. Cependant, il arrive souvent que votre application/DLL soit également liée à d'autres bibliothèques, dans ce cas, ces bibliothèques seront sur la ligne de liaison. après vos fichiers sources d'entrée. Si vous souhaitez que l'un de vos objets globaux soit l'objet tout premier pour s'initialiser, alors vous avez de la chance et vous pouvez mettre le fichier source contenant cet objet en premier dans la liste des fichiers sources. Cependant, si vous souhaitez que l'un d'entre eux soit le fichier source de l'objet la toute dernière pour s'initialiser (ce qui peut effectivement reproduire le comportement de DllMain() !) alors vous pouvez faire un appel à add_library() avec ce seul fichier source pour produire une bibliothèque statique, et ajouter la bibliothèque statique résultante comme toute dernière dépendance de lien dans votre appel target_link_libraries() pour votre application/DLL. Soyez conscient que votre objet global peut être optimisé dans ce cas et vous pouvez utiliser la fonction --whole-archive pour forcer l'éditeur de liens à ne pas supprimer les symboles inutilisés pour ce petit fichier d'archive spécifique.
Conseil de clôture
Pour connaître absolument l'ordre d'initialisation résultant de votre application/bibliothèque partagée liée, passez --print-map à l'éditeur de liens ld et recherchez .init_array (ou dans GCC avant 4.7, recherchez .ctors). Chaque constructeur global sera imprimé dans l'ordre dans lequel il sera initialisé, et rappelez-vous que l'ordre est opposé dans GCC avant 4.7 (voir le point (1) ci-dessus).
Le facteur de motivation pour écrire cette réponse est que j'avais besoin de connaître cette information, que je n'avais pas d'autre choix que de me fier à l'ordre d'initialisation et que je n'ai trouvé que des bribes de cette information dans d'autres messages de l'OS et dans des forums Internet. La plupart de ces informations ont été apprises par l'expérimentation, et j'espère que cela évitera à certaines personnes de devoir le faire !