# C99 (Estándar de C)

C99 (formalmente conocido como ISO/IEC 9899:1999 ) es una revisión del estándar abierto para lenguaje de programación C. Sucede a la versión anterios (C90) con nuevas características para el lenguaje y la biblioteca estándar, y ayuda a las implementaciones a hacer un mejor uso del hardware informático disponible, como la aritmética de punto flotante IEEE 754-1985 y la tecnología del compilador. Fue sustituida por la revisión C11, publicada en 2011.
| Predecesor: ANSI C | Estándares de C | Sucesor: C11 |

## Historia

Después de que ANSI desarrollara el estándar oficial para el lenguaje de programación C en 1989, que se convirtió en un estándar internacional en 1990, la especificación del lenguaje C permaneció relativamente estática durante cierto tiempo. Mientras tanto, C++ continuó evolucionando, en gran medida por su propio esfuerzo de estandarización. La Enmienda Normativa 1 creó un nuevo estándar para C en 1995, pero sólo para corregir algunos detalles del estándar de 1989 y para agregar soporte más amplio para conjuntos de caracteres internacionales. La norma fue objeto de una revisión adicional a finales de la década de 1990, lo que llevó a la publicación de ISO/IEC 9899:1999 en 1999, que fue adoptada como norma ANSI en mayo de 2000. El lenguaje definido por esa versión del estándar se conoce comúnmente como "C99". El estándar internacional C es mantenido por el grupo de trabajo ISO/IEC JTC1/SC22 /WG14.

## Diseño

C99 es, en gran parte, compatible con C89, pero es más estricto en algunos aspectos.
En concreto, una declaración que carece de un especificador de tipo ya no se asume implícitamente de tipo int . El comité de estándares C decidió que era más valioso para los compiladores diagnosticar la omisión del especificador de tipo que procesar silenciosamente el código heredado que dependía de la asunción implicita del tipo int. En la práctica, es probable que los compiladores muestren una advertencia, y luego asuman el tipo int y continúen traduciendo el programa.
C99 introdujo varias características nuevas, muchas de las cuales ya habían sido implementadas como extensiones en varios compiladores:
* funciones en línea (inline)
* Declaraciones y código entremezclados: la declaración de variables ya no está restringida al alcance del archivo o al inicio de una declaración compuesta (bloque)
* Varios tipos de datos nuevos, incluidos long long int, tipos de enteros extendidos opcionales, un tipo de datos booleano explícito y un tipo complex para representar números complejos.
* Arrays de longitud variable (VLA) (aunque posteriormente relegadas en C11 a una característica condicional que las implementaciones no están obligadas a soportar)
* miembros de array flexibles
* Soporte para comentarios de una línea que comiencen con //, como en BCPL, C++ y Java
* Nuevas funciones de biblioteca, como snprintf
* Nuevos encabezados, como <stdbool.h>, <complex.h>, <tgmath.h> y <inttypes.h>
* funciones matemáticas (macros) de tipo genérico, en <tgmath.h>, que seleccionan una función de biblioteca matemática en función de argumentos float, double o long double, etc.
* Soporte mejorado para arithmética de punto flotante IEEE
* Inicializadores designados (por ejemplo, inicializar una estructura por nombres de campo: struct point p = { .x = 1, .y = 2 }; )
* Literales compuestos ("compund literals") (por ejemplo, es posible construir estructuras en llamadas de función: function((struct x) {1, 2}) )
* Soporte para macros variádicas (macros con un número variable de argumentos)
* La calificación restrict permite una optimización de código más agresiva, eliminando las ventajas de acceso a array en tiempo de compilación que anteriormente tenía FORTRAN sobre ANSI C
* Nombres de caracteres universales, que permiten que las variables de usuario contengan caracteres distintos del conjunto de caracteres estándar: secuencias hexadecimales \u0040 de cuatro dígitos o \U0001f431 de ocho dígitos
* Palabra clave static en índices de array en declaraciones de parámetros
Partes del estándar C99 están incluidas en la versión actual del estándar C++, incluidos los tipos enteros, encabezados y funciones de biblioteca. Los arrays de longitud variable no se encuentran entre estas partes incluidas porque la biblioteca de plantillas estándar de C++ ya incluye una funcionalidad similar.

## Soporte de punto flotante IEEE 754

Una característica importante de C99 es su soporte numérico y, en particular, su soporte para el acceso a las características de IEEE 754-1985 (también conocido como IEC 60559) hardware de punto flotante presente en la gran mayoría de procesadores modernos (definido en el "Anexo F IEC 60559 aritmética de punto flotante"). Plataformas sin soporte hardware para IEEE 754 también pueden implementarlo en software.
En plataformas con soporte para aritmética de punto flotante IEEE 754:
* float es definido como un real de precisión simple IEEE 754, double es definido como un real de precisión doble IEEE 754 y long double es definido como un real de precisión extendida IEEE 754 (p.e. Intel 80-bit double de precisión extendida en plataformas x86 o x86-64), u otras formas de precisión cuádruple, cuando estén disponibles
* Los 4 operadores aritméticos, y la raíz cuadrada, son redondeados según el estándar IEEE 754
* La evaluación de expresiones se define para ser realizada en una de las 3 maneras existentes, indicando
* La evaluación de expresiones se define para realizarse en uno de los tres métodos bien definidos, que indican si las variables de punto flotante se promueven primero a un formato más preciso en las expresiones
** FLT_EVAL_METHOD == 2 indica que todos los cálculos intermedios internos se realizan de forma predeterminada con alta precisión (doble largo) donde esté disponible (por ejemplo, 80 bit doble extendido)
** FLT_EVAL_METHOD == 1 realiza todas las expresiones intermedias internas con doble precisión (a menos que un operando sea long double)
** FLT_EVAL_METHOD == 0 especifica que cada operación se evalúa solo con la precisión del operando más ancho de cada operador. El tipo de resultado intermedio para los operandos de una precisión dada se resume en la siguiente tabla:
| FLT_EVAL_METHOD | float | double | long double |
| 0 | float | double | long double |
| 1 | double | double | long double |
| 2 | long double | long double | long double |
El método FLT_EVAL_METHOD == 2 tiende a limitar el riesgo de errores de redondeo que afectan a expresiones numéricamente inestables (ver IEEE 754 fundamento del diseño ) y es el método predeterminado diseñado para hardware x87, pero tiene un comportamiento poco intuitivo para el usuario incauto; FLT_EVAL_METHOD == 1 fue el método de evaluación predeterminado utilizado originalmente en K&R C, que promovía todos los float a double en las expresiones; y FLT_EVAL_METHOD == 0 también se usa comúnmente y especifica una estricta "evaluación al tipo" de los operandos. (Para gcc, FLT_EVAL_METHOD == 2 es el valor predeterminado en 32 bit x86 y FLT_EVAL_METHOD == 0 es el valor predeterminado en 64 bit x86-64, pero FLT_EVAL_METHOD == 2 (Se puede especificar FLT_EVAL_METHOD == 2 en x86-64 con la opción -mfpmath=387.) Antes de C99, los compiladores podían redondear los resultados intermedios de manera inconsistente, especialmente al usar hardware de punto flotante x87, lo que generaba un comportamiento específico del compilador; dichas inconsistencias no están permitidas en compiladores que cumplen con C99 (anexo F).

### Ejemplo

El siguiente ejemplo de código C99 anotado para calcular una función de fracción continua demuestra las características principales:
Notas al pie:
* Compilar con: gcc -std=c99 -mfpmath=387 -o test_c99_fp test_c99_fp.c -lm
* Como indica el IEEE 754, en esta función se manipulan indicadores de estado; este #pragma es necesario para evitar que el compilador reorganice incorrectamente dichas pruebas al optimizar. (Los pragmas generalmente están definidos por la implementación, pero aquellos con el prefijo STDC están definidos en el estándar C).
* C99 define un número limitado de métodos de evaluación de expresiones: se puede verificar el modo de compilación actual para garantizar que cumple con los supuestos bajo los cuales se escribió el código.
* Se pueden probar y configurar valores especiales como NaN e infinito positivo o negativo.
* long double se define como precisión doble extendida o cuádruple IEEE 754 si está disponible. El uso de una precisión mayor que la requerida para los cálculos intermedios puede minimizar el error de redondeo (el typedef double_t se puede utilizar para código que sea portable bajo todos los FLT_EVAL_METHOD ).
* La función principal a evaluar. Aunque parece que algunos argumentos para esta fracción continua, por ejemplo, 3.0, llevarían a un error de división por cero, de hecho la función está bien definida en 3.0 y la división por 0 simplemente devolverá un +infinito que entonces llevará correctamente a un resultado finito: IEEE 754 está definido para no atrapar tales excepciones de manera predeterminada y está diseñado para que puedan ignorarse muy a menudo, como en este caso. (Si FLT_EVAL_METHOD se define como 2, entonces todos los cálculos internos, incluidas las constantes, se realizarán en precisión doble larga; si FLT_EVAL_METHOD se define como 0, entonces se necesita un cuidado adicional para garantizar esto, incluyendo posiblemente conversiones adicionales y especificación explícita de constantes como doble larga).
* Como la flag de división por cero no es un error en este caso, simplemente se puede descartar para borrar la flag para su uso por parte del código posterior.
* En algunos casos, otras excepciones pueden considerarse un error, como un desbordamiento (aunque, de hecho, se puede demostrar que esto no puede ocurrir en este caso).
* __STDC_IEC_559__ debe definirse solo si el compilador y la biblioteca C implementan completamente la "aritmética de punto flotante del Anexo F IEC 60559" (los usuarios deben tener en cuenta que esta macro a veces se define cuando no debería estarlo).
* El modo de redondeo predeterminado es redondear al más cercano (con la regla de redondeo par en los casos intermedios) para IEEE 754, pero configurar explícitamente el modo de redondeo hacia + y - infinito (al definir TEST_NUMERIC_STABILITY_UP, etc. en este ejemplo, al depurar) se puede utilizar para diagnosticar la inestabilidad numérica. Este método se puede utilizar incluso si compute_fn es parte de una biblioteca binaria compilada por separado. Pero dependiendo de la función, no siempre se pueden detectar inestabilidades numéricas.

## Detección de versiones

Se define una macro estándar __STDC_VERSION__ con el valor 199901L para indicar que está disponible el soporte para C99. Al igual que con la macro __STDC__ para C90, __STDC_VERSION__ se puede usar para escribir código que se compilará de manera diferente para los compiladores C90 y C99, como en este ejemplo que garantiza que inline esté disponible en cualquier caso (reemplazándolo con static en C90 para evitar errores del enlazador).

## Implementaciones

La mayoría de los compiladores de C proporcionan soporte para al menos algunas de las características introducidas en C99.
| Compilador | Nivel de soporte | Detalles de compatibilidad con C99 |
| Acorn C/C++ | Parcial | La documentación oficial afirma que "casi todas" las características del compilador, junto con "algunas" funciones de librería son soportadas. |
| AMD x86 Open64 Compiler Suite | Completo | Tiene soporte para C99 similar al de GCC. |
| cc65 | Parcial | No se implementa la compatibilidad completa con C89 y C99, en parte debido a las limitaciones de la plataforma (MOS Technology 6502). No hay soporte planeado para algunos tipos de C99 como _Complex y enteros de 64 bits (long long). |
| Ch | Parcial | Soporta las principales características de C99. |
| Clang | Completo | Soporta todas las características de C99, excepto los pragmas de punto flotante. |
| CompCert | Completo | Un compilador certificado, formalmente comprobado. Admite todas las funciones de C99, excepto los números complejos, los VLA, y restricciones menores en las instrucciones switch (sin Duff's device). |
| cparser | Completo | Soporta las características de C99. |
| C++ Builder | Completo | |
| Digital Mars C/C++ Compiler | Parcial | Soporta algunas características, como por ejemplo <tgmath.h> y _Pragma. |
| GCC | Completo | A fecha de Julio de 2021, faltan pragmas estándar y soporte para aritmética de punto flotante IEEE 754 / IEC 60559 en la versión principal de GCC. Además, algunas características (como los tipos enteros extendidos y las nuevas funciones de biblioteca) deben ser proporcionadas por la biblioteca estándar de C y están fuera del ámbito de GCC. Las versiones 4.6 y 4.7 de GCC también proporcionan el mismo nivel de cumplimiento.. Compatibilidad parcial con IEEE 754, incluso cuando el hardware es compatible: es posible que se necesiten algunas opciones del compilador para evitar optimizaciones incorrectas (por ejemplo, -std=c99 y -fsignaling-nans), pero falta el soporte completo de los modos de redondeo dirigido incluso cuando se usa -frounding-math. |
| Green Hills Software | Full | |
| IBM C for AIX, V6 and XL C/C++ V11.1 for AIX | Full | |
| IBM Rational logiscope | Full | Hasta Logiscope 6.3, solo se admitían las construcciones básicas de C99. C99 está oficialmente soportado por Logiscope 6.4 y versiones posteriores. |
| The Portland Group PGI C/C++ | Full | |
| IAR Systems Embedded Workbench | Mostly | No es compatible con UCN (Unified Character Name). Compilador para objetivos incrustados ("embeded"), como ARM, Coldfire, MSP430, AVR, AVR32, 8051,... No hay objetivos x86. |
| Intel C++ compiler | Completo | |
| Microsoft Visual C++ | Parcial | Visual C++ 2012 y versiones anteriores no eran compatibles con C99. Visual C++ 2013 implementa un subconjunto limitado de C99, necesario para compilar proyectos de código abierto populares. Visual C++ 2015 implementa la biblioteca estándar C99, con la excepción de las características de la biblioteca que dependen de las características del compilador que aún no son compatibles con el compilador (por ejemplo, <tgmath.h> no está implementado). Visual C++ 2019 (16.6) agrega compatibilidad opcional para un preprocesador compatible con C99. |
| Open Watcom | Parcial | Implementa las partes más utilizadas de la norma. Sin embargo, solo se habilitan a través del argumento de línea de comandos -za99. Tres características de C99 se han agrupado como extensiones de C90 desde la versión anterior a la versión 1.0: comentarios de estilo de C++ (//), arrays de longitud variable, coma final en la declaración de enumeración. |
| Pelles C | Full | Soporta todas las características de C99. |
| Portable C compiler | Parcial | Trabajando en conseguir portabilidad completa con C99. |
| Sun Studio | Full | |
| The Amsterdam Compiler Kit | No | Un front-end para C99 está siendo desarrollado. |
| Tiny C Compiler | Parcial | No admite números complejos. Los arrays de longitud variable son soportados, pero no como argumentos en las funciones. Los desarrolladores afirman que "TCC se dirige hacia el cumplimiento total de ISOC99". |
| vbcc | Parcial | |
Históricamente, Microsoft ha sido lento en implementar nuevas características de C en sus herramientas Visual C++, enfocándose en cambio principalmente en apoyar desarrollos en los estándares de C++. Sin embargo, con la introducción de Visual C++ 2013, Microsoft implementó un subconjunto limitado de C99, que se amplió en Visual C++ 2015.

## Trabajo futuro

Desde la ratificación del estándar C de 1999, el grupo de trabajo de estándares preparó informes técnicos que especifican soporte mejorado para procesamiento integrado, tipos de datos de caracteres adicionales (soporte Unicode ) y funciones de biblioteca con verificación de límites mejorada. Se continúa trabajando en informes técnicos que abordan el punto flotante decimal, funciones matemáticas especiales adicionales y funciones adicionales de asignación de memoria dinámica. Los comités de estándares C y C++ han estado colaborando en especificaciones para programación multi-hilo .
La siguiente revisión de la norma C, C11, fue ratificada en 2011. El comité de estándares C adoptó pautas que limitaron la adopción de nuevas características que no hayan sido probadas por implementaciones existentes. Se invirtió mucho esfuerzo en desarrollar un modelo de memoria, con el fin de aclarar los puntos de secuencia y admitir la programación multi-hilo .