Las diferencias entre varios métodos para definir variables/constantes globales en C/C++

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

Resumen:

1. Las variables globales no deben definirse en el archivo de encabezado. Sólo pueden declararse en el archivo de encabezado. La definición debe estar en el archivo fuente. 2.
Si desea utilizar variables globales en otros archivos, la forma más segura es declararlos en common.h. Definidos en common.cpp, otros archivos incluyen "common.h" para usar variables globales
3. El alcance de las variables globales modificadas por static y const solo puede ser su propia unidad de compilación, por lo que solo desea use variables globales en este archivo, solo en Use const o static en el archivo fuente para modificar
4. Solo se pueden declarar e inicializar variables en el dominio global, pero no se pueden realizar operaciones.

 

Antes de discutir las variables globales, primero debemos comprender algunos conceptos básicos:

1. Unidad de compilación (módulo):

    Hoy en día, con la popularidad de las herramientas de desarrollo IDE, muchas personas ya no tienen claros algunos conceptos de compilación. Lo que más temen muchos programadores es manejar errores de enlace (LINK ERROR), porque no le da errores de programa como errores de compilación. A menudo te molesta este tipo de error, pero si utilizas gcc, makefile y otras herramientas con frecuencia para realizar trabajos de desarrollo en Linux o integrados, entonces es posible que comprendas muy bien la diferencia entre compilación y conexión. Cuando termina de escribir el código en una herramienta de desarrollo como VC y hace clic en el botón compilar para prepararse para generar el archivo exe, VC en realidad realiza dos pasos : El primer paso es compilar cada archivo .cpp (.c) y el archivo .h correspondiente en obj; el segundo paso es ENLACE todos los archivos obj en el proyecto para generar el archivo .exe final. Entonces pueden ocurrir errores en dos lugares. Uno es un error de compilación, que es principalmente un error de sintaxis, y el otro es un error de conexión Los errores se deben principalmente a la definición repetida de variables, etc. Lo que llamamos unidad de compilación se refiere a cada archivo obj generado durante la fase de compilación . Un archivo obj es una unidad de compilación, es decir, un cpp (.c) y su correspondiente archivo .h juntos forman una unidad de compilación. El proyecto se compone de muchas unidades de compilación y cada archivo obj contiene la dirección relativa de almacenamiento de variables, etc.

2. La diferencia entre declaración y definición
    : cuando se declara una función o variable, no se le da espacio de memoria física real. Esto a veces puede garantizar que su programa se compila correctamente, pero cuando la función o variable está definida, está en la memoria. El espacio físico real es limitado. Si la variable externa a la que hace referencia en el módulo compilado no está definida en ninguna parte de todo el proyecto, incluso si se puede pasar durante la compilación, se informará un error durante la vinculación porque el programa no puede encontrarla. en la memoria a esta variable! También puedes entenderlo de esta manera: la misma variable o función se puede declarar varias veces, ¡pero la definición solo puede ser una vez!

3. El rol de extern
    extern tiene dos roles : primero, cuando se usa junto con "C", como: extern "C" void fun (int a, int b), le dice al compilador que compile la función fun. Al traducir el nombre de la función, siga las reglas de C para traducir el nombre de la función correspondiente en lugar de C++. Las reglas de C++ cambiarán el nombre fun más allá del reconocimiento al traducir el nombre de la función. Puede ser fun@aBc_int_int#%$ o algo más . Esto depende del "temperamento" del compilador (diferentes compiladores adoptan diferentes métodos). ¿Por qué haces esto? Porque C++ admite la sobrecarga de funciones. No discutiré demasiado este tema aquí. Si tiene Si está interesado, puede ¡Puede buscar en línea y creo que puede obtener una explicación satisfactoria!
    Cuando extern no se usa junto con "C" para modificar una variable o función, como en un archivo de encabezado: extern int g_Int; su función es declarar la función o global variable. La palabra clave alcance , las funciones y variables que declara se pueden usar en este módulo o en otros módulos. ¡Recuerde que es una declaración y no una definición! En otras palabras, si el módulo B (unidad de compilación) se refiere al módulo (unidad de compilación) unidad) definido en A Para variables o funciones globales, solo necesita incluir el archivo de encabezado del módulo A. Durante la fase de compilación, aunque el módulo B no puede encontrar la función o variable, no informará un error. Generará el objetivo código del módulo A al realizar la conexión.Esta función se encuentra en .

 Si ya comprende muy bien los conceptos anteriores, veamos las diferencias en el uso de las siguientes variables/constantes globales:

1. Variables globales modificadas con extern:
el papel de extern se ha mencionado anteriormente, demos un ejemplo, por ejemplo, existe la siguiente declaración en test1.h:

#ifndef TEST1H
#define TEST1H

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

#endif

En prueba1.cpp

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

Lo anterior es el módulo test1. Su compilación y conexión pueden pasar. Si también tenemos el módulo test2 y queremos usar g_str, solo necesitamos citarlo en el archivo original.

#include "test1.h"

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

 Los test1 y test2 anteriores se pueden compilar y conectar al mismo tiempo. Si está interesado, puede usar ultraEdit para abrir test1.obj. Puede encontrar la cadena "123456" en él, pero no puede encontrarla en test2.obj. Esto se debe a que g_str es una variable global de todo el proyecto. Solo hay una copia en la memoria . La unidad de compilación test2.obj no necesita tener otra copia, de lo contrario se informará el error de definición repetida durante la conexión. !
    A algunas personas les gusta poner la declaración y definición de variables globales juntas, esto puede evitar que se olvide la definición, como cambiar el test1.h anterior a extern char g_str[] = "123456"; // Esto equivale a no extern y luego elimine la definición de g_str en test1.cpp, esto Cuando compila y conecta los módulos test1 y test2, se informará un error de conexión . Esto se debe a que colocó la definición de la variable global g_str después del archivo de encabezado. El módulo test1.cpp contiene test1.h, por lo que g_str se define una vez. Y test2.cpp también contiene test1.h, por lo que g_str se define nuevamente. En este momento, el conector encuentra dos g_str al conectar test1 y test2. Si insiste en colocar la definición de g_str en test1.h, elimine #include "test1.h" del código de test2 y reemplácelo con:

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

       En este momento, el compilador sabe que g_str es un módulo de compilación externo y no lo definirá nuevamente en este módulo, pero quiero decir que esto es muy malo porque no puede usarlo en test2.cpp # incluye "test1.h" , entonces no puede usar otras funciones declaradas en test1.h , a menos que también se modifiquen con extern. En este caso, tendrá una larga lista de funciones declaradas y la función del archivo de encabezado es proporcionar La interfaz se proporciona externamente , así que recuerde hacer declaraciones solo en el archivo de encabezado. La verdad es siempre así de simple .

2. Variables globales modificadas con estática:

        En primer lugar, quiero decirles que static y extern son incompatibles, lo que significa que extern y static no pueden modificar una variable al mismo tiempo ; en segundo lugar, la variable global modificada por static se declara y define al mismo tiempo , lo que significa que cuando la variable global se declara usando estática en el archivo de encabezado, también se define al mismo tiempo ; finalmente, el alcance de la variable global modificada estática solo puede ser su propia unidad de compilación , lo que significa que es "global" solo es válido para esta unidad de compilación, y otras unidades de compilación no pueden verlo , como test1.h:

#ifndef TEST1H
#define TEST1H

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

#endif

prueba1.cpp:

#include "test1.h"

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

prueba2.cpp:

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

 Las dos unidades de compilación anteriores se pueden conectar con éxito. Cuando abre test1.obj, puede encontrar la cadena "123456" en él y también puede encontrarlas en test2.obj. La razón por la que se pueden conectar con éxito sin informar El error de definición duplicada se debe a que aunque tienen el mismo contenido, las direcciones físicas almacenadas son diferentes, al igual que a dos variables diferentes se les asigna el mismo valor, y estas dos variables actúan sobre sus respectivas unidades de compilación.
       Tal vez sea más serio y rastree y depure en secreto el código anterior. Como resultado, encontrará que la dirección de memoria de g_str en las dos unidades de compilación (test1, test2) es la misma, por lo que concluye que las variables modificadas estáticamente también pueden actuar en otros módulos, pero quiero decirte que tu compilador te está engañando. La mayoría de los compiladores tienen funciones de optimización del código para lograr el objetivo de generar un programa objetivo que ahorre memoria y tenga una mayor eficiencia de ejecución. Cuando el compilador conecta los distintos unidades de compilación, a veces, solo copiará una copia de la memoria con el mismo contenido, como "123456" arriba. Las variables ubicadas en las dos unidades de compilación tienen el mismo contenido, por lo que solo existirá una copia en la memoria al conectarse ., si cambia el código anterior a lo siguiente, puede exponer inmediatamente las mentiras del compilador:

prueba1.cpp:

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

prueba2.cpp:

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

Cuando rastree el código en este momento, encontrará que la dirección g_str en las dos unidades de compilación no es la misma. Debido a que la modificó en un lugar, el compilador fue restaurado por la fuerza a la apariencia original de la memoria, y hay dos copias en la memoria. Cópielo a las variables en los dos módulos. Es precisamente porque static tiene las características anteriores que al definir variables globales estáticas, generalmente se coloca en el archivo fuente en lugar del archivo de encabezado, para no causar contaminación de información innecesaria a otros módulos. ¡Recuerde también este principio!

3. Constantes globales modificadas por const:

Las constantes globales decoradas con const se utilizan ampliamente. Por ejemplo, las cadenas de mensajes de error en el software se definen mediante constantes globales. Las constantes globales modificadas por const tienen las mismas características que static , es decir, solo se pueden usar en este módulo compilado , pero const se puede usar con extern para declarar que la constante se puede usar en otros módulos compilados, como
extern const char. g_str[]; Entonces no olvides definirlo en el archivo original: const char g_str[] = "123456";

       Entonces, cuando se usa const solo, es lo mismo que static , y cuando se usa junto con extern, ¡sus características son las mismas que extern ! Así que no tengo mucho que describir const. Sólo quiero recordarles que const char* g_str = "123456" es diferente de const char g_str[] = "123465" . La const anterior modifica char * en lugar de g_str , su g_str no es una constante , se considera una variable global definida (puede ser utilizada por otras unidades de compilación), por lo que si desea que char *g_str cumpla con las reglas de las constantes globales const, es mejor definir const como este carácter* const g_str="123456"

Supongo que te gusta

Origin blog.csdn.net/SwordArcher/article/details/113512348
Recomendado
Clasificación