A menudo es bastante difícil distinguir entre alcance y vinculación, y las funciones que desempeñan. Este artículo se centra en el alcance y la vinculación, y cómo se utilizan en lenguaje C.
Nota: Todos los programas C se compilaron en GCC 4.9.2 de 64 bits. Además, los términos «identificador» y «nombre» se han utilizado indistintamente en este artículo.
Definiciones
- Alcance: El alcance de un identificador es la parte del programa donde el identificador puede ser directamente accesible. En C, todos los identificadores tienen un alcance léxico (o estático).
- Vinculación: la vinculación describe cómo los nombres pueden o no referirse a la misma entidad en todo el programa o en una sola unidad de traducción.
Lo anterior suena similar a Scope, pero no es así. Para comprender lo que significa lo anterior, profundicemos en el proceso de compilación. - Unidad de traducción: una unidad de traducción es un archivo que contiene código fuente, archivos de encabezado y otras dependencias. Todas estas fuentes se agrupan para formar una sola unidad de traducción que luego puede ser utilizada por el compilador para producir un solo objeto ejecutable. Es importante vincular las fuentes de manera significativa. Por ejemplo, el compilador debe saber que la
printf
definición se encuentra enstdio
el archivo de encabezado.
En C y C++, un programa que consta de varios archivos de código fuente se compila uno a la vez . Hasta el proceso de compilación, una variable puede describirse por su alcance. Solo cuando comienza el proceso de vinculación, entra en juego la propiedad de vinculación. Por lo tanto, el alcance es una propiedad manejada por el compilador, mientras que el enlace es una propiedad manejada por el enlazador.
El Linker vincula los recursos en la etapa de vinculación del proceso de compilación. El Linker es un programa que toma varios archivos de código de máquina como entrada y produce un código de objeto ejecutable. Resuelve símbolos (es decir, obtiene la definición de símbolos como «+», etc.) y organiza los objetos en el espacio de direcciones.
La vinculación es una propiedad que describe cómo el vinculador debe vincular las variables. ¿Debería estar disponible una variable para que la use otro archivo? ¿Se debe usar una variable solo en el archivo declarado? Ambos se deciden por vinculación.
Por lo tanto, la vinculación le permite unir nombres por archivo, el alcance determina la visibilidad de esos nombres.
Hay 2 tipos de enlace:
- Vinculación interna : no se puede acceder a un identificador que implementa una vinculación interna fuera de la unidad de traducción en la que se declara. Cualquier identificador dentro de la unidad puede acceder a un identificador que tenga una vinculación interna. Se implementa con la palabra clave
static
. Un identificador vinculado internamente se almacena en un segmento de RAM inicializado o no inicializado. ( nota:static
también tiene un significado en referencia al alcance, pero eso no se discute aquí).
Algunos ejemplos:Animales.cpp
// C code to illustrate Internal Linkage
#include <stdio.h>
static
int
animals = 8;
const
int
i = 5;
int
call_me(
void
)
{
printf
(
"%d %d"
, i, animals);
}
El código anterior implementa un enlace estático en el identificador
animals
. ConsiderFeed.cpp
se encuentra en la misma unidad de traducción.Feed.cpp
// C code to illustrate Internal Linkage
#include <stdio.h>
int
main()
{
call_me();
animals = 2;
printf
(
"%d"
, animals);
return
0;
}
Al compilar Animals.cpp primero y luego Feed.cpp, obtenemos
Output : 5 8 2
Ahora, considere que Feed.cpp está ubicado en una unidad de traducción diferente. Se compilará y ejecutará como arriba solo si usamos
#include "Animals.cpp"
.
Considere Wash.cpp ubicado en una tercera unidad de traducción.Lavado.cpp
// C code to illustrate Internal Linkage
#include <stdio.h>
#include "animal.cpp" // note that animal is included.
int
main()
{
call_me();
printf
(
"\n having fun washing!"
);
animals = 10;
printf
(
"%d\n"
, animals);
return
0;
}
Al compilar, obtenemos:
Output : 5 8 having fun washing! 10
Hay 3 unidades de traducción (Animales, Alimento, Lavado) que usan
animals
código.
Esto nos lleva a concluir que cada unidad de traducción accede a su propia copia deanimals
. Por eso tenemosanimals
= 8 paraAnimals.cpp
,animals
= 2 paraFeed.cpp
yanimals
= 10 paraWash.cpp
. Un archivo. Este comportamiento consume memoria y disminuye el rendimiento.Otra propiedad del enlace interno es que solo se implementa cuando la variable tiene alcance global y todas las constantes están enlazadas internamente de forma predeterminada.
Uso: como sabemos, una variable vinculada internamente se pasa por copia. Por lo tanto, si un archivo de encabezado tiene una función
fun1()
y el código fuente en el que está incluido también la tienefun1()
pero con una definición diferente, entonces las 2 funciones no chocarán entre sí. Por lo tanto, comúnmente usamos enlaces internos para ocultar las funciones auxiliares locales de la unidad de traducción del alcance global. Por ejemplo, podríamos incluir un archivo de encabezado que contenga un método para leer la entrada del usuario, en un archivo que pueda describir otro método para leer la entrada del usuario. Ambas funciones son independientes entre sí cuando están vinculadas. - Enlace externo: un identificador que implementa el enlace externo es visible para cada unidad de traducción . Los identificadores vinculados externamente se comparten entre las unidades de traducción y se considera que están ubicados en el nivel más externo del programa. En la práctica, esto significa que debe definir un identificador en un lugar que sea visible para todos, de modo que solo tenga una definición visible. Es el enlace predeterminado para variables y funciones de ámbito global. Por lo tanto, todas las instancias de un identificador particular con vinculación externa se refieren al mismo identificador en el programa. La palabra clave
extern
implementa enlaces externos.Cuando usamos la palabra clave
extern
, le decimos al enlazador que busque la definición en otra parte. Por lo tanto, la declaración de un identificador vinculado externamente no ocupa ningún espacio.Extern
los identificadores generalmente se almacenan en un segmento de RAM inicializado/no inicializado o de texto.Consulte Comprensión de la palabra clave externa en C antes de continuar con los siguientes ejemplos.
Es posible utilizar unaextern
variable en un ámbito local. Esto describirá aún más las diferencias entre vinculación y alcance. Considere el siguiente código:// C code to illustrate External Linkage
#include <stdio.h>
void
foo()
{
int
a;
extern
int
b;
// line 1
}
void
bar()
{
int
c;
c = b;
// error
}
int
main()
{
foo();
bar();
}
Error: 'b' was not declared in this scope
Explicación: la variableb
tiene alcance local en la funciónfoo
, aunque es unaextern
variable. Tenga en cuenta que la compilación tiene lugar antes de vincular; es decir, el alcance es un concepto que solo se puede usar durante la fase de compilación. Una vez compilado el programa, no existe el concepto de «alcance de la variable».Durante la compilación,
b
se considera el alcance de. Tiene alcance local enfoo()
. Cuando el compilador ve laextern
declaración, confía en que hay una definición deb
algún lugar y deja que el enlazador maneje el resto.Sin embargo, el mismo compilador revisará la
bar()
función e intentará encontrar la variableb
. Dado queb
ha sido declaradoextern
, el compilador aún no le ha dado memoria; todavía no existe. El compilador permitirá que el enlazador encuentre la definición deb
en la unidad de traducción, y luego el enlazador asignaráb
el valor especificado en definición. Sólo entoncesb
existirá y se le asignará memoria. Sin embargo, dado que no se proporciona una declaración en tiempo de compilación dentro del alcance debar()
, o incluso en el alcance global, el compilador se queja con el error anterior.Dado que es trabajo del compilador asegurarse de que todas las variables se usen dentro de sus alcances, se queja cuando ve
b
enbar()
, cuandob
se ha declarado enfoo()
el alcance de . El compilador dejará de compilar y el programa no se pasará al enlazador.Podemos arreglar el programa declarándolo
b
como una variable global, moviendo la línea 1 antesfoo
de la definición.Veamos otro ejemplo
// C code to illustrate External Linkage
#include <stdio.h>
int
x = 10;
int
z = 5;
int
main()
{
extern
int
y;
// line 2
extern
int
z;
printf
(
"%d %d %d"
, x, y, z);
}
int
y = 2;
Output: 10 2 5
Podemos explicar la salida observando el comportamiento del enlace externo. Definimos 2 variables
x
yz
en ámbito global . Por defecto, ambos tienen enlace externo. Ahora, cuando declaramosy
comoextern
, le decimos al compilador que existe uny
con alguna definición dentro de la misma unidad de traducción. Tenga en cuenta que esto es durante la fase de tiempo de compilación, donde el compilador confía en laextern
palabra clave y compila el resto del programa. La siguiente líneaextern int z
no tiene efecto enz
, ya quez
está vinculada externamente por defecto cuando la declaramos como una variable global fuera del programa. Cuando encontramosprintf
la línea, el compilador ve 3 variables, las 3 han sido declaradas antes, y las 3 se usan dentro de sus alcances (en elprintf
función). El programa por lo tanto compila con éxito, aunque el compilador no conoce la definición dey
La siguiente fase es la vinculación. El enlazador revisa el código compilado y encuentra
x
yz
primero. Como son variables globales, están vinculadas externamente por defecto. Luego, el vinculador actualiza el valor dex
yz
en toda la unidad de traducción como 10 y 5. Si hay referencias ax
yz
en cualquier otro archivo en la unidad de traducción, se establecen en 10 y 5.Ahora, el enlazador vuelve
extern int y
e intenta encontrar cualquier definición dey
dentro de la unidad de traducción. Examina todos los archivos de la unidad de traducción para encontrar la definición dey
. Si no encuentra ninguna definición, se generará un error de vinculación. En nuestro programa, hemos dado la definición outsidemain()
, que ya ha sido compilada para nosotros. Por lo tanto, el enlazador encuentra esa definición y la actualizay
.Este artículo es una contribución de Simran Dhamija . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
Publicación traducida automáticamente
Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA