Externo, interno y sin vinculación o por qué esto no funciona?
language-lawyer linkage (3)
Según la norma C:
En el conjunto de unidades de traducción y bibliotecas que constituyen un programa completo, cada declaración de un identificador particular con enlace externo denota el mismo objeto o función. Dentro de una unidad de traducción, cada declaración de un identificador con enlace interno denota el mismo objeto o función. Cada declaración de un identificador sin vínculo denota una entidad única.
En mi ejemplo, tenemos tres declaraciones separadas con cada identificador que tiene un enlace diferente. Entonces, ¿por qué no funciona esto?
static int a; //a_Internal
int main(void) {
int a; //a_Local
{
extern int a; //a_External
}
return 0;
}
Error:
En la función ''principal'': Línea 9: error: variable declarada anteriormente ''estática'' redeclarada ''extern''
¿Por qué el compilador insiste en que estoy redeclarando en lugar de intentar acceder a un objeto externo en otro archivo?
Ejemplo válido de C ++ para referencia:
static void f();
static int i = 0; // #1
void g() {
extern void f(); // internal linkage
int i; // #2 i has no linkage
{
extern void f(); // internal linkage
extern int i; // #3 external linkage
}
}
Tanto Clang como VC parecen estar bien con mi ejemplo de C; solo algunas versiones de GCC (no todas) producen el error mencionado anteriormente.
§6.2.2, 7 dice:
Si, dentro de una unidad de traducción, aparece el mismo identificador con enlaces internos y externos, el comportamiento es indefinido.
Por lo tanto, su programa tiene un comportamiento indefinido .
§6.2.2, 4 dice que
extern int a; //a_External
tiene enlace externo porque la declaración anterior visible en el alcance int a; //a_Local
int a; //a_Local
no tiene vinculación . Pero
static int a; //a_Internal
Declara a
con vinculación interna. Por lo tanto, es indefinido por §6.2.2, 7.
El compilador está dando este error porque dentro del ámbito a_External
, a_Internal
sigue siendo accesible, por lo que está redeclarando a_Internal
de static
a extern
en a_External
debido a la colisión de nombres de a
. Este problema se puede resolver utilizando diferentes nombres de variables, por ejemplo:
static int a1; //a_Internal
int main(void) {
int a2; //a_Local
{
extern int a3; //a_External
}
return 0;
}
El estándar C dice:
En el conjunto de unidades de traducción, cada declaración de un identificador particular con enlace externo denota la misma entidad (objeto o función). Dentro de una unidad de traducción, cada declaración de un identificador con enlace interno denota la misma entidad.
En el conjunto de unidades de traducción no podemos tener varias entidades externas distintas con el mismo nombre, por lo que los tipos de cada declaración que denota esa entidad externa única deberían coincidir. Podemos verificar si los tipos concuerdan con una unidad de traducción, esto se hace en tiempo de compilación. No podemos verificar si los tipos concuerdan entre diferentes unidades de traducción ni en tiempo de compilación ni en tiempo de enlace.
Para un identificador declarado con el especificador de la clase de almacenamiento extern en un ámbito en el que es visible una declaración previa de ese identificador, 31) si la declaración anterior especifica un enlace interno o externo, el enlace del identificador en la declaración posterior es el mismo que El enlace especificado en la declaración anterior. Si no hay una declaración previa visible, o si la declaración anterior no especifica ningún enlace, entonces el identificador tiene un enlace externo.
static int a; //a_Internal
int main(void) {
int a; //No linkage
{
extern int a; //a_External
}
return 0;
}
Aquí la declaración anterior del identificador a no tiene enlace, por lo que extern int a
tiene enlace externo. Significa que tenemos que definir int a en otra unidad de traducción. Sin embargo, GCC decidió rechazar este código con una variable previamente declarada static
redeclarada error ''extern'', probablemente porque tenemos un comportamiento indefinido según el estándar C