variable pasar otro externo archivo c global-variables extern

otro - pasar variable php a archivo javascript externo



¿Cómo uso extern para compartir variables entre archivos de origen? (15)

Agregar un extern convierte una definición de variable en una declaración de variable. Vea este hilo en cuanto a cuál es la diferencia entre una declaración y una definición.

Sé que las variables globales en C a veces tienen la palabra clave extern . ¿Qué es una variable extern ? ¿Cómo es la declaración? ¿Cuál es su alcance?

Esto está relacionado con compartir variables en los archivos de origen, pero ¿cómo funciona eso precisamente? ¿Dónde uso extern ?


El uso de extern solo es relevante cuando el programa que está creando consta de varios archivos de origen vinculados entre sí, donde algunas de las variables definidas, por ejemplo, en el archivo de origen file1.c deben estar referenciadas en otros archivos de origen, como file2.c .

Es importante comprender la diferencia entre definir una variable y declarar una variable :

  • Se declara una variable cuando se informa al compilador que existe una variable (y este es su tipo); no asigna el almacenamiento para la variable en ese punto.
  • Una variable se define cuando el compilador asigna el almacenamiento para la variable.

Puede declarar una variable varias veces (aunque una vez es suficiente); Solo puedes definirlo una vez dentro de un alcance dado. Una definición de variable también es una declaración, pero no todas las declaraciones de variables son definiciones.

La mejor manera de declarar y definir variables globales

La forma limpia y confiable de declarar y definir variables globales es usar un archivo de encabezado para contener una declaración extern de la variable.

El encabezado está incluido por el único archivo de origen que define la variable y por todos los archivos de origen que hacen referencia a la variable. Para cada programa, un archivo de origen (y solo un archivo de origen) define la variable. De manera similar, un archivo de encabezado (y solo un archivo de encabezado) debe declarar la variable. El archivo de encabezado es crucial; permite la verificación cruzada entre unidades de traducción independientes (unidades de traducción, archivos fuente de think think) y garantiza la consistencia.

Aunque hay otras formas de hacerlo, este método es simple y confiable. Se demuestra mediante file3.h , file1.c y file2.c :

archivo3.h

extern int global_variable; /* Declaration of the variable */

archivo1.c

#include "file3.h" /* Declaration made available here */ #include "prog1.h" /* Function declarations */ /* Variable defined here */ int global_variable = 37; /* Definition checked against declaration */ int increment(void) { return global_variable++; }

archivo2.c

#include "file3.h" #include "prog1.h" #include <stdio.h> void use_it(void) { printf("Global variable: %d/n", global_variable++); }

Esa es la mejor manera de declarar y definir variables globales.

Los siguientes dos archivos completan la fuente para prog1 :

Los programas completos que se muestran usan funciones, por lo que las declaraciones de funciones se han introducido. Tanto C99 como C11 requieren que las funciones se declaren o definan antes de que se usen (mientras que C90 no lo hizo, por buenas razones). Uso la palabra clave extern delante de las declaraciones de funciones en los encabezados para mantener la coherencia, para hacer coincidir el extern frente a las declaraciones de variables en los encabezados Muchas personas prefieren no usar extern delante de las declaraciones de funciones; al compilador no le importa, y en última instancia, tampoco a mí, siempre y cuando usted sea consistente, al menos dentro de un archivo fuente.

prog1.h

extern void use_it(void); extern int increment(void);

prog1.c

#include "file3.h" #include "prog1.h" #include <stdio.h> int main(void) { use_it(); global_variable += 19; use_it(); printf("Increment: %d/n", increment()); return 0; }

  • prog1 usa prog1.c , file1.c , file2.c , file3.h y prog1.h .

El archivo prog1.mk es un makefile solo para prog1 . Funcionará con la mayoría de las versiones de make producidas desde aproximadamente el cambio de milenio. No está vinculado específicamente a GNU Make.

prog1.mk

# Minimal makefile for prog1 PROGRAM = prog1 FILES.c = prog1.c file1.c file2.c FILES.h = prog1.h file3.h FILES.o = ${FILES.c:.c=.o} CC = gcc SFLAGS = -std=c11 GFLAGS = -g OFLAGS = -O3 WFLAG1 = -Wall WFLAG2 = -Wextra WFLAG3 = -Werror WFLAG4 = -Wstrict-prototypes WFLAG5 = -Wmissing-prototypes WFLAGS = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5} UFLAGS = # Set on command line only CFLAGS = ${SFLAGS} ${GFLAGS} ${OFLAGS} ${WFLAGS} ${UFLAGS} LDFLAGS = LDLIBS = all: ${PROGRAM} ${PROGRAM}: ${FILES.o} ${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS} prog1.o: ${FILES.h} file1.o: ${FILES.h} file2.o: ${FILES.h} # If it exists, prog1.dSYM is a directory on macOS DEBRIS = a.out core *~ *.dSYM RM_FR = rm -fr clean: ${RM_FR} ${FILES.o} ${PROGRAM} ${DEBRIS}

Pautas

Reglas que deben ser rotas solo por expertos, y solo con una buena razón:

  • Un archivo de encabezado solo contiene declaraciones extern de variables, nunca definiciones de variables static o no calificadas.
  • Para cualquier variable dada, solo un archivo de encabezado lo declara (SPOT - Single Point of Truth).
  • Un archivo de origen nunca contiene declaraciones extern de variables: los archivos de origen siempre incluyen el encabezado (único) que los declara.
  • Para cualquier variable dada, exactamente un archivo fuente define la variable, preferiblemente inicializándola también. (Aunque no hay necesidad de inicializar explícitamente a cero, no hace daño y puede hacer algo bueno, porque solo puede haber una definición inicializada de una variable global en particular en un programa).
  • El archivo de origen que define la variable también incluye el encabezado para garantizar que la definición y la declaración sean coherentes.
  • Una función nunca debería necesitar declarar una variable usando extern .
  • Evite las variables globales siempre que sea posible, use funciones en su lugar.

El código fuente y el texto de esta respuesta están disponibles en mi SOQ ( Questions) en GitHub en el src/so-0143-3204 .

Si no es un programador de C experimentado, podría (y quizás debería) dejar de leer aquí.

No es una buena manera de definir variables globales

Con algunos (de hecho, muchos) compiladores de C, puede salirse con lo que se llama una definición "común" de una variable también. ''Común'', aquí, se refiere a una técnica utilizada en Fortran para compartir variables entre archivos de origen, utilizando un bloque COMÚN (posiblemente denominado). Lo que sucede aquí es que cada uno de una serie de archivos proporciona una definición tentativa de la variable. Mientras que no más de un archivo proporcione una definición inicializada, los distintos archivos terminarán compartiendo una única definición común de la variable:

archivo10.c

#include "prog2.h" int i; /* Do not do this in portable code */ void inc(void) { i++; }

archivo11.c

#include "prog2.h" int i; /* Do not do this in portable code */ void dec(void) { i--; }

file12.c

#include "prog2.h" #include <stdio.h> int i = 9; /* Do not do this in portable code */ void put(void) { printf("i = %d/n", i); }

Esta técnica no se ajusta a la letra del estándar C y la ''regla de una definición''; es un comportamiento oficialmente no definido:

J.2 Comportamiento indefinido

Se usa un identificador con enlace externo, pero en el programa no existe exactamente una definición externa para el identificador, o el identificador no se usa y existen varias definiciones externas para el identificador (6.9).

§6.9 Definiciones externas ¶5

Una definición externa es una declaración externa que también es una definición de una función (que no sea una definición en línea) o un objeto. Si un identificador declarado con enlace externo se usa en una expresión (que no sea parte del operando de un operador _Alignof o _Alignof cuyo resultado sea una constante entera), en algún lugar del programa completo habrá exactamente una definición externa para el identificador; De lo contrario, no habrá más de uno. 161)

161) Por lo tanto, si un identificador declarado con enlace externo no se utiliza en una expresión, no es necesario que exista una definición externa.

Sin embargo, el estándar C también lo incluye en el Anexo J informativo como una de las extensiones comunes .

J.5.11 Múltiples definiciones externas

Puede haber más de una definición externa para el identificador de un objeto, con o sin el uso explícito de la palabra clave extern; Si las definiciones no están de acuerdo, o se inicializa más de una, el comportamiento no está definido (6.9.2).

Debido a que esta técnica no siempre es compatible, es mejor evitar su uso, especialmente si su código necesita ser portátil . Usando esta técnica, también puedes terminar con un tipo de punting involuntario. Si uno de los archivos declaró i como double lugar de int , los enlazadores de tipo no seguro de C probablemente no detectarán la falta de coincidencia. Si está en una máquina con 64 bits int y double , ni siquiera recibirá una advertencia; en una máquina con 32 bits int y 64 bits double , es probable que reciba una advertencia sobre los diferentes tamaños: el enlazador usará el tamaño más grande, exactamente como un programa Fortran tomaría el tamaño más grande de todos los bloques comunes.

