¿Cómo encuentran los compiladores de c ++ una variable externa?

15

Compilo este programa por g ++ y clang ++. Hay una diferencia:
g ++ imprime 1, pero clang ++ imprime 2.
Parece que
g ++: la variable externa se define en el ámbito más corto.
clang ++: la variable externa se define en el ámbito global más corto.

¿La especificación C ++ tiene alguna especificación al respecto?

main.cpp

#include <iostream>
static int i;
static int *p = &i;

int main() {
  int i;
  {
    extern int i;
    i = 1;
    *p = 2;
    std::cout << i << std::endl;
  }
}

other.cpp

int i;

versión: g ++: 7.4.0 / clang ++:
compilación 10.0.0 : $ (CXX) main.cpp other.cpp -o extern.exe

eddie kuo
fuente
44
El compilador no hace nada con extern excepto marcarlos como variables que tienen referencias externas, el enlazador es lo que intenta resolver los enlaces entre todos los archivos de objetos compilados.
SPlatten
Una excelente (si extraña) pregunta! Jugando con su código MSVCy clang-cl(ambos dan 2), parece que extern int iambos lo ignoran por completo: incluso si no enlace en el other.cpparchivo, el programa se construye y se ejecuta.
Adrian Mole
1
@SPlatten Presumiblemente, como el enlazador no necesita 'resolver' la referencia i, no lo intenta.
Adrian Mole
3
El error de GCC suspendido antiguo relacionado se puede encontrar aquí y el error de Clang abierto correspondiente aquí
nogal

Respuestas:

11

[basic.link/7] debería ser la parte relevante de la Norma. En el borrador actual, dice:

El nombre de una función declarada en el alcance del bloque y el nombre de una variable declarada por una externdeclaración del alcance del bloque tienen vinculación. Si dicha declaración se adjunta a un módulo con nombre, el programa está mal formado. Si hay una declaración visible de una entidad con vinculación, ignorando las entidades declaradas fuera del alcance del espacio de nombres más cercano, de modo que la declaración del alcance del bloque sería una redeclaración (posiblemente mal formada) si las dos declaraciones aparecieran en la misma región declarativa, el La declaración de alcance de bloque declara esa misma entidad y recibe el enlace de la declaración anterior. Si hay más de una entidad coincidente, el programa está mal formado. De lo contrario, si no se encuentra una entidad coincidente, la entidad de alcance de bloque recibe un enlace externo.Si, dentro de una unidad de traducción, se declara la misma entidad con enlaces internos y externos, el programa está mal formado.

Tenga en cuenta que el siguiente ejemplo coincide casi exactamente con su caso:

static void f();
extern "C" void h();
static int i = 0;               // #1
void g() {
  extern void f();              // internal linkage
  extern void h();              // C language linkage
  int i;                        // #2: i has no linkage
  {
    extern void f();            // internal linkage
    extern int i;               // #3: external linkage, ill-formed
  }
}

Entonces, el programa debe estar mal formado. La explicación está debajo del ejemplo:

Sin la declaración en la línea # 2, la declaración en la línea # 3 se vincularía con la declaración en la línea # 1. Sin embargo, debido a que la declaración con vinculación interna está oculta, # 3 recibe vinculación externa, lo que hace que el programa esté mal formado.

Daniel Langr
fuente
El programa en el ejemplo está mal formado porque no hay i con enlace externo definido en ningún lado. Este no es el caso con el ejemplo de OP.
n. 'pronombres' m.
3
@ n.'pronouns'm. Pero la regla se aplica a una unidad de traducción: si, dentro de una unidad de traducción, la misma entidad se declara con vinculación interna y externa, el programa está mal formado. .
Daniel Langr
2
La respuesta solo se aplica a C ++ 17 y posterior, consulte la resolución del problema 426 de CWG . Me parece que GCC estaba en lo correcto antes de ese cambio.
nogal
OK, parece que estaba leyendo la edición anterior del estándar.
n. 'pronombres' m.