Les différences entre plusieurs méthodes de définition de variables/constantes globales en C/C++

Tiré de : https://www.cnblogs.com/catkins/p/5270388.html

Résumé:

1. Les variables globales ne doivent pas être définies dans le fichier d'en-tête. Elles peuvent uniquement être déclarées dans le fichier d'en-tête. La définition doit être dans le fichier source. 2. Si vous souhaitez utiliser
des variables globales dans d'autres fichiers, le moyen le plus sûr est de déclarez-les dans common.h. Défini dans common.cpp, d'autres fichiers incluent "common.h" pour utiliser des variables globales
3. La portée des variables globales modifiées par static et const ne peut être que la propre unité de compilation, vous souhaitez donc uniquement utilisez des variables globales dans ce fichier, juste dans Utilisez const ou static dans le fichier source pour modifier
4. Seules les variables peuvent être déclarées et initialisées dans le domaine global, mais aucune opération ne peut être effectuée.

 

Avant de discuter des variables globales, nous devons d'abord comprendre quelques concepts de base :

1. Unité de compilation (module) :

    Aujourd'hui, avec la popularité des outils de développement IDE, beaucoup de gens ne comprennent plus certains concepts de compilation. Ce que beaucoup de programmeurs craignent le plus, c'est la gestion des erreurs de lien (LINK ERROR), car cela ne vous donne pas d'erreurs de programme comme les erreurs de compilation. , vous vous sentez souvent agacé par ce genre d'erreur, mais si vous utilisez souvent gcc, makefile et d'autres outils pour effectuer des travaux de développement sous Linux ou embarqué, alors vous comprendrez peut-être très bien la différence entre compilation et connexion ! Lorsque vous avez fini d'écrire le code sur un outil de développement tel que VC et que vous cliquez sur le bouton de compilation pour préparer la génération du fichier exe, VC effectue en fait deux étapes . La première étape consiste à compiler chaque fichier .cpp (.c) et le fichier .h correspondant dans obj ; la deuxième étape consiste à lier tous les fichiers obj du projet pour générer le fichier .exe final. Des erreurs peuvent alors survenir à deux endroits. L'un est une erreur de compilation, qui est principalement une erreur de syntaxe, et l'autre est un erreur de connexion. Les erreurs sont principalement causées par la définition répétée de variables, etc. Ce que l'on appelle une unité de compilation fait référence à chaque fichier obj généré lors de la phase de compilation . Un fichier obj est une unité de compilation, c'est-à-dire qu'un cpp (.c) et son fichier .h correspondant forment ensemble une unité de compilation. Le projet est composé de nombreuses unités de compilation, et chaque fichier obj contient l'adresse relative du stockage des variables, etc.

2. La différence entre déclaration et définition
    : Lorsqu'une fonction ou une variable est déclarée, aucun espace mémoire physique réel ne lui est attribué. Cela peut parfois garantir que votre programme se compile avec succès, mais lorsque la fonction ou la variable est définie, elle est en mémoire . L'espace physique réel est limité. Si la variable externe que vous référencez dans le module compilé n'est définie nulle part dans l'ensemble du projet, alors même si elle peut être transmise lors de la compilation, une erreur sera signalée lors de la liaison car le programme ne peut pas la trouver. dans la mémoire.à cette variable! Vous pouvez aussi le comprendre de cette façon, la même variable ou fonction peut être déclarée plusieurs fois, mais la définition ne peut l'être qu'une seule fois !

3. Le rôle de extern
    extern a deux rôles.Premièrement , lorsqu'il est utilisé avec "C", comme : extern "C" void fun(int a, int b);il indique au compilateur de compiler la fonction fun. Lors de la traduction du nom de la fonction, suivez les règles du C pour traduire le nom de la fonction correspondant au lieu du C++. Les règles du C++ modifieront le nom fun de manière méconnaissable lors de la traduction du nom de la fonction. Il peut s'agir de fun@aBc_int_int#%$ ou autre chose. . Cela dépend de "l'humeur" du compilateur (différents compilateurs adoptent des méthodes différentes). Pourquoi faites-vous cela ? Parce que C++ prend en charge la surcharge de fonctions. Je ne discuterai pas trop de ce problème ici. Si vous êtes intéressé, vous pouvez effectuer une recherche en ligne et je pense que vous pouvez obtenir une explication satisfaisante !
    Lorsque extern n'est pas utilisé avec "C" pour modifier une variable ou une fonction, comme dans un fichier d'en-tête : extern int g_Int ; sa fonction est de déclarer la fonction ou le global variable. Le mot clé scope , les fonctions et variables qu'il déclare peuvent être utilisées dans ce module ou dans d'autres modules. N'oubliez pas qu'il s'agit d'une déclaration et non d'une définition ! En d'autres termes, si le module B (unité de compilation) fait référence au module (unité de compilation unité) définie dans A Pour les variables ou fonctions globales, il suffit d'inclure le fichier d'en-tête du module A. Pendant la phase de compilation, bien que le module B ne trouve pas la fonction ou la variable, il ne signalera pas d'erreur. Il générera la cible code du module A lors de la connexion. Cette fonction se trouve dans .

 Si vous comprenez déjà très bien les concepts ci-dessus, examinons les différences dans l'utilisation des variables/constantes globales suivantes :

1. Variables globales modifiées avec extern :
Le rôle de extern a été mentionné ci-dessus. Donnons un exemple. Par exemple, il y a l'instruction suivante dans test1.h :

#ifndef TEST1H
#define TEST1H

extern char g_str[]; // 声明全局变量g_str
void fun1();

#endif

Dans test1.cpp

 #include "test1.h"
    
 char g_str[] = "123456"; // 定义全局变量g_str
    
 void fun1()
 {
     cout << g_str << endl;
 }

Ce qui précède est le module test1. Sa compilation et sa connexion peuvent réussir. Si nous avons également le module test2 et que nous voulons utiliser g_str, il suffit de le citer dans le fichier d'origine.

#include "test1.h"

void fun2()
{
    cout << g_str << endl;
}

 Les test1 et test2 ci-dessus peuvent être compilés et connectés en même temps. Si vous êtes intéressé, vous pouvez utiliser ultraEdit pour ouvrir test1.obj. Vous pouvez y trouver la chaîne "123456", mais vous ne la trouvez pas dans test2.obj. .C'est à cause deg_strEst une variable globale de l'ensemble du projet.Il n'y a qu'une seule copie en mémoire.L'Unité de compilation test2.obj n'a pas besoin d'une autre copie, sinon une erreur de définition répétée sera signalée lors de la connexion !
    Certaines personnes aiment mettre la déclaration et la définition des variables globales dans Together, cela peut éviter d'oublier la définition, comme changer le test1.h ci-dessus en extern char g_str[] = "123456"; // Ceci équivaut à aucun extern , puis supprimez la définition de g_str dans test1.cpp, Ceci Lorsque vous compilez et connectez les modules test1 et test2, une erreur de connexion sera signalée . En effet, vous avez placé la définition de la variable globale g_str après le fichier d'en-tête. Le module test1.cpp contient test1.h, donc g_str est défini une fois. Et test2.cpp contient également test1.h, donc g_str est défini à nouveau. À ce stade, le connecteur trouve deux g_str lors de la connexion de test1 et test2. Si vous insistez pour placer la définition de g_str dans test1.h, supprimez #include "test1.h" du code de test2 et remplacez-le par :

extern char g_str[];
void fun2()
{
    cout << g_str << endl;
}

       Pour le moment, le compilateur sait que g_str est un module de compilation externe et ne le définira plus dans ce module. Mais je tiens à dire que c'est très mauvais car vous ne pouvez pas l'utiliser dans test2.cpp# include "test1.h" , alors vous ne pouvez pas utiliser d'autres fonctions déclarées dans test1.h , à moins qu'elles ne soient également modifiées avec extern. Dans ce cas, vous aurez une longue liste de fonctions déclarées, et la fonction du fichier d'en-tête est de fournir L'interface est fournie en externe , alors n'oubliez pas de faire des déclarations uniquement dans le fichier d'en-tête. La vérité est toujours aussi simple .

2. Variables globales modifiées avec statique :

        Tout d'abord, je tiens à vous dire que static et extern sont incompatibles, ce qui signifie que extern et static ne peuvent pas modifier une variable en même temps ; deuxièmement, la variable globale modifiée par static est déclarée et définie en même temps , ce qui signifie que lorsque vous Après que la variable globale soit déclarée en utilisant static dans le fichier d'en-tête, elle est également définie en même temps ; enfin, la portée de la variable globale statique modifiée ne peut être que sa propre unité de compilation , ce qui signifie que son "global" n'est valable que pour cette unité de compilation, et d'autres unités de compilation ne peuvent pas le voir ,Par exemple, test1.h :

#ifndef TEST1H
#define TEST1H

static char g_str[] = "123456"; 
void fun1();

#endif

test1.cpp:

#include "test1.h"

void fun2()
{
    cout << g_str << endl;
}

test2.cpp:

#include "test1.h"
    
void fun2()
{
    cout << g_str << endl;
}

 Les deux unités de compilation ci-dessus peuvent être connectées avec succès. Lorsque vous ouvrez test1.obj, vous pouvez y trouver la chaîne "123456", et vous pouvez également les trouver dans test2.obj. La raison pour laquelle elles peuvent être connectées avec succès sans signaler Le L'erreur de définition en double est due au fait que bien qu'elles aient le même contenu, les adresses physiques stockées sont différentes, tout comme deux variables différentes se voient attribuer la même valeur, et ces deux variables agissent sur leurs unités de compilation respectives.
       Peut-être que vous êtes plus sérieux et que vous tracez et déboguez secrètement le code ci-dessus. En conséquence, vous constatez que l'adresse mémoire de g_str dans les deux unités de compilation (test1, test2) est la même, vous concluez donc que les variables modifiées statiquement peuvent également agir sur d'autres modules, mais je tiens à vous dire que votre compilateur vous trompe. La plupart des compilateurs ont des fonctions d'optimisation du code afin d'atteindre l'objectif de générer un programme cible qui économise de la mémoire et a une plus grande efficacité d'exécution. Lorsque le compilateur connecte les différents unités de compilation, Parfois, il ne copiera qu'une seule copie de la mémoire avec le même contenu, comme "123456" ci-dessus. Les variables situées dans les deux unités de compilation ont le même contenu, donc une seule copie existera dans la mémoire lors de la connexion . , si vous modifiez le code ci-dessus comme suit, vous pouvez immédiatement exposer les mensonges du compilateur :

test1.cpp:

#include "test1.h"
    
void fun1()
{
    g_str[0] = 'a';
    cout << g_str << endl;
}

test2.cpp:

#include "test1.h"
    
void fun2()
{
    cout << g_str << endl;
}
void main()
{
    fun1(); // a23456
    fun2(); // 123456
}

Lorsque vous tracez le code à ce moment-là, vous constaterez que l'adresse g_str dans les deux unités de compilation n'est pas la même. Parce que vous l'avez modifié au même endroit, le compilateur a été restauré de force à l'apparence d'origine de la mémoire, et il y a deux copies en mémoire. Copiez-le dans les variables des deux modules. C'est précisément parce que static a les caractéristiques ci-dessus que lors de la définition de variables globales statiques, il est généralement placé dans le fichier source au lieu du fichier d'en-tête, afin de ne pas causer de pollution inutile des informations aux autres modules. Rappelez-vous également ce principe. !

3. Constantes globales modifiées par const :

Les constantes globales décorées avec const sont largement utilisées. Par exemple, les chaînes de messages d'erreur dans les logiciels sont définies à l'aide de constantes globales. Les constantes globales modifiées par const ont les mêmes caractéristiques que static , c'est-à-dire qu'elles ne peuvent être utilisées que dans ce module compilé , mais const peut être utilisé avec extern pour déclarer que la constante peut être utilisée dans d'autres modules compilés, comme
extern const char g_str[]; Alors n'oubliez pas de le définir dans le fichier d'origine : const char g_str[] = "123456";

       Ainsi, lorsque const est utilisé seul, c'est la même chose que static , et lorsqu'il est utilisé avec extern, ses caractéristiques sont les mêmes que extern ! Je n'ai donc rien à décrire de trop sur const. Je veux juste vous rappeler que const char* g_str = "123456" est différent de const char g_str[] = "123465" . La const précédente modifie char * au lieu de g_str , son g_str n'est pas une constante , il est considéré comme une variable globale définie (peut être utilisée par d'autres unités de compilation), donc si vous voulez que char *g_str respecte les règles des constantes globales const, il est préférable de définir const comme ce char* const g_str="123456"

Je suppose que tu aimes

Origine blog.csdn.net/SwordArcher/article/details/113512348
conseillé
Classement