В языке программирования C типы данных составляют семантику и характеристики хранения элементов данных. Они выражаются в синтаксисе языка в виде объявлений для ячеек памяти или переменных . Типы данных также определяют типы операций или методы обработки элементов данных.
Язык C предоставляет основные арифметические типы, такие как целые и действительные типы чисел, а также синтаксис для построения массивов и составных типов. Заголовки для стандартной библиотеки C , которые должны использоваться через директивы include , содержат определения типов поддержки, которые имеют дополнительные свойства, такие как предоставление хранилища с точным размером, независимо от реализации языка на конкретных аппаратных платформах. [1] [2]
Язык C предоставляет четыре основных спецификатора арифметических типов char , int , float и double , а также модификаторы signed , unsigned , short и long . В следующей таблице перечислены допустимые комбинации при указании большого набора объявлений, специфичных для размера хранилища.
SCHAR_MIN == −128
и SCHAR_MAX == 127
) для 8-битного знакового символа . Начиная с C23, единственным разрешенным представлением является дополнение до двух, поэтому значения варьируются по крайней мере от [−2 n −1 , 2 n −1 −1] . [5]%hhi
для числового вывода%hhu
для числового выводаФактический размер целочисленных типов зависит от реализации. Стандарт требует только соотношения размеров между типами данных и минимальные размеры для каждого типа данных:
Требования к отношению таковы, что long long
не меньше long , который не меньше int , который не меньше short . Поскольку размер char всегда является минимальным поддерживаемым типом данных, никакие другие типы данных (кроме битовых полей ) не могут быть меньше.
Минимальный размер для char составляет 8 бит, минимальный размер для short и int составляет 16 бит, для long он составляет 32 бита и должен содержать не менее 64 бит.long long
Тип int должен быть целочисленным типом, с которым целевой процессор работает наиболее эффективно. Это обеспечивает большую гибкость: например, все типы могут быть 64-битными. Однако популярны несколько различных схем целочисленной ширины (моделей данных). Поскольку модель данных определяет, как взаимодействуют разные программы, в данном интерфейсе приложения операционной системы используется единая модель данных. [9]
На практике char обычно имеет размер 8 бит, а short — 16 бит (как и их беззнаковые аналоги). Это справедливо для таких разных платформ, как SunOS 4 Unix 1990-х годов, Microsoft MS-DOS , современный Linux и Microchip MCC18 для встроенных 8-битных микроконтроллеров PIC . POSIX требует , чтобы char имел размер ровно 8 бит. [10] [11]
Различные правила стандарта C делают unsigned char
базовый тип, используемый для массивов, подходящим для хранения произвольных объектов, не являющихся битовыми полями: отсутствие битов заполнения и представлений ловушек, определение представления объекта [7] и возможность псевдонимов. [12]
Фактический размер и поведение типов с плавающей точкой также различаются в зависимости от реализации. Единственное требование — long double
не быть меньше double , который не меньше float . Обычно для float и double используются 32-битный и 64-битный двоичные форматы с плавающей точкой IEEE 754 соответственно.
Стандарт C99 включает новые действительные типы с плавающей точкой float_t и double_t , определенные в . Они соответствуют типам, используемым для промежуточных результатов выражений с плавающей точкой, когда FLT_EVAL_METHOD равен 0, 1 или 2. Эти типы могут быть шире, чем .<math.h>
long double
C99 также добавил комплексные типы: float _Complex
, double _Complex
, long double _Complex
. C11 добавил мнимые типы (которые были описаны в информативном приложении C99): float _Imaginary
, double _Imaginary
, long double _Imaginary
. Включение заголовка <complex.h>
позволяет получить доступ ко всем этим типам с использованием комплексного и мнимого соответственно.
C99 добавил тип данных Boolean _Bool
. Кроме того, <stdbool.h>
заголовок определяет bool
как удобный псевдоним для этого типа, а также предоставляет макросы для true
и false
. _Bool
функции, аналогичные обычному целочисленному типу, с одним исключением: любые назначения a, _Bool
которые не равны 0 (ложь), сохраняются как 1 (истина). Такое поведение существует для того, чтобы избежать целочисленных переполнений в неявных сужающихся преобразованиях. Например, в следующем коде:
беззнаковый символ b = 256 ; если ( б ) { /* сделать что-то */ }
Переменная b
оценивается как false, если unsigned char
имеет размер 8 бит. Это происходит потому, что значение 256 не помещается в тип данных, что приводит к использованию нижних 8 бит, что приводит к нулевому значению. Однако изменение типа приводит к тому, что предыдущий код ведет себя нормально:
_Bool b = 256 ; если ( б ) { /* сделать что-то */ }
Тип _Bool также гарантирует, что истинные значения всегда будут равны друг другу:
_Bool a = 1 , b = 2 ; если ( a == b ) { /* этот код будет запущен */ }
Начиная с C23 , язык позволяет программисту определять целые числа, имеющие ширину произвольного числа бит. Эти типы указываются как , где N — целочисленное константное выражение, которое обозначает число бит, включая бит знака для знаковых типов, представленных в дополнительном коде. Максимальное значение N предоставляется и составляет не менее . Таким образом, тип (или ) принимает значения от −2 до 1, а принимает значения от 0 до 3. Тип также существует, будучи либо 0, либо 1, и не имеет эквивалентного знакового типа. [13]_BitInt(N)
BITINT_MAXWIDTH
ULLONG_WIDTH
_BitInt(2)
signed _BitInt(2)
unsigned _BitInt(2)
unsigned _BitInt(1)
Спецификация языка C включает typedef s и для представления связанных с памятью величин. Их размер определяется в соответствии с арифметическими возможностями целевого процессора, а не возможностями памяти, такими как доступное адресное пространство. Оба эти типа определены в заголовке ( в C++).size_t
ptrdiff_t
<stddef.h>
cstddef
size_t
— это тип целого числа без знака, используемый для представления размера любого объекта (включая массивы) в конкретной реализации. Оператор sizeof возвращает значение типа . Максимальный размер предоставляется через , макроконстанту, которая определена в заголовке ( заголовок в C++). гарантированно имеет ширину не менее 16 бит. Кроме того, POSIX включает , который является типом целого числа со знаком той же ширины, что и .size_t
size_t
SIZE_MAX
<stdint.h>
cstdint
size_t
ssize_t
size_t
ptrdiff_t
— это знаковый целочисленный тип, используемый для представления разницы между указателями. Он гарантированно действителен только для указателей одного типа; вычитание указателей, состоящих из разных типов, определяется реализацией.
Информация о фактических свойствах, таких как размер, основных арифметических типов, предоставляется через макроконстанты в двух заголовках: <limits.h>
заголовок ( climits
header в C++) определяет макросы для целочисленных типов, а <float.h>
заголовок ( cfloat
header в C++) определяет макросы для типов с плавающей точкой. Фактические значения зависят от реализации.
CHAR_BIT
– размер символьного типа в битах, обычно называемый размером байта ( не менее 8 бит)SCHAR_MIN
, SHRT_MIN
, INT_MIN
, LONG_MIN
, LLONG_MIN
(C99) – минимально возможное значение знаковых целых типов: signed char, signed short, sign int, sign long, sign long longSCHAR_MAX
, SHRT_MAX
, INT_MAX
, LONG_MAX
, LLONG_MAX
(C99) – максимально возможное значение знаковых целых типов: signed char, signed short, sign int, sign long, sign long longUCHAR_MAX
, USHRT_MAX
, UINT_MAX
, ULONG_MAX
, ULLONG_MAX
(C99) – максимально возможное значение беззнаковых целочисленных типов: unsigned char, unsigned short, unsigned int, unsigned long, unsigned long longCHAR_MIN
– минимально возможное значение символаCHAR_MAX
– максимально возможное значение символаMB_LEN_MAX
– максимальное количество байтов в многобайтовом символеBOOL_WIDTH
(C23) - разрядность _Bool
, всегда 1CHAR_WIDTH
(C23) - разрядность char
; CHAR_WIDTH
, UCHAR_WIDTH
и по определению SCHAR_WIDTH
равныCHAR_BIT
SCHAR_WIDTH
, SHRT_WIDTH
, INT_WIDTH
, LONG_WIDTH
, LLONG_WIDTH
(C23) - разрядность signed char
, short
, int
, long
, и long long
соответственноUCHAR_WIDTH
, USHRT_WIDTH
, UINT_WIDTH
, ULONG_WIDTH
, ULLONG_WIDTH
(C23) - разрядность unsigned char
, unsigned short
, unsigned int
, unsigned long
, и unsigned long long
соответственноFLT_MIN
, DBL_MIN
, LDBL_MIN
– минимальное нормализованное положительное значение float, double, long double соответственноFLT_TRUE_MIN
, DBL_TRUE_MIN
, LDBL_TRUE_MIN
(C11) – минимальное положительное значение float, double, long double соответственноFLT_MAX
, DBL_MAX
, LDBL_MAX
– максимальное конечное значение float, double, long double соответственноFLT_ROUNDS
– режим округления для операций с плавающей точкойFLT_EVAL_METHOD
(C99) – метод оценки выражений, включающих различные типы с плавающей точкойFLT_RADIX
– основание экспоненты в типах с плавающей точкойFLT_DIG
, DBL_DIG
, LDBL_DIG
– количество десятичных цифр, которые можно представить без потери точности с помощью float, double, long double соответственноFLT_EPSILON
, DBL_EPSILON
, LDBL_EPSILON
– разница между 1,0 и следующим представимым значением float, double, long double соответственноFLT_MANT_DIG
, DBL_MANT_DIG
, LDBL_MANT_DIG
– количество FLT_RADIX
цифр в системе счисления с плавающей точкой для типов float, double, long double соответственноFLT_MIN_EXP
, DBL_MIN_EXP
, LDBL_MIN_EXP
– минимальное отрицательное целое число, FLT_RADIX
возведенное в степень, меньшую этого числа, является нормализованным числом с плавающей точкой, числом двойной точности, числом длинной двойной точности соответственноFLT_MIN_10_EXP
, DBL_MIN_10_EXP
, LDBL_MIN_10_EXP
– минимальное отрицательное целое число, такое что 10, возведенное в эту степень, является нормализованным числом float, double, long double соответственноFLT_MAX_EXP
, DBL_MAX_EXP
, LDBL_MAX_EXP
– максимальное положительное целое число, FLT_RADIX
возведенное в степень на единицу меньше этого числа, является нормализованным числом float, double, long double соответственноFLT_MAX_10_EXP
, DBL_MAX_10_EXP
, LDBL_MAX_10_EXP
– максимальное положительное целое число, такое что 10, возведенное в эту степень, является нормализованным числом float, double, long double соответственноDECIMAL_DIG
(C99) – минимальное количество десятичных цифр, при котором любое число самого широкого поддерживаемого типа с плавающей точкой может быть представлено в десятичном виде с точностью до DECIMAL_DIG
цифр и считано обратно в исходном типе с плавающей точкой без изменения его значения. DECIMAL_DIG
составляет не менее 10.Стандарт C99 включает определения нескольких новых целочисленных типов для улучшения переносимости программ. [2] Уже имеющиеся базовые целочисленные типы были признаны недостаточными, поскольку их фактические размеры определяются реализацией и могут различаться в разных системах. Новые типы особенно полезны во встроенных средах , где оборудование обычно поддерживает только несколько типов, и эта поддержка различается в разных средах. Все новые типы определены в <inttypes.h>
заголовке ( cinttypes
header в C++) и также доступны в <stdint.h>
заголовке ( cstdint
header в C++). Типы можно сгруппировать в следующие категории:
В следующей таблице приведены типы и интерфейс для получения подробностей реализации ( n относится к количеству бит):
Заголовок <inttypes.h>
( cinttypes
в C++) предоставляет возможности, которые расширяют функциональность типов, определенных в <stdint.h>
заголовке. Он определяет макросы для спецификаторов формата printf и scanf, соответствующие типам, определенным в <stdint.h>
, и несколько функций для работы с типами intmax_t
и uintmax_t
. Этот заголовок был добавлен в C99 .
Макросы имеют формат . Здесь {fmt} определяет форматирование вывода и может быть одним из следующих: (десятичное), (шестнадцатеричное), (восьмеричное), (беззнаковое) и (целое). {type} определяет тип аргумента и может быть одним из следующих : , , , , , где соответствует количеству бит в аргументе.PRI{fmt}{type}
d
x
o
u
i
n
FASTn
LEASTn
PTR
MAX
n
Макросы имеют формат . Здесь {fmt} определяет форматирование вывода и может быть одним из следующих: (десятичное), (шестнадцатеричное), (восьмеричное), (беззнаковое) и (целое). {type} определяет тип аргумента и может быть одним из следующих : , , , , , где соответствует количеству бит в аргументе.SCN{fmt}{type}
d
x
o
u
i
n
FASTn
LEASTn
PTR
MAX
n
Аналогично целочисленным типам фиксированной ширины, стандарт ISO/IEC TS 18661 определяет типы с плавающей точкой для обмена IEEE 754 и расширенных форматов в двоичном и десятичном виде:
_FloatN
для двоичных форматов обмена;_DecimalN
для десятичных форматов обмена;_FloatNx
для двоичных расширенных форматов;_DecimalNx
для расширенных десятичных форматов.Структуры объединяют хранение нескольких элементов данных, потенциально различающихся типов данных, в один блок памяти, на который ссылается одна переменная. В следующем примере объявляется тип данных struct birthday
, содержащий имя и день рождения человека. За определением структуры следует объявление переменной John
, которая выделяет необходимое хранилище.
struct birthday { char name [ 20 ]; int day ; int month ; int year ; }; структура день рождения Джона ;
Размещение памяти структуры — это проблема языковой реализации для каждой платформы с некоторыми ограничениями. Адрес памяти первого члена должен совпадать с адресом самой структуры. Структуры могут быть инициализированы или назначены с использованием составных литералов. Функция может напрямую возвращать структуру, хотя это часто неэффективно во время выполнения. Начиная с C99 , структура также может заканчиваться гибким членом массива .
Структура, содержащая указатель на структуру своего типа, обычно используется для построения связанных структур данных :
структурный узел { int val ; структурный узел * следующий ; };
Для каждого типа T
, за исключением типов void и function, существуют типы "массив N
элементов типа T
" . Массив — это коллекция значений, все одного типа, хранящихся в памяти последовательно. Массив размером N
индексируется целыми числами от 0
до включительно N−1
. Вот краткий пример:
int cat [ 10 ]; // массив из 10 элементов, каждый типа int
Массивы могут быть инициализированы составным инициализатором, но не назначены. Массивы передаются функциям путем передачи указателя на первый элемент. Многомерные массивы определяются как "массив массивов …" , и все, кроме самого внешнего измерения, должны иметь постоянный размер времени компиляции:
int a [ 10 ][ 8 ]; // массив из 10 элементов, каждый типа 'массив из 8 элементов int'
Каждый тип данных T
имеет соответствующий указатель типа наT
. Указатель — это тип данных, содержащий адрес места хранения переменной определенного типа. Они объявляются с помощью *
декларатора типа звездочка ( ), следующего за базовым типом хранения и предшествующего имени переменной. Пробелы до или после звездочки необязательны.
char * квадрат ; long * круг ; int * овал ;
Указатели также могут быть объявлены для типов данных указателя, тем самым создавая несколько косвенных указателей, таких как char ** и int *** , включая указатели на типы массивов. Последние встречаются реже, чем массив указателей, и их синтаксис может быть запутанным:
char * pc [ 10 ]; // массив из 10 элементов 'указателя на char' char ( * pa )[ 10 ]; // указатель на массив из 10 элементов char
Элементу pc
требуется десять блоков памяти размером с указательchar
(обычно 40 или 80 байт на обычных платформах), но элемент pa
представляет собой только один указатель (размером 4 или 8 байт), а данные, на которые он ссылается, представляют собой массив из десяти байт ( ).sizeof *pa == 10
Тип объединения — это специальная конструкция, которая позволяет получить доступ к одному и тому же блоку памяти, используя выбор различных описаний типов. Например, объединение типов данных может быть объявлено для разрешения чтения одних и тех же данных как целого числа, числа с плавающей точкой или любого другого объявленного пользователем типа:
union { int i ; float f ; struct { unsigned int u ; double d ; } s ; } u ;
Общий размер u
равен размеру u.s
– который является суммой размеров u.s.u
и u.s.d
– поскольку s
больше, чем i
и f
. При назначении чего-либо u.i
, некоторые части u.f
могут быть сохранены, если u.i
меньше, чем u.f
.
Чтение из члена объединения — это не то же самое, что приведение, поскольку значение члена не преобразуется, а просто считывается.
Указатели функций позволяют ссылаться на функции с определенной сигнатурой. Например, для сохранения адреса стандартной функции abs
в переменной my_int_f
:
int ( * my_int_f )( int ) = & abs ; // оператор & можно опустить, но он дает понять, что здесь используется «адрес» abs
Указатели функций вызываются по имени, как и обычные вызовы функций. Указатели функций отделены от указателей и указателей void .
Вышеупомянутые типы могут быть дополнительно охарактеризованы квалификаторами типов , что даст квалифицированный тип . По состоянию на 2014 год [обновлять]и C11 в стандарте C есть четыре квалификатора типов: const
( C89 ), volatile
( C89 ), restrict
( C99 ) и _Atomic
( C11 ) — последний имеет личное имя, чтобы избежать конфликта с именами пользователей, [14] но более обычное имя atomic
может быть использовано, если <stdatomic.h>
включен заголовок. Из них, const
является, безусловно, самым известным и наиболее используемым, появляющимся в стандартной библиотеке и встречающимся при любом значительном использовании языка C, который должен удовлетворять константной корректности . Другие квалификаторы используются для низкоуровневого программирования, и хотя широко используются там, редко используются типичными программистами. [ необходима цитата ]