C99 (ранее C9X , официально ISO/IEC 9899:1999 ) — предыдущая версия открытого стандарта языка программирования C. [1] Она расширяет предыдущую версию ( C90 ) новыми функциями для языка и стандартной библиотеки , а также помогает реализациям лучше использовать имеющееся компьютерное оборудование, такое как арифметика с плавающей точкой IEEE 754-1985 и технология компилятора. [2] Версия C11 стандарта языка программирования C, опубликованная в 2011 году, обновляет C99.
После того, как ANSI выпустил официальный стандарт для языка программирования C в 1989 году, который стал международным стандартом в 1990 году, спецификация языка C некоторое время оставалась относительно статичной, в то время как C++ продолжал развиваться, в основном в ходе собственных усилий по стандартизации. Нормативная поправка 1 создала новый стандарт для C в 1995 году, но только для исправления некоторых деталей стандарта 1989 года и добавления более обширной поддержки международных наборов символов. Стандарт подвергся дальнейшему пересмотру в конце 1990-х годов, что привело к публикации ISO/IEC 9899:1999 в 1999 году, который был принят в качестве стандарта ANSI в мае 2000 года. Язык, определенный этой версией стандарта, обычно называют «C99». Международный стандарт C поддерживается рабочей группой ISO/IEC JTC1/SC22 /WG14.
C99 по большей части обратно совместим с C89, но в некоторых отношениях он строже. [3]
В частности, объявление, в котором отсутствует спецификатор типа, больше не int
подразумевается. Комитет по стандартам C решил, что для компиляторов важнее диагностировать непреднамеренное упущение спецификатора типа, чем молча обрабатывать устаревший код, который полагался на implicit int
. На практике компиляторы, скорее всего, выведут предупреждение, затем предполагают int
и продолжат трансляцию программы.
C99 представил несколько новых функций, многие из которых уже были реализованы как расширения в нескольких компиляторах: [4]
long long int
необязательные расширенные целочисленные типы, явный логический тип данных и complex
тип для представления комплексных чисел//
, как в BCPL , C++ и Javasnprintf
<stdbool.h>
<complex.h>
<tgmath.h>
<inttypes.h>
<tgmath.h>
, которые выбирают функцию математической библиотеки на основе , , или аргументов и т. д.float
double
long double
struct point p = { .x = 1, .y = 2 };
) [5]function((struct x) {1, 2})
) [6]restrict
квалификация позволяет более агрессивно оптимизировать код , устраняя преимущества доступа к массиву во время компиляции, которые ранее имел FORTRAN по сравнению с ANSI C [7]\u0040
или восьмизначные шестнадцатеричные последовательности\U0001f431
static
в индексах массива в объявлениях параметров [8]Части стандарта C99 включены в текущую версию стандарта C++ , включая целочисленные типы, заголовки и библиотечные функции. Массивы переменной длины не входят в число этих включенных частей, поскольку библиотека стандартных шаблонов C++ уже включает в себя подобную функциональность.
Главной особенностью C99 является его поддержка чисел, и в частности его поддержка доступа к функциям IEEE 754-1985 (также известного как IEC 60559) оборудования с плавающей точкой , присутствующего в подавляющем большинстве современных процессоров (определено в «Приложении F IEC 60559 Арифметика с плавающей точкой»). Платформы без оборудования IEEE 754 также могут реализовать его программно. [2]
На платформах с плавающей точкой IEEE 754:
float
определяется как одинарная точность IEEE 754 , double
определяется как двойная точность и long double
определяется как расширенная точность IEEE 754 (например, Intel 80-битная двойная расширенная точность на платформах x86 или x86-64 ) или некоторая форма учетверенной точности , если она доступна; в противном случае это двойная точность.FLT_EVAL_METHOD == 2
указывает, что все внутренние промежуточные вычисления выполняются по умолчанию с высокой точностью (long double), где это возможно (например, 80-битный double extended ), FLT_EVAL_METHOD == 1
выполняет все внутренние промежуточные выражения с двойной точностью (если операнд не является long double), в то время как FLT_EVAL_METHOD == 0
указывает, что каждая операция оценивается только с точностью самого широкого операнда каждого оператора. Тип промежуточного результата для операндов заданной точности суммирован в соседней таблице.FLT_EVAL_METHOD == 2
имеет тенденцию ограничивать риск ошибок округления, влияющих на численно нестабильные выражения (см. обоснование дизайна IEEE 754 ) и является разработанным методом по умолчанию для оборудования x87 , но приводит к неинтуитивному поведению для неосторожного пользователя; [9] FLT_EVAL_METHOD == 1
был методом оценки по умолчанию, первоначально использовавшимся в K&R C , который продвигал все числа с плавающей точкой к double в выражениях; а FLT_EVAL_METHOD == 0
также широко используется и определяет строгое «вычислять по типу» операндов. (Для gcc являетсяFLT_EVAL_METHOD == 2
методом по умолчанию для 32-битной x86 и FLT_EVAL_METHOD == 0
является методом по умолчанию для 64-битной x86-64, но FLT_EVAL_METHOD == 2
может быть указан для x86-64 с помощью параметра -mfpmath=387.) До C99 компиляторы могли округлять промежуточные результаты непоследовательно, особенно при использовании оборудования с плавающей точкой x87 , что приводило к поведению, специфичному для компилятора; [10] такие непоследовательности не допускаются в компиляторах, соответствующих C99 (приложение F).
Следующий аннотированный пример кода C99 для вычисления функции цепной дроби демонстрирует основные функции:
#include <stdio.h> #include <math.h> #include <float.h> #include <fenv.h> #include <tgmath.h> #include <stdbool.h> #include <assert.h> двойной compute_fn ( двойной z ) // [1] { #pragma STDC FENV_ACCESS ВКЛ // [2] утверждать ( FLT_EVAL_METHOD == 2 ); // [3] если ( иснан ( z )) // [4] puts ( "z не является числом" ); если ( isinf ( z )) puts ( "z бесконечно" ); длинный двойной r = 7,0 - 3,0 / ( z - 2,0 - 1,0 / ( z - 7,0 + 10,0 / ( z - 2,0 - 2,0 / ( z - 3,0 )))); // [5, 6] feclearexcept ( FE_DIVBYZERO ); // [7] bool поднят = fetestexcept ( FE_OVERFLOW ); // [8] если ( поднятый ) puts ( "Непредвиденное переполнение." ); вернуть г ; }int main ( пустота ) { #ifndef __STDC_IEC_559__ puts ( "Предупреждение: __STDC_IEC_559__ не определено. Плавающая точка IEEE 754 поддерживается не полностью." ); // [9] #endif #pragma STDC FENV_ACCESS ВКЛ #ifdef ТЕСТ_ЧИСЛОВАЯ_СТАБИЛЬНОСТЬ_ВВЕРХ fesetround ( FE_UPWARD ); // [10] #elif ТЕСТ_ЧИСЛОВАЯ_СТАБИЛЬНОСТЬ_ВНИЗ fesetround ( FE_DOWNWARD ); #endif printf ( "%.7g \n " , compute_fn ( 3.0 )); printf ( "%.7g \n " , Compute_fn ( NAN )); вернуть 0 ; }
Сноски:
gcc -std=c99 -mfpmath=387 -o test_c99_fp test_c99_fp.c -lm
STDC
определены в стандарте C.)long double
определяется как IEEE 754 double extended или quad precision, если доступно. Использование более высокой точности, чем требуется для промежуточных вычислений, может минимизировать ошибку округления [11] ( typedef double_t
может использоваться для кода, который является переносимым при всех FLT_EVAL_METHOD
s).FLT_EVAL_METHOD
определено как 2, то все внутренние вычисления, включая константы, будут выполняться с точностью long double; если FLT_EVAL_METHOD
определено как 0, то для обеспечения этого необходимо дополнительное внимание, включая возможные дополнительные приведения и явное указание констант как long double.)__STDC_IEC_559__
должен быть определен только в том случае, если «Приложение F IEC 60559 Арифметика с плавающей точкой» полностью реализовано компилятором и библиотекой C (пользователи должны знать, что этот макрос иногда определяется, хотя это не должно быть так).TEST_NUMERIC_STABILITY_UP
и т. д. в этом примере при отладке) может использоваться для диагностики числовой нестабильности. [12] Этот метод можно использовать, даже если compute_fn()
он является частью отдельно скомпилированной двоичной библиотеки. Но в зависимости от функции числовая нестабильность не всегда может быть обнаружена.Стандартный макрос __STDC_VERSION__
определен со значением, 199901L
указывающим на доступность поддержки C99. Как и __STDC__
макрос для C90, __STDC_VERSION__
может использоваться для написания кода, который будет компилироваться по-разному для компиляторов C90 и C99, как в этом примере, который гарантирует, что inline
он доступен в любом случае (путем замены его на static
в C90, чтобы избежать ошибок компоновщика).
#if __STDC_VERSION__ >= 199901L /* "inline" — это ключевое слово */ #else # определить inline static #endif
Большинство компиляторов C поддерживают по крайней мере некоторые функции, представленные в C99.
Исторически сложилось так, что Microsoft не спешила внедрять новые функции C в свои инструменты Visual C++ , вместо этого сосредоточившись в основном на поддержке разработок в стандартах C++. [13] Однако с выпуском Visual C++ 2013 Microsoft реализовала ограниченное подмножество C99, которое было расширено в Visual C++ 2015. [14]
После ратификации стандарта C 1999 года рабочая группа по стандартам подготовила технические отчеты, определяющие улучшенную поддержку встроенной обработки, дополнительные типы символьных данных ( поддержка Unicode ) и библиотечные функции с улучшенной проверкой границ . Продолжается работа над техническими отчетами, касающимися десятичных чисел с плавающей точкой , дополнительных математических специальных функций и дополнительных функций динамического распределения памяти . Комитеты по стандартам C и C++ сотрудничают в разработке спецификаций для потокового программирования.
Следующая редакция стандарта C, C11 , была ратифицирована в 2011 году. [41] Комитет по стандартам C принял руководящие принципы, которые ограничили принятие новых функций, которые не были протестированы существующими реализациями. Много усилий было потрачено на разработку модели памяти , чтобы прояснить точки последовательности и поддержать потоковое программирование.