
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:
- con 2
.c
archivos y con int i;
en el encabezado.
- con 2
.c
archivos y con int i=100;
en el encabezado (o cualquier otro valor; eso no importa).
- 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:
-
funciona bien debido a las “definiciones tentativas” ya mencionadas: cada .o
El archivo contiene uno de ellos, por lo que el enlazador dice “ok”.
-
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.
-
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.)
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;

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).
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.

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.