Los siguientes dos archivos completan la fuente para prog2 :

prog2.h

extern void dec(void); extern void put(void); extern void inc(void);

prog2.c

#include "prog2.h" #include <stdio.h> int main(void) { inc(); put(); dec(); put(); dec(); put(); }

  • prog2 utiliza prog2.c , file10.c , file11.c , file12.c , prog2.h .

Advertencia

Como se indica en los comentarios aquí, y como se indica en mi respuesta a una question similar, el uso de múltiples definiciones para una variable global conduce a un comportamiento indefinido (J.2; §6.9), que es la forma estándar de decir "cualquier cosa podría pasar". Una de las cosas que puede suceder es que el programa se comporte como usted espera; y J.5.11 dice, aproximadamente, "podrías tener suerte más a menudo de lo que mereces". Pero un programa que se basa en múltiples definiciones de una variable externa, con o sin la palabra clave explícita "extern", no es un programa estrictamente conforme y no se garantiza que funcione en todas partes. Equivalente: contiene un error que puede o no mostrarse.

Violando las pautas

Hay, por supuesto, muchas formas en que se pueden romper estas pautas. Ocasionalmente, puede haber una buena razón para romper las pautas, pero tales ocasiones son extremadamente inusuales.

faulty_header.h

int some_var; /* Do not do this in a header!!! */

Nota 1: si el encabezado define la variable sin la palabra clave extern , entonces cada archivo que incluye el encabezado crea una definición provisional de la variable. Como se señaló anteriormente, esto funcionará a menudo, pero el estándar C no garantiza que funcionará.

broken_header.h

int some_var = 13; /* Only one source file in a program can use this */

Nota 2: si el encabezado define e inicializa la variable, entonces solo un archivo de origen en un programa determinado puede usar el encabezado. Dado que los encabezados son principalmente para compartir información, es un poco tonto crear uno que solo pueda usarse una vez.

raramente_correcto.h

static int hidden_global = 3; /* Each source file gets its own copy */

Nota 3: si el encabezado define una variable estática (con o sin inicialización), entonces cada archivo fuente termina con su propia versión privada de la variable ''global''.

Si la variable es en realidad una matriz compleja, por ejemplo, esto puede llevar a una duplicación extrema de código. Puede, muy ocasionalmente, ser una forma sensata de lograr algún efecto, pero eso es muy inusual.

Resumen

Usa la técnica de cabecera que mostré primero. Funciona de forma fiable y en todas partes. Tenga en cuenta, en particular, que el encabezado que declara la global_variable se incluye en cada archivo que lo usa, incluido el que lo define. Esto asegura que todo sea auto-consistente.

Preocupaciones similares surgen con la declaración y definición de funciones, se aplican reglas análogas Pero la pregunta era específicamente sobre las variables, por lo que he mantenido la respuesta solo a las variables.

Fin de la respuesta original

Si no eres un programador de C experimentado, probablemente deberías dejar de leer aquí.

Adición mayor tardía

Evitar la duplicación de código

Una preocupación que a veces (y legítimamente) se plantea sobre el mecanismo de ''declaraciones en encabezados, definiciones en origen'' descrito aquí es que hay dos archivos que deben mantenerse sincronizados: el encabezado y el origen. Por lo general, se continúa con una observación de que se puede usar una macro para que el encabezado cumpla una doble función: normalmente declara las variables, pero cuando se establece una macro específica antes de incluir el encabezado, define las variables en su lugar.

Otra preocupación puede ser que las variables deben definirse en cada uno de una serie de ''programas principales''. Esto es normalmente una preocupación espuria; simplemente puede introducir un archivo fuente en C para definir las variables y vincular el archivo objeto generado con cada uno de los programas.

Un esquema típico funciona así, utilizando la variable global original ilustrada en file3.h :

file3a.h

#ifdef DEFINE_VARIABLES #define EXTERN /* nothing */ #else #define EXTERN extern #endif /* DEFINE_VARIABLES */ EXTERN int global_variable;

archivo1a.c

#define DEFINE_VARIABLES #include "file3a.h" /* Variable defined - but not initialized */ #include "prog3.h" int increment(void) { return global_variable++; }

file2a.c

#include "file3a.h" #include "prog3.h" #include <stdio.h> void use_it(void) { printf("Global variable: %d/n", global_variable++); }

Los siguientes dos archivos completan la fuente para prog3 :

prog3.h

extern void use_it(void); extern int increment(void);

prog3.c

#include "file3a.h" #include "prog3.h" #include <stdio.h> int main(void) { use_it(); global_variable += 19; use_it(); printf("Increment: %d/n", increment()); return 0; }

  • prog3 utiliza prog3.c , file1a.c , file2a.c , file3a.h , prog3.h .

Inicialización variable

El problema con este esquema como se muestra es que no permite la inicialización de la variable global. Con C99 o C11 y las listas de argumentos variables para macros, también podría definir una macro para admitir la inicialización. (Con C89 y sin soporte para listas de argumentos variables en macros, no hay una manera fácil de manejar inicializadores arbitrariamente largos).

file3b.h

#ifdef DEFINE_VARIABLES #define EXTERN /* nothing */ #define INITIALIZER(...) = __VA_ARGS__ #else #define EXTERN extern #define INITIALIZER(...) /* nothing */ #endif /* DEFINE_VARIABLES */ EXTERN int global_variable INITIALIZER(37); EXTERN struct { int a; int b; } oddball_struct INITIALIZER({ 41, 43 });

Invierta el contenido de los bloques #if y #if error identificado por Denis Kniazhev

archivo1b.c

#define DEFINE_VARIABLES #include "file3b.h" /* Variables now defined and initialized */ #include "prog4.h" int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file2b.c

#include "file3b.h" #include "prog4.h" #include <stdio.h> void use_them(void) { printf("Global variable: %d/n", global_variable++); oddball_struct.a += global_variable; oddball_struct.b -= global_variable / 2; }

Claramente, el código para la estructura extraña no es lo que normalmente se escribiría, pero ilustra el punto. El primer argumento de la segunda invocación de INITIALIZER es { 41 y el argumento restante (el singular en este ejemplo) es 43 } . Sin C99 o soporte similar para las listas de argumentos variables para macros, los inicializadores que necesitan contener comas son muy problemáticos.

file3b.h incluyó el file3b.h encabezado correcto file3b.h (en lugar de fileba.h ) por Denis Kniazhev

Los siguientes dos archivos completan la fuente para prog4 :

prog4.h

extern int increment(void); extern int oddball_value(void); extern void use_them(void);

prog4.c

#include "file3b.h" #include "prog4.h" #include <stdio.h> int main(void) { use_them(); global_variable += 19; use_them(); printf("Increment: %d/n", increment()); printf("Oddball: %d/n", oddball_value()); return 0; }

  • prog4 utiliza prog4.c , file1b.c , file2b.c , prog4.h , file3b.h .

Guardias de cabecera

Cualquier encabezado debe estar protegido contra la reinclusión, de modo que las definiciones de tipo (enumeración, estructura o tipo de unión, o definiciones de tipo en general) no causen problemas. La técnica estándar es envolver el cuerpo del encabezado en un protector de encabezado como:

#ifndef FILE3B_H_INCLUDED #define FILE3B_H_INCLUDED ...contents of header... #endif /* FILE3B_H_INCLUDED */

El encabezado puede ser incluido dos veces indirectamente. Por ejemplo, si file4b.h incluye file3b.h para una definición de tipo que no se muestra, y file1b.c necesita usar tanto el encabezado file4b.h file3b.h , entonces tiene algunos problemas más difíciles de resolver. Claramente, puede revisar la lista de encabezados para incluir solo file4b.h . Sin embargo, es posible que no esté al tanto de las dependencias internas, y el código debería, idealmente, continuar funcionando.

Además, comienza a complicarse porque podría incluir file4b.h antes de incluir file3b.h para generar las definiciones, pero las file3b.h encabezado normales en file3b.h evitarían que se file3b.h a file3b.h el encabezado.

Por lo tanto, debe incluir el cuerpo de file3b.h como máximo una vez para las declaraciones, y como máximo una vez para las definiciones, pero puede que necesite ambos en una sola unidad de traducción (TU, una combinación de un archivo fuente y los encabezados que usa) .

Inclusión múltiple con definiciones de variables.

Sin embargo, se puede hacer sujeto a una restricción no demasiado irrazonable. Vamos a introducir un nuevo conjunto de nombres de archivos:

  • external.h para las definiciones de macros EXTERN, etc.
  • file1c.h para definir tipos (en particular, struct oddball , el tipo de oddball_struct ).
  • file2c.h para definir o declarar las variables globales.
  • file3c.c que define las variables globales.
  • file4c.c que simplemente usa las variables globales.
  • file5c.c que muestra que puede declarar y luego definir las variables globales.
  • file6c.c que muestra que puede definir y luego (intentar) declarar las variables globales.

En estos ejemplos, file5c.c y file6c.c incluyen directamente el encabezado file2c.h varias veces, pero esa es la forma más sencilla de demostrar que el mecanismo funciona. Esto significa que si el encabezado se incluyó indirectamente dos veces, también sería seguro.

Las restricciones para que esto funcione son:

  1. El encabezado que define o declara las variables globales puede que no defina ningún tipo.
  2. Inmediatamente antes de incluir un encabezado que debe definir variables, debe definir la macro DEFINE_VARIABLES.
  3. El encabezado que define o declara las variables tiene contenidos estilizados.

externo.h

/* ** This header must not contain header guards (like <assert.h> must not). ** Each time it is invoked, it redefines the macros EXTERN, INITIALIZE ** based on whether macro DEFINE_VARIABLES is currently defined. */ #undef EXTERN #undef INITIALIZE #ifdef DEFINE_VARIABLES #define EXTERN /* nothing */ #define INITIALIZE(...) = __VA_ARGS__ #else #define EXTERN extern #define INITIALIZE(...) /* nothing */ #endif /* DEFINE_VARIABLES */

archivo1c.h

#ifndef FILE1C_H_INCLUDED #define FILE1C_H_INCLUDED struct oddball { int a; int b; }; extern void use_them(void); extern int increment(void); extern int oddball_value(void); #endif /* FILE1C_H_INCLUDED */

archivo2c.h

/* Standard prologue */ #if defined(DEFINE_VARIABLES) && !defined(FILE2C_H_DEFINITIONS) #undef FILE2C_H_INCLUDED #endif #ifndef FILE2C_H_INCLUDED #define FILE2C_H_INCLUDED #include "external.h" /* Support macros EXTERN, INITIALIZE */ #include "file1c.h" /* Type definition for struct oddball */ #if !defined(DEFINE_VARIABLES) || !defined(FILE2C_H_DEFINITIONS) /* Global variable declarations / definitions */ EXTERN int global_variable INITIALIZE(37); EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 }); #endif /* !DEFINE_VARIABLES || !FILE2C_H_DEFINITIONS */ /* Standard epilogue */ #ifdef DEFINE_VARIABLES #define FILE2C_H_DEFINITIONS #endif /* DEFINE_VARIABLES */ #endif /* FILE2C_H_INCLUDED */

file3c.c

#define DEFINE_VARIABLES #include "file2c.h" /* Variables now defined and initialized */ int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file4c.c

#include "file2c.h" #include <stdio.h> void use_them(void) { printf("Global variable: %d/n", global_variable++); oddball_struct.a += global_variable; oddball_struct.b -= global_variable / 2; }

file5c.c

#include "file2c.h" /* Declare variables */ #define DEFINE_VARIABLES #include "file2c.h" /* Variables now defined and initialized */ int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

archivo6c.c

#define DEFINE_VARIABLES #include "file2c.h" /* Variables now defined and initialized */ #include "file2c.h" /* Declare variables */ int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

El siguiente archivo fuente completa la fuente (proporciona un programa principal) para prog5 , prog6 y prog7 :

prog5.c

#include "file2c.h" #include <stdio.h> int main(void) { use_them(); global_variable += 19; use_them(); printf("Increment: %d/n", increment()); printf("Oddball: %d/n", oddball_value()); return 0; }

  • prog5 utiliza prog5.c , file3c.c , file4c.c , file1c.h , file2c.h , external.h .
  • prog6 utiliza prog5.c , file5c.c , file4c.c , file1c.h , file2c.h , external.h .
  • prog7 utiliza prog5.c , file6c.c , file4c.c , file1c.h , file2c.h , external.h .

Este esquema evita la mayoría de los problemas. Solo se encuentra con un problema si otro encabezado (por ejemplo, file2c.h ) incluye un encabezado que define variables (como file7c.h ) que define variables. No hay una manera fácil de evitar eso que no sea "no hacerlo".

Puede solucionar parcialmente el problema revisando file2c.h en file2d.h :

file2d.h

/* Standard prologue */ #if defined(DEFINE_VARIABLES) && !defined(FILE2D_H_DEFINITIONS) #undef FILE2D_H_INCLUDED #endif #ifndef FILE2D_H_INCLUDED #define FILE2D_H_INCLUDED #include "external.h" /* Support macros EXTERN, INITIALIZE */ #include "file1c.h" /* Type definition for struct oddball */ #if !defined(DEFINE_VARIABLES) || !defined(FILE2D_H_DEFINITIONS) /* Global variable declarations / definitions */ EXTERN int global_variable INITIALIZE(37); EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 }); #endif /* !DEFINE_VARIABLES || !FILE2D_H_DEFINITIONS */ /* Standard epilogue */ #ifdef DEFINE_VARIABLES #define FILE2D_H_DEFINITIONS #undef DEFINE_VARIABLES #endif /* DEFINE_VARIABLES */ #endif /* FILE2D_H_INCLUDED */

El problema se convierte en ''¿debería incluir el encabezado #undef DEFINE_VARIABLES ?'' Si omites eso del encabezado y envuelves cualquier invocación de definición con #define y #undef :

#define DEFINE_VARIABLES #include "file2c.h" #undef DEFINE_VARIABLES

en el código fuente (por lo tanto, los encabezados nunca alteran el valor de DEFINE_VARIABLES ), entonces debería estar limpio. Es solo una molestia tener que recordar escribir la línea extra. Una alternativa podría ser:

#define HEADER_DEFINING_VARIABLES "file2c.h" #include "externdef.h"

externdef.h

/* ** This header must not contain header guards (like <assert.h> must not). ** Each time it is included, the macro HEADER_DEFINING_VARIABLES should ** be defined with the name (in quotes - or possibly angle brackets) of ** the header to be included that defines variables when the macro ** DEFINE_VARIABLES is defined. See also: external.h (which uses ** DEFINE_VARIABLES and defines macros EXTERN and INITIALIZE ** appropriately). ** ** #define HEADER_DEFINING_VARIABLES "file2c.h" ** #include "externdef.h" */ #if defined(HEADER_DEFINING_VARIABLES) #define DEFINE_VARIABLES #include HEADER_DEFINING_VARIABLES #undef DEFINE_VARIABLES #undef HEADER_DEFINING_VARIABLES #endif /* HEADER_DEFINING_VARIABLES */

Esto se está volviendo un poco complicado, pero parece ser seguro (usando file2d.h , sin #undef DEFINE_VARIABLES en file2d.h ).

archivo7c.c

/* Declare variables */ #include "file2d.h" /* Define variables */ #define HEADER_DEFINING_VARIABLES "file2d.h" #include "externdef.h" /* Declare variables - again */ #include "file2d.h" /* Define variables - again */ #define HEADER_DEFINING_VARIABLES "file2d.h" #include "externdef.h" int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

archivo8c.h

/* Standard prologue */ #if defined(DEFINE_VARIABLES) && !defined(FILE8C_H_DEFINITIONS) #undef FILE8C_H_INCLUDED #endif #ifndef FILE8C_H_INCLUDED #define FILE8C_H_INCLUDED #include "external.h" /* Support macros EXTERN, INITIALIZE */ #include "file2d.h" /* struct oddball */ #if !defined(DEFINE_VARIABLES) || !defined(FILE8C_H_DEFINITIONS) /* Global variable declarations / definitions */ EXTERN struct oddball another INITIALIZE({ 14, 34 }); #endif /* !DEFINE_VARIABLES || !FILE8C_H_DEFINITIONS */ /* Standard epilogue */ #ifdef DEFINE_VARIABLES #define FILE8C_H_DEFINITIONS #endif /* DEFINE_VARIABLES */ #endif /* FILE8C_H_INCLUDED */

file8c.c

/* Define variables */ #define HEADER_DEFINING_VARIABLES "file2d.h" #include "externdef.h" /* Define variables */ #define HEADER_DEFINING_VARIABLES "file8c.h" #include "externdef.h" int increment(void) { return global_variable++; } int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

Los siguientes dos archivos completan la fuente para prog8 y prog9 :

prog8.c

#include "file2d.h" #include <stdio.h> int main(void) { use_them(); global_variable += 19; use_them(); printf("Increment: %d/n", increment()); printf("Oddball: %d/n", oddball_value()); return 0; }

archivo9c.c

#include "file2d.h" #include <stdio.h> void use_them(void) { printf("Global variable: %d/n", global_variable++); oddball_struct.a += global_variable; oddball_struct.b -= global_variable / 2; }

  • prog8 utiliza prog8.c , file7c.c , file9c.c .
  • prog9 utiliza prog8.c , file8c.c , file9c.c .

Sin embargo, es poco probable que ocurran problemas en la práctica, especialmente si sigue los consejos estándar para

Evitar variables globales.

¿Esta exposición se pierde algo?

Confesión : El esquema de ''evitar código duplicado'' descrito aquí se desarrolló porque el problema afecta a algunos códigos en los que trabajo (pero no tengo), y es una preocupación constante con el esquema descrito en la primera parte de la respuesta. Sin embargo, el esquema original le deja solo dos lugares para modificar para mantener sincronizadas las definiciones de variables y las declaraciones, lo que es un gran paso adelante en comparación con declaraciones de variables externas dispersas por todo el código base (lo que realmente importa cuando hay miles de archivos en total) . Sin embargo, el código en los archivos con los nombres fileNc.[ch] (más external.h y externdef.h ) muestra que se puede hacer que funcione. Claramente, no sería difícil crear un script generador de encabezado para darle la plantilla estandarizada para una variable que define y declara el archivo de encabezado.

NB Estos son programas de juguete con un código apenas suficiente para que sean ligeramente interesantes. Hay repetición dentro de los ejemplos que podrían eliminarse, pero no es para simplificar la explicación pedagógica. (Por ejemplo: la diferencia entre prog5.c y prog8.c es el nombre de uno de los encabezados incluidos. Sería posible reorganizar el código para que la función main() no se repitiera, pero ocultaría más de lo que reveló.)


En C, una variable dentro de un archivo, por ejemplo, example.c tiene un alcance local.El compilador espera que la variable tenga su definición dentro del mismo archivo example.c y cuando no encuentre la misma, generará un error. Por otra parte, una función tiene un alcance global predeterminado. Por lo tanto, no tiene que mencionar explícitamente al compilador "look dude ... aquí puede encontrar la definición de esta función". Para una función que incluya el archivo que contiene su declaración es suficiente (el archivo al que realmente llama un archivo de encabezado). Por ejemplo, considere los siguientes 2 archivos:
example.c

#include<stdio.h> extern int a; main(){ printf("The value of a is <%d>/n",a); }

ejemplo1.c

int a = 5;

Ahora cuando compiles los dos archivos juntos, usa los siguientes comandos:

paso 1) cc -o ex example.c example1.c paso 2) ./ ex

Obtienes la siguiente salida: El valor de a es <5>


Extern es la palabra clave que utiliza para declarar que la variable reside en otra unidad de traducción.

Por lo tanto, puede decidir utilizar una variable en una unidad de traducción y luego acceder a ella desde otra, luego en la segunda la declara como externa y el símbolo será resuelto por el enlazador.

Si no lo declara como externo, obtendrá 2 variables con el mismo nombre pero no relacionadas en absoluto, y un error de varias definiciones de la variable.


La interpretación correcta de extern es que le dices algo al compilador. Le dice al compilador que, a pesar de no estar presente en este momento, la variable declarada de alguna manera será encontrada por el enlazador (típicamente en otro objeto (archivo)). El enlazador será el afortunado que encuentre todo y lo arme, tengas o no algunas declaraciones externas.


Me gusta pensar en una variable externa como una promesa que le haces al compilador.

Cuando se encuentra con un externo, el compilador solo puede averiguar su tipo, no dónde "vive", por lo que no puede resolver la referencia.

Usted está diciendo: "Confíe en mí. En el momento del enlace, esta referencia se podrá resolver".


Una variable extern es una declaración (gracias a sbi por la corrección) de una variable que se define en otra unidad de traducción. Eso significa que el almacenamiento para la variable se asigna en otro archivo.

Digamos que tienes dos .c test1.c y test2.c . Si define una variable global int test1_var; en test1.c y te gustaría acceder a esta variable en test2.c tienes que usar extern int test1_var; en test2.c .

Muestra completa:

$ cat test1.c int test1_var = 5; $ cat test2.c #include <stdio.h> extern int test1_var; int main(void) { printf("test1_var = %d/n", test1_var); return 0; } $ gcc test1.c test2.c -o test $ ./test test1_var = 5


extern le dice al compilador que confíe en usted que la memoria para esta variable está declarada en otro lugar, por lo que no intenta asignar / verificar la memoria.

Por lo tanto, puede compilar un archivo que tenga referencia a un externo, pero no puede vincularlo si esa memoria no está declarada en algún lugar.

Útil para bibliotecas y variables globales, pero peligroso porque el enlazador no escribe check.


extern simplemente significa que una variable se define en otro lugar (por ejemplo, en otro archivo).


externpermite que un módulo de su programa acceda a una variable global o función declarada en otro módulo de su programa. Por lo general, tiene variables externas declaradas en los archivos de cabecera.

Si no desea que un programa acceda a sus variables o funciones, use staticque le dice al compilador que esta variable o función no se puede usar fuera de este módulo.


externse utiliza para que un first.carchivo pueda tener acceso completo a un parámetro global en otro second.carchivo.

Se externpuede declarar en el first.carchivo o en cualquiera de los archivos de encabezado first.cincluidos.


La palabra clave externa se usa con la variable para su identificación como variable global.

También representa que puede usar la variable declarada usando una palabra clave externa en cualquier archivo, aunque se declara / define en otro archivo.


Implementación GCC ELF Linux

main.c :

#include <stdio.h> int not_extern_int = 1; extern int extern_int; void main() { printf("%d/n", not_extern_int); printf("%d/n", extern_int); }

Compilar y descompilar:

gcc -c main.c readelf -s main.o

La salida contiene:

Num: Value Size Type Bind Vis Ndx Name 9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 not_extern_int 12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND extern_int

El capítulo de la "Tabla de símbolos" de la especificación ELF ABI de System V explica:

SHN_UNDEF Este índice de tabla de sección significa que el símbolo no está definido. Cuando el editor de enlaces combina este archivo de objeto con otro que define el símbolo indicado, las referencias de este archivo al símbolo se vincularán a la definición real.

que es básicamente el comportamiento que el estándar C le da a las externvariables.

A partir de ahora, es tarea del vinculador hacer el programa final, pero la externinformación ya se ha extraído del código fuente al archivo de objeto.

Probado en GCC 4.8.


Con xc8, debe tener cuidado al declarar una variable como el mismo tipo en cada archivo, ya que podría, erróneamente, declarar algo inten un archivo y chardecir en otro. Esto podría llevar a la corrupción de variables.

Este problema se resolvió con elegancia en un foro de microchips hace unos 15 años / * Vea "http: www.htsoft.com" / / "forum / all / showflat.php / Cat / 0 / Number / 18766 / an / 0 / page / 0 # 18766 "

Pero este enlace parece que ya no funciona ...

Así que trataré de explicarlo rápidamente; Hacer un archivo llamado global.h.

En ella se declara lo siguiente.

#ifdef MAIN_C #define GLOBAL /* #warning COMPILING MAIN.C */ #else #define GLOBAL extern #endif GLOBAL unsigned char testing_mode; // example var used in several C files

Ahora en el archivo main.c

#define MAIN_C 1 #include "global.h" #undef MAIN_C

Esto significa que en main.c la variable se declarará como un unsigned char.

Ahora, en otros archivos, simplemente incluyendo global.h, se lo declarará como externo para ese archivo .

extern unsigned char testing_mode;

Pero será declarado correctamente como un unsigned char.

El viejo post del foro probablemente explicaba esto un poco más claramente. Pero este es un potencial real gotchacuando se usa un compilador que le permite declarar una variable en un archivo y luego declararla externa como un tipo diferente en otro. Los problemas asociados con eso son que si usted declara testing_mode como un int en otro archivo, pensaría que era una var de 16 bits y sobrescribiría otra parte del ram, corrompiendo potencialmente otra variable. Difícil de depurar!


En primer lugar, la externpalabra clave no se utiliza para definir una variable; más bien se utiliza para declarar una variable. Puedo decir que externes una clase de almacenamiento, no un tipo de datos.

externse utiliza para que otros archivos C o componentes externos sepan que esta variable ya está definida en algún lugar. Ejemplo: si está creando una biblioteca, no es necesario definir obligatoriamente la variable global en algún lugar de la biblioteca. La biblioteca se compilará directamente, pero al vincular el archivo, verifica la definición.