2010-09-11 8 views

Respuesta

27

gcc, en el modo C:

globales sin inicializar que no se declaren extern son tratados como símbolos "comunes", no símbolos débiles.

Los símbolos comunes se fusionan en el tiempo de enlace para que todos se refieran al mismo almacenamiento; si más de un objeto intenta inicializar dicho símbolo, obtendrá un error de tiempo de enlace. (Si no se inicializan explícitamente en cualquier lugar, que serán colocados en el BSS, es decir, inicializado a 0.)

gcc, en C++ modo:

No es lo mismo - que no hace el cosa de símbolos comunes Los valores globales "no inicializados" que no están declarados extern se inicializan implícitamente a un valor predeterminado (0 para tipos simples o constructor predeterminado).


En cualquier caso, un símbolo débil permite que un símbolo inicializado a ser anulado por un símbolo no débil inicializado del mismo nombre en tiempo de enlace.


Para ilustrar (concentrándose en el caso C aquí), voy a utilizar 4 variantes de un programa principal, que son todos iguales excepto por la forma en que se declara global:

  1. main_init.c:

    #include <stdio.h> 
    
    int global = 999; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  2. main_uninit.c, whic h omite la inicialización:

    #include <stdio.h> 
    
    int global; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  3. main_uninit_extern.c, que añade el extern palabra clave:

    #include <stdio.h> 
    
    extern int global; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  4. main_weak_init.c, que inicializa global y declara que es un símbolo débil:

    #include <stdio.h> 
    
    int global __attribute__((weak)) = 999; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    

y another_def.c que inicializa el mismo global:

int global = 1234; 

Usando main_uninit.c por sí solo da 0:

$ gcc -o test main_uninit.c && ./test 
0 

pero cuando another_def.c se incluye también, global se inicializa de forma explícita y obtenemos el resultado esperado:

$ gcc -o test main_uninit.c another_def.c && ./test 
1234 

(Nota que este caso falla en su lugar si está usando C++.)

Si tratamos con tanto main_init.c y another.def.c lugar, tenemos 2 inicializaciones de global, que no funcionarán:

$ gcc -o test main_init.c another_def.c && ./test 
/tmp/cc5DQeaz.o:(.data+0x0): multiple definition of `global' 
/tmp/ccgyz6rL.o:(.data+0x0): first defined here 
collect2: ld returned 1 exit status 

main_uninit_extern.c por sí solo no va a funcionar en absoluto - la palabra clave extern hace que el símbolo sea una referencia externa ordinaria en lugar de un símbolo común, por lo que el enlazador se queja:

$ gcc -o test main_uninit_extern.c && ./test 
/tmp/ccqdYUIr.o: In function `main': 
main_uninit_extern.c:(.text+0x12): undefined reference to `global' 
collect2: ld returned 1 exit status 

funciona bien una vez que la inicialización de another_def.c i s incluyó:

$ gcc -o test main_uninit_extern.c another_def.c && ./test 
1234 

Usando main_init_weak.c por sí solo da el valor que inicializa el símbolo débil a (999), ya que no hay nada que anularlo:

$ gcc -o test main_init_weak.c && ./test 
999 

Pero tirando en el otra definición de another_def.c funciona en este caso, porque la definición fuerte allí prevalece sobre la definición débil en main_init_weak.c:

+0

Buena respuesta, gracias. ¿Puedo preguntar si se aplica a C++? –

+0

Gran explicación con ejemplos. ¿Podrías también incluir qué tan débil interactúa con las funciones? – Tomek

+0

@Maciej - ¡buen punto, olvidé que esto fue etiquetado con ambos al escribir mi respuesta originalmente! No, no es lo mismo para C++, así que actualicé mi respuesta en consecuencia. –

3

¿Es esto lo que quiso decir?

weak.c

#include <stdio.h> 

int weak; /* global, weak, zero */ 

int main(void) { 
    printf("weak value is %d.\n", weak); 
    return 0; 
} 

strong.c

int weak = 42; /* global, strong, 42 */ 

ejecución de ejemplo

$ gcc weak.c 
$ ./a.out 
weak value is 0. 
$ gcc weak.c strong.c 
$ ./a.out 
weak value is 42.

El int weak; en weak.c es una declaración, no una definición. O puede decir que es una definición tentativa. La definición real está en strong.c cuando ese archivo objeto está vinculado en el programa final o en weak.c en caso contrario. Esta es una extensión común, una que usa gcc (gracias a Andrey).

+2

No es cierto. 'int weak' en' weak.c' es una definición.Una tentativa, pero definición sin embargo. El programa anterior * infringe * las reglas del lenguaje, pero se admite como una extensión no estándar. – AnT

3

Cualquier definición múltiple de un símbolo global es un comportamiento indefinido, por lo que gcc (o más bien el enlazador binutils de GNU) puede hacer lo que quiera. En la práctica, sigue el comportamiento tradicional para evitar romper el código que se basa en este comportamiento.

+0

¿Quiere decir definición ** múltiple **? – pmg

+0

Sí, arreglándolo. –

9

La pregunta se basa en una premisa incorrecta. Las variables globales no inicializadas no son símbolos débiles.

Aparentemente, la pregunta se refiere a la capacidad de definir el mismo objeto no inicializado con vinculación externa en varias unidades de traducción. Formalmente, no está permitido; es un error tanto en C como en C++.Sin embargo, al menos en C es reconocido por la norma C99 como "extensión común" de la lengua, implementado en muchos compiladores de la vida real

J.5 extensiones comunes

J.5.11 múltiple externa definiciones

puede haber más de un definición externa para el identificador de un objeto , con o sin la explícita uso de la palabra clave extern; si las definiciones no coinciden, o se inicializa más de un , el comportamiento es indefinido (6.9.2).

Tenga en cuenta que, contrariamente a la creencia popular, el lenguaje C prohíbe explícitamente la introducción de múltiples definiciones de entidades con enlaces externos en el programa, al igual que hace C++.

6,9 definiciones externas

una definición externa es un declaración externa que es también una definición de una función (que no sea definiciones en línea) o un objeto. Si un identificador declarado con enlace externo se utiliza en una expresión (distinta de como parte del operando de un operador sizeof cuyo resultado es una constante de número entero), en alguna parte de todo el programa habrá exactamente uno definición externa para el identificador ; de lo contrario, debe haber no más de un.

Sin embargo, la extensión que permite esto ha sido bastante popular con muchos compiladores de C, de los cuales GCC resulta ser uno.

+1

La función del enlazador de soporte es necesaria para implementar los bloques comunes de FORTRAN. Notará que GCC incluye un compilador FORTRAN en la colección. Dado que las bibliotecas de soporte se implementan en C, no es sorprendente que haya una pequeña cantidad de fugas inofensivas desde el enlazador. – RBerteig

+0

¿Puedes dar un ejemplo de definición externa y declaración externa? – Thomson

+0

No es solo FORTRAN, las funciones en línea de C++ y las plantillas implícitamente instanciadas también son débiles. – MSalters

Cuestiones relacionadas