¿Qué significa doble guión bajo (__const) en C?
extern int ether_hostton (__const char *__hostname, struct ether_addr *__addr)
__THROW;
Encontré la definición de función anterior en /usr/include/netinet/ether.h en una máquina Linux.
¿Alguien puede explicar qué significan los guiones bajos dobles delante de const (palabra clave), addr (identificador) y, por último, __THROW.
En C, los símbolos que comienzan con un guión bajo seguido de una letra mayúscula u otro guión bajo están reservados para la implementación. Usted, como usuario de C, no debe crear ningún símbolo que comience con las secuencias reservadas. En C++, la restricción es más estricta; El usuario no puede crear un símbolo que contenga un guión bajo doble.
Dado:
extern int ether_hostton (__const char *__hostname, struct ether_addr *__addr)
__THROW;
La __const
notación está ahí para permitir la posibilidad (algo improbable) de que un compilador con el que se utiliza este código admita notaciones de prototipo pero no tenga una comprensión correcta de la palabra clave estándar C89 const
. Las autoconf
macros aún pueden verificar si el compilador tiene soporte funcional para const
; este código podría usarse con un compilador defectuoso que no tenga ese soporte.
El uso de __hostname
y __addr
es una medida de protección para usted, el usuario del encabezado. Si compila con GCC y la -Wshadow
opción, el compilador le advertirá cuando alguna variable local ensombrezca una variable global. Si la función se usara simplemente hostname
en lugar de __hostname
y si tuviera una función llamada hostname()
, habría un sombreado. Al utilizar nombres reservados para la implementación, no hay conflicto con su código legítimo.
El uso de __THROW
significa que el código puede, bajo algunas circunstancias, declararse con algún tipo de "especificación de lanzamiento". Este no es el estándar C; es más como C++. Pero el código se puede usar con un compilador de C siempre que uno de los encabezados (o el propio compilador) se defina __THROW
como vacío, o con alguna extensión específica del compilador de la sintaxis estándar de C.
La sección 7.1.3 de la norma C (ISO 9899:1999) dice:
7.1.3 Identificadores reservados
Cada encabezado declara o define todos los identificadores enumerados en su subcláusula asociada y, opcionalmente, declara o define identificadores enumerados en su subcláusula asociada de instrucciones de biblioteca futuras e identificadores que siempre están reservados para cualquier uso o para su uso como identificadores de alcance de archivo.
— Todos los identificadores que comienzan con un guión bajo y una letra mayúscula u otro guión bajo siempre están reservados para cualquier uso.
— Todos los identificadores que comienzan con un guión bajo siempre están reservados para su uso como identificadores con alcance de archivo tanto en el espacio de nombres ordinario como en el de etiquetas.
— Cada nombre de macro en cualquiera de las siguientes subcláusulas (incluidas las instrucciones futuras de la biblioteca) está reservado para su uso según se especifica si se incluye alguno de sus encabezados asociados; a menos que se indique explícitamente lo contrario (ver 7.1.4).
— Todos los identificadores con enlace externo en cualquiera de las siguientes subcláusulas (incluidas las instrucciones futuras de la biblioteca) siempre están reservados para su uso como identificadores con enlace externo. 154)
— Cada identificador con alcance de archivo enumerado en cualquiera de las siguientes subcláusulas (incluidas las instrucciones futuras de la biblioteca) está reservado para su uso como nombre de macro y como identificador con alcance de archivo en el mismo espacio de nombres si se incluye alguno de sus encabezados asociados.
No se reservan otros identificadores. Si el programa declara o define un identificador en un contexto en el que está reservado (distinto del permitido en 7.1.4), o define un identificador reservado como un nombre de macro, el comportamiento no está definido.
Si el programa elimina (con
#undef
) cualquier definición de macro de un identificador en el primer grupo enumerado anteriormente, el comportamiento no está definido.Nota al pie 154) La lista de identificadores reservados con enlace externo incluye
errno
,math_errhandling
,setjmp
yva_end
.
Consulte también ¿Cuáles son las reglas sobre el uso de un guión bajo en un identificador de C++ ? muchas de las mismas reglas se aplican tanto a C como a C++, aunque la regla de doble subrayado incrustada está solo en C++, como se menciona al principio de esta respuesta.
Justificación del C99
El fundamento del C99 dice:
7.1.3 Identificadores reservados
Para brindar a los implementadores la máxima libertad a la hora de empaquetar las funciones de la biblioteca en archivos, todos los identificadores externos definidos por la biblioteca están reservados en un entorno alojado. En efecto, esto significa que ningún nombre externo proporcionado por el usuario puede coincidir con los nombres de la biblioteca, ni siquiera si la función del usuario tiene la misma especificación. Así, por ejemplo,
strtod
se puede definir en el mismo módulo de objeto queprintf
, sin temor a que se produzcan conflictos en el tiempo de enlace. Igualmente,strtod
puede llamar aprintf
, oprintf
puede llamar astrtod
, por cualquier motivo, sin temor a que se llame a la función incorrecta.También están reservados para el implementador todos los identificadores externos que comienzan con un guión bajo y todos los demás identificadores que comienzan con un guión bajo seguido de una letra mayúscula o un guión bajo. Esto proporciona un espacio de nombres para escribir las numerosas funciones y macros no externas detrás de escena que una biblioteca necesita para hacer su trabajo correctamente.
Con estas excepciones, el Estándar asegura al programador que todos los demás identificadores están disponibles, sin temor a colisiones inesperadas al mover programas de una implementación a otra 5 . Tenga en cuenta, en particular, que parte del espacio de nombres de los identificadores internos que comienzan con un guión bajo está disponible para el usuario: los implementadores del traductor no han sido los únicos en encontrar uso para nombres "ocultos". C es un lenguaje tan portátil en muchos aspectos que el problema de la “contaminación del espacio de nombres” ha sido y es una de las principales barreras para escribir código completamente portátil. Por lo tanto, el Estándar garantiza que las macros y
typedef
los nombres se reserven sólo si el encabezado asociado se incluye explícitamente.5 Véase §6.2.1 para una discusión de algunas de las precauciones que un implementador debe tomar para cumplir esta promesa. Tenga en cuenta también que cualquier nombre de miembro definido por la implementación en estructuras definidas en
<time.h>
y<locale.h>
debe comenzar con un guión bajo, en lugar de seguir el patrón de otros nombres en esas estructuras.
Y la parte relevante del fundamento del §6.2.1 Alcance de los identificadores es:
Aunque el alcance de un identificador en un prototipo de función comienza en su declaración y termina al final del declarador de esa función, el preprocesador ignora este alcance. Por tanto, un identificador en un prototipo que tiene el mismo nombre que el de una macro existente se trata como una invocación de esa macro. Por ejemplo:
#define status 23 void exit(int status);
genera un error, ya que el prototipo luego del preprocesamiento se vuelve
void exit(int 23);
Quizás lo más sorprendente sea lo que sucede si el estatus se define
#define status []
Entonces el prototipo resultante es
void exit(int []);
lo cual es sintácticamente correcto pero semánticamente bastante diferente de la intención.
Para proteger los prototipos de encabezado de una implementación de tales malas interpretaciones, el implementador debe escribirlos para evitar estas sorpresas. Las posibles soluciones incluyen no usar identificadores en prototipos o usar nombres en el espacio de nombres reservado (como
__status
o_Status
).
Véase también PJ Plauger The Standard C Library (1992) para una discusión extensa sobre las reglas de espacio de nombres y las implementaciones de bibliotecas. El libro hace referencia al C90 en lugar de a cualquier versión posterior del estándar, pero la mayoría de los consejos de implementación que contiene siguen siendo válidos hasta el día de hoy.
Los nombres con guiones bajos dobles están reservados para su uso por parte de la implementación. Esto no significa necesariamente que sean internos per se, aunque a menudo lo son.
La idea es que no se le permite usar ningún nombre que comience con __
, por lo que la implementación es libre de usarlos en lugares como expansiones de macros o en los nombres de extensiones de sintaxis (por ejemplo, __gcnew
no es parte de C++, pero Microsoft puede agregar a C++/CLI con la confianza de que ningún código existente debería tener algo así int __gcnew;
que deje de compilarse).
Para saber qué significan estas extensiones específicas, es decir, __const
deberá consultar la documentación de su compilador/plataforma específica. En este caso particular, probablemente debería considerar el prototipo en la documentación (por ejemplo, http://www.kernel.org/doc/man-pages/online/pages/man3/ether_aton.3.html ) como la interfaz de la función y ignore las decoraciones __const
y __THROW
que aparecen en el encabezado real.