Enlace interno y enlace externo en C

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 printfdefinición se encuentra en stdioel 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:

  1. 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. Consider Feed.cppse 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 animalscódigo.
    Esto nos lleva a concluir que cada unidad de traducción accede a su propia copia de animals. Por eso tenemos animals= 8 para Animals.cpp, animals= 2 para Feed.cppy animals= 10 para Wash.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 tiene fun1()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.

  2. 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 externimplementa 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. Externlos 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 una externvariable 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 variable btiene alcance local en la función foo, aunque es una externvariable. 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, bse considera el alcance de. Tiene alcance local en foo(). Cuando el compilador ve la externdeclaración, confía en que hay una definición de balgún lugar y deja que el enlazador maneje el resto.

    Sin embargo, el mismo compilador revisará la bar()función e intentará encontrar la variable b. Dado que bha sido declarado extern, el compilador aún no le ha dado memoria; todavía no existe. El compilador permitirá que el enlazador encuentre la definición de ben la unidad de traducción, y luego el enlazador asignará bel valor especificado en definición. Sólo entonces bexistirá y se le asignará memoria. Sin embargo, dado que no se proporciona una declaración en tiempo de compilación dentro del alcance de bar(), 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 ben bar(), cuando bse ha declarado en foo()el alcance de . El compilador dejará de compilar y el programa no se pasará al enlazador.

    Podemos arreglar el programa declarándolo bcomo una variable global, moviendo la línea 1 antes foode 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 xy zen ámbito global . Por defecto, ambos tienen enlace externo. Ahora, cuando declaramos ycomo extern, le decimos al compilador que existe un ycon 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 la externpalabra clave y compila el resto del programa. La siguiente línea extern int zno tiene efecto en z, ya que zestá vinculada externamente por defecto cuando la declaramos como una variable global fuera del programa. Cuando encontramos printfla línea, el compilador ve 3 variables, las 3 han sido declaradas antes, y las 3 se usan dentro de sus alcances (en elprintffunció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 xy zprimero. Como son variables globales, están vinculadas externamente por defecto. Luego, el vinculador actualiza el valor de xy zen toda la unidad de traducción como 10 y 5. Si hay referencias a xy zen cualquier otro archivo en la unidad de traducción, se establecen en 10 y 5.

    Ahora, el enlazador vuelve extern int ye intenta encontrar cualquier definición de ydentro de la unidad de traducción. Examina todos los archivos de la unidad de traducción para encontrar la definición de y. Si no encuentra ninguna definición, se generará un error de vinculación. En nuestro programa, hemos dado la definición outside main(), que ya ha sido compilada para nosotros. Por lo tanto, el enlazador encuentra esa definición y la actualiza y.

    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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *