¿Qué significa doble guión bajo (__const) en C?

10 minutos de lectura

avatar de usuario
Deepak

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 caja de Linux.

¿Alguien puede explicar qué significan los guiones bajos dobles delante de const (palabra clave), addr (identificador) y, por último, __THROW?

  • Hay un resumen muy, muy bueno de las reglas para los identificadores reservados aquí: stackoverflow.com/questions/228783/…

    – Michael Burr

    20 de septiembre de 2009 a las 0:46

  • Técnicamente, x-ref son ‘identificadores reservados para C++’, pero (ahora) también hay buena información del estándar C.

    –Jonathan Leffler

    20 de septiembre de 2009 a las 1:27

avatar de usuario
jonathan leffler

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; usted, 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;

Él __const la notación está ahí para permitir la posibilidad (algo improbable) de que un compilador con el que se usa este código admita notaciones prototipo pero no tiene una comprensión correcta de la palabra clave estándar C89 const. Él autoconf las macros aún pueden verificar si el compilador tiene soporte funcional para const; este código podría usarse con un compilador roto 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 el -Wshadow opción, el compilador le advertirá cuando cualquier variable local sombree una variable global. Si la función utilizada sólo hostname en vez de __hostnamey si tuvieras una función llamada hostname(), habría un sombreado. Al usar 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, en algunas circunstancias, declararse con algún tipo de ‘especificación de lanzamiento’. Esto no es C estándar; es más como C++. Pero el código se puede usar con un compilador C siempre que uno de los encabezados (o el compilador mismo) defina __THROW para vaciar, o para 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 los identificadores enumerados en su subcláusula de direcciones de biblioteca futura asociada e identificadores que siempre están reservados para cualquier uso o para 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 se reservan para cualquier uso.

— Todos los identificadores que comienzan con un guión bajo siempre se reservan para su uso como identificadores con ámbito de archivo en los espacios de nombres ordinarios y de etiquetas.

— Cada nombre de macro en cualquiera de las siguientes subcláusulas (incluidas las direcciones futuras de la biblioteca) se reserva para su uso como se especifica si se incluye cualquiera 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 futuras instrucciones de la biblioteca) siempre se reservan 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 futuras instrucciones de la biblioteca) se reserva 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 (aparte de lo permitido por 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 mencionado anteriormente, el comportamiento no está definido.

Nota al pie 154) La lista de identificadores reservados con enlace externo incluye errno, math_errhandling,
setjmpy va_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 incrustada de doble guión bajo solo está en C++, como se menciona en la parte superior de esta respuesta.


Justificación C99

Él Justificación C99 dice:

7.1.3 Identificadores reservados

Para dar a los implementadores la máxima libertad para empaquetar las funciones de la biblioteca en archivos, todos los identificadores externos definidos por la biblioteca se reservan en un entorno alojado. Esto significa, en efecto, que ningún nombre externo proporcionado por el usuario puede coincidir con los nombres de biblioteca, ni siquiera si la función de usuario tiene la misma especificación. Así, por ejemplo, strtod puede definirse en el mismo módulo de objeto que printf, sin temor a que se produzcan conflictos en el tiempo de enlace. Igualmente, strtod puede llamar printfo printf puede llamar strtodpor el motivo que sea, sin temor a que se llame a la función equivocada.

También están reservados para el implementador todos 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 brinda 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 los identificadores están disponibles, sin temor a colisiones inesperadas al mover programas de una implementación a otra5. 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 de traductores 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, la Norma asegura que macro y typedef los nombres se reservan solo si el encabezado asociado se incluye explícitamente.

5 Ver §6.2.1 para una discusión de algunas de las precauciones que un implementador debe tomar para mantener 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 de la justificación de §6.2.1 Alcances 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. Así, un identificador en un prototipo que tenga 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 después del preprocesado se vuelve

   void exit(int 23);

Quizás más sorprendente es lo que sucede si se define el estado

   #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 interpretaciones erróneas, 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 La biblioteca estándar de C (1992) para una discusión extensa de las reglas del espacio de nombres y las implementaciones de la biblioteca. El libro se refiere a C90 en lugar de a cualquier versión posterior del estándar, pero la mayoría de los consejos de implementación siguen siendo válidos hasta el día de hoy.

Los nombres con guiones bajos dobles al principio están reservados para que los use la implementación. Esto no significa necesariamente que sean internos per se, aunque a menudo lo son.

La idea es que no está permitido 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 (p. ej. __gcnew no es parte de C++, pero Microsoft puede agregarlo a C++/CLI con la confianza de que ningún código existente debería tener algo como int __gcnew; en él que dejaría de compilar).

Para averiguar qué significan estas extensiones específicas, es decir, __const deberá consultar la documentación de su compilador/plataforma específico. 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) para ser la interfaz de la función e ignorar el __const y __THROW decoraciones que aparecen en el encabezado real.

  • Gracias por el enlace… eso definitivamente ayuda.

    – Deepak

    19 de septiembre de 2009 a las 19:14

Por convención en algunas bibliotecas, esto indica que un símbolo en particular es para uso interno y no pretende ser parte de la API pública de la biblioteca.

  • @Rob… Eso significa que, para fines de programación, puedo considerar la definición de función externa int ether_hostton (__const char *__hostname, struct ether_addr *__addr) __THROW; estar sin guiones bajos y __THROW.?

    – Deepak

    19 de septiembre de 2009 a las 18:40

  • ¿Quizás esas son macros y son para compatibilidad con C ++? Si está compilando en modo C++, __THROW == throw() y si const es compatible entonces __const == consto algo por el estilo?

    – Johannes Schaub – litb

    19 de septiembre de 2009 a las 18:44

  • @Deepak: No entiendo tu pregunta.

    – Rob H.

    19 de septiembre de 2009 a las 18:47

  • @Rob si están destinados para uso interno, ¿podría ignorar los guiones bajos y simplemente definir variables del tipo deseado y acceder a los valores durante las llamadas a funciones? Solo quería saber si el guión bajo hizo alguna diferencia para el programador que usa la API.

    – Deepak

    19 de septiembre de 2009 a las 18:50

  • @Deepak: en lo que a usted respecta, la función devuelve un int y toma una cadena (char *) y un struct ether_addr puntero. No importa qué tipo de cadena pase como nombre, la función no la modificará. Eso es todo lo que realmente necesitas saber. Si estuviera escribiendo la función, tendría que preocuparse por la semántica de __const. Él __THROW bit no tiene sentido en C a menos que haya #define __THROW /* Nothing */ o se usa una extensión específica del compilador; no tienes que preocuparte por eso, por lo tanto.

    –Jonathan Leffler

    19 de septiembre de 2009 a las 20:07

avatar de usuario
caótico

El guión bajo en __const significa que esta palabra clave es una extensión del compilador y su uso no es portátil (creo que la palabra clave const se agregó a C en una revisión posterior, 89). El __THROW también es una especie de extensión, supongo que se define en algún __atributo__(algo) si se usa gcc, pero no estoy seguro de eso y soy demasiado perezoso para verificar. El __addr puede significar cualquier cosa que el programador quisiera que significara, es solo un nombre.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad