Namespaces
Variants

Lookup and name spaces

From cppreference.net

Cuando se encuentra un identificador en un programa C, se realiza una búsqueda para localizar la declaración que introdujo ese identificador y que está actualmente en ámbito . C permite que más de una declaración para el mismo identificador esté en ámbito simultáneamente si estos identificadores pertenecen a diferentes categorías, llamadas espacios de nombres :

1) Espacio de nombres de etiquetas: todos los identificadores declarados como labels .
2) Nombres de etiquetas: todos los identificadores declarados como nombres de structs , unions y tipos enumerados . Nótese que los tres tipos de etiquetas comparten un mismo espacio de nombres.
3) Nombres de miembros: todos los identificadores declarados como miembros de cualquier struct o union . Cada struct y union introduce su propio espacio de nombres de este tipo.
4) Espacio de nombres de atributos globales: tokens de atributo definidos por el estándar o prefijos de atributo definidos por la implementación.
5) Nombres de atributos no estándar: nombres de atributos que siguen a prefijos de atributo. Cada prefijo de atributo tiene un espacio de nombres separado para los atributos definidos por la implementación que introduce.
(desde C23)
6) Todos los demás identificadores, llamados identificadores ordinarios para distinguirlos de (1-5) (nombres de funciones, nombres de objetos, nombres de typedef, constantes de enumeración).

En el momento de la búsqueda, el espacio de nombres de un identificador se determina por la forma en que se utiliza:

1) el identificador que aparece como operando de una sentencia goto se busca en el espacio de nombres de etiquetas.
2) el identificador que sigue a la palabra clave struct , union , o enum se busca en el espacio de nombres de etiquetas.
3) el identificador que sigue al operador de acceso a miembro o al operador de acceso a miembro a través de puntero se busca en el espacio de nombres de los miembros del tipo determinado por el operando izquierdo del operador de acceso a miembro.
4) identificador que aparece directamente en un especificador de atributo ( [ [ ... ] ] ) se busca en el espacio de nombres global de atributos.
5) identificador que sigue al token :: después de un prefijo de atributo se busca en el espacio de nombres introducido por el prefijo de atributo.
(desde C23)
6) todos los demás identificadores se buscan en el espacio de nombres de identificadores ordinarios.

Contenidos

Notas

Los nombres de las macros no forman parte de ningún espacio de nombres porque son reemplazados por el preprocesador antes del análisis semántico.

Es una práctica común inyectar los nombres de struct/union/enum en el espacio de nombres de los identificadores ordinarios usando una typedef declaration:

struct A { };       // introduce el nombre A en el espacio de nombres de etiquetas
typedef struct A A; // primero, la búsqueda de A después de "struct" encuentra uno en el espacio de nombres de etiquetas
                    // luego introduce el nombre A en el espacio de nombres ordinario
struct A* p;        // OK, este A se busca en el espacio de nombres de etiquetas
A* q;               // OK, este A se busca en el espacio de nombres ordinario

Un ejemplo conocido del mismo identificador utilizado en dos espacios de nombres es el identificador stat del encabezado POSIX sys/stat.h . Este nombra una función cuando se utiliza como identificador ordinario y indica una struct cuando se utiliza como etiqueta.

A diferencia de C++, las constantes de enumeración no son miembros de struct, y su espacio de nombres es el espacio de nombres de identificadores ordinarios, y dado que no hay ámbito de struct en C, su ámbito es el ámbito en el que aparece la declaración del struct:

struct tagged_union {
   enum {INT, FLOAT, STRING} type;
   union {
      int integer;
      float floating_point;
      char *string;
   };
} tu;
tu.type = INT; // Correcto en C, error en C++

Si un atributo estándar, un prefijo de atributo o un nombre de atributo no estándar no es compatible, el atributo inválido en sí se ignora sin causar un error.

(since C23)

Ejemplo

void foo (void) { return; } // espacio de nombres ordinario, ámbito de archivo
struct foo {      // espacio de nombres de etiquetas, ámbito de archivo
    int foo;      // espacio de nombres de miembros para este struct foo, ámbito de archivo
    enum bar {    // espacio de nombres de etiquetas, ámbito de archivo
        RED       // espacio de nombres ordinario, ámbito de archivo
    } bar;        // espacio de nombres de miembros para este struct foo, ámbito de archivo
    struct foo* p; // OK: usa el nombre "foo" de ámbito de etiqueta/archivo
};
enum bar x; // OK: usa bar de ámbito de etiqueta/archivo
// int foo; // Error: el espacio de nombres ordinario foo ya está en ámbito
//union foo { int a, b; }; // Error: el espacio de nombres de etiquetas foo está en ámbito
int main(void)
{
    goto foo; // OK usa "foo" del espacio de nombres de etiquetas/ámbito de función
    struct foo { // espacio de nombres de etiquetas, ámbito de bloque (oculta el ámbito de archivo)
       enum bar x; // OK, usa "bar" del espacio de nombres de etiquetas/ámbito de archivo
    };
    typedef struct foo foo; // OK: usa foo del espacio de nombres de etiquetas/ámbito de bloque
                            // define foo ordinario de ámbito de bloque (oculta el ámbito de archivo)
    (foo){.x=RED}; // usa foo ordinario/ámbito-de-bloque y RED ordinario/ámbito-de-archivo
foo:; // espacio de nombres de etiquetas, ámbito de función
}

Referencias

  • Estándar C17 (ISO/IEC 9899:2018):
  • 6.2.3 Espacios de nombres de identificadores (p: 29-30)
  • Estándar C11 (ISO/IEC 9899:2011):
  • 6.2.3 Espacios de nombres de identificadores (p: 37)
  • Estándar C99 (ISO/IEC 9899:1999):
  • 6.2.3 Espacios de nombres de identificadores (p: 31)
  • Estándar C89/C90 (ISO/IEC 9899:1990):
  • 3.1.2.3 Espacios de nombres de identificadores

Véase también

Documentación de C++ para Name lookup