Variables globales en el archivo de encabezado

6 minutos de lectura

Variables globales en el archivo de encabezado
bruce

Tengo 2 módulos (archivos .c) y un archivo de encabezado .h:

archivo1.c:

#include <stdio.h>
#include "global.h"

int main()
{
    i = 100;
    printf("%d\n",i);
    foo();
    return 0;
}

archivo2.c

#include <stdio.h>
#include "global.h"

void foo()
{
    i = 10;
    printf("%d\n",i);
}

global.h

int i;
extern void foo()

Cuando hago gcc file1.c file2.c todo funciona bien y obtengo el resultado esperado. Ahora, cuando inicializo la variable ‘i’ en el archivo de encabezado para decir 0 y compilo de nuevo, aparece un error de vinculación:

/tmp/cc0oj7yA.o:(.bss+0x0): multiple definition of `i'
/tmp/cckd7TTI.o:(.bss+0x0): first defined here

Si solo compilo file1.c (eliminando la llamada a foo()) con la inicialización en el archivo de encabezado, es decir, gcc file1.c, todo funciona bien. Que esta pasando?

Hay 3 escenarios, usted describe:

  1. con 2 .c archivos y con int i; en el encabezado.
  2. con 2 .c archivos y con int i=100; en el encabezado (o cualquier otro valor; eso no importa).
  3. con 1 .c archivo y con int i=100; en el encabezado.

En cada escenario, imagine el contenido del archivo de encabezado insertado en el .c archivo y esto .c archivo compilado en un .o archivo y luego estos vinculados entre sí.

Entonces sucede lo siguiente:

  1. funciona bien debido a las “definiciones tentativas” ya mencionadas: cada .o El archivo contiene uno de ellos, por lo que el enlazador dice “ok”.

  2. no funciona, porque ambos .o los archivos contienen una definición con un valor, que chocan (incluso si tienen el mismo valor); puede haber solo uno con cualquier nombre dado en todos .o archivos que están vinculados entre sí en un momento dado.

  3. funciona, por supuesto, porque solo tienes uno .o archivo y por lo tanto no hay posibilidad de colisión.

En mi humilde opinión, una cosa limpia sería

  • para poner cualquiera extern int i; o solo int i; en el archivo de cabecera,
  • y luego poner la definición “real” de i (a saber int i = 100;) dentro file1.c. En este caso, esta inicialización se usa al inicio del programa y la línea correspondiente en main() puede ser omitido. (Además, espero que el nombre sea solo un ejemplo; no nombre ninguna variable global como i en programas reales.)

  • Lo que dije sobre “poner cualquiera extern int i; o solo int i; en el archivo de encabezado”: extern int i es mejor porque te dice inmediatamente si la definición “real” falta por algún accidente. con un mero int ihabría una definición silenciosa para 0.

    – glglgl

    13 de noviembre de 2011 a las 12:06

  • No obtuve la primera explicación, ¿puede dar más detalles sobre cuándo la memoria se asigna a la variable i? hasta ahora lo que entiendo es int i en global.h es equivalente a extern int i; lo que significa que ambos archivos de objetos tienen la referencia de esa memoria a i que está asignada en otro lugar.

    – usuario

    11 de febrero de 2015 a las 7:12


  • @user2383973 El enlazador asigna y reserva la memoria. Cuando ve una solicitud con asignación y una definición “provisional”, todo está bien. Cuando ve varias asignaciones, incluso si tienen el mismo valor, no está bien.

    – glglgl

    11 de febrero de 2015 a las 12:16

  • @FoadRezek Supongo que probó la estructura de archivos en la pregunta. Esto se debe a que las asignaciones no son válidas a nivel de archivo, solo dentro de las funciones.

    – glglgl

    30 de mayo de 2018 a las 8:40

  • el caso 1 es un comportamiento indefinido, la definición tentativa hace que se genere una definición externa para cada unidad de traducción en la que aparece. (C11 6.9.2/2). Tu recomendación sin extern tiene el mismo problema

    –MM

    7 de febrero de 2020 a las 5:13


No inicialice las variables en los encabezados. Ponga la declaración en el encabezado y la inicialización en uno de los c archivos

En el encabezado:

extern int i;

En archivo2.c:

int i=1;

  • Tenga en cuenta que el OP no está inicializando la variable, es una definición tentativa.

    – ninjalj

    13 de noviembre de 2011 a las 1:01

  • @Banthar: ¿Por qué funciona en el segundo caso entonces (cuando solo compilo file1.c)?

    – bruce

    13 de noviembre de 2011 a las 1:29

  • @Bruce: Porque en este caso, solo se inicializa una vez.

    – glglgl

    13 de noviembre de 2011 a las 2:12

1647589088 96 Variables globales en el archivo de encabezado
ninjalj

No debe definir variables globales en archivos de encabezado. Puedes declararlos como extern en el archivo de encabezado y definirlos en un .c archivo fuente.

(Nota: en C, int i; es una definición tentativa, asigna almacenamiento para la variable (= es una definición) si no se encuentra otra definición para esa variable en la unidad de traducción).

  • ¿Qué pasa si pongo #ifndef en el encabezado y declaro la variable? int a en el encabezado? Sigue apareciendo un error de definición múltiple, aunque ahora solo tengo una copia del archivo de encabezado.

    – CKM

    14 de octubre de 2016 a las 9:54


La respuesta actualmente aceptada a esta pregunta es incorrecta. C11 6.9.2/2:

Si una unidad de traducción contiene una o más definiciones tentativas para un identificador, y la unidad de traducción no contiene una definición externa para ese identificador, entonces el comportamiento es exactamente como si la unidad de traducción contuviera una declaración de alcance de archivo de ese identificador, con el tipo compuesto como del final de la unidad de traducción, con un inicializador igual a 0.

Entonces el código original en la pregunta se comporta como si file1.c y file2.c cada uno contenía la línea int i = 0; al final, lo que provoca un comportamiento indefinido debido a múltiples definiciones externas (6.9/5).

Dado que esta es una regla semántica y no una restricción, no se requiere diagnóstico.

Aquí hay dos preguntas más sobre el mismo código con respuestas correctas:

  • Definiciones tentativas en C y enlaces
  • ¿Cómo uso extern para compartir variables entre archivos fuente?

@glglgl ya explicó por qué lo que intentabas hacer no funcionaba. En realidad, si realmente estás apuntando a definiendo una variable en un encabezado, puede engañar usando algunas directivas de preprocesador:

archivo1.c:

#include <stdio.h>

#define DEFINE_I
#include "global.h"

int main()
{
    printf("%d\n",i);
    foo();
    return 0;
}

archivo2.c:

#include <stdio.h>
#include "global.h"

void foo()
{
    i = 54;
    printf("%d\n",i);
}

global.h:

#ifdef DEFINE_I
int i = 42;
#else
extern int i;
#endif

void foo();

En esta situación, i es solo definido en la unidad de compilación donde definió DEFINE_I y es declarado en todos lados. El enlazador no se queja.

He visto esto un par de veces antes donde se declaraba una enumeración en un encabezado, y justo debajo había una definición de un char** que contenía las etiquetas correspondientes. Entiendo por qué el autor prefirió tener esa definición en el encabezado en lugar de ponerla en un archivo fuente específico, pero no estoy seguro de si la implementación es tan elegante.

1647589089 199 Variables globales en el archivo de encabezado
Anand Nekkunti

No defina varibale en el archivo de encabezado, haga una declaración en el archivo de encabezado (buena práctica) … en su caso, está funcionando porque hay múltiples símbolos débiles. Lea sobre el símbolo débil y fuerte … enlace:http://csapp.cs.cmu.edu/public/ch7-preview.pdf

Este tipo de código crea un problema durante la migración.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad