В языке программирования 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
для числового выводаФактический размер целочисленных типов зависит от реализации. Стандарт требует только соотношения размеров между типами данных и минимальных размеров для каждого типа данных:
Требования к отношению заключаются в том, что the 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 . Обычно 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 добавлен логический тип данных _Bool
. Кроме того, <stdbool.h>
заголовок определяет bool
удобный псевдоним для этого типа, а также предоставляет макросы для true
и false
. _Bool
функционирует аналогично обычному целочисленному типу, за одним исключением: любые присвоения a, _Bool
отличные от 0 (ложь), сохраняются как 1 (истина). Такое поведение существует для того, чтобы избежать целочисленного переполнения при неявных сужающих преобразованиях. Например, в следующем коде:
беззнаковый символ b = 256 ; if ( b ) { /* сделать что-нибудь */ }
Переменная b
оценивается как ложная, если unsigned char
ее размер составляет 8 бит. Это связано с тем, что значение 256 не соответствует типу данных, в результате чего используются младшие 8 битов, что приводит к нулевому значению. Однако изменение типа приводит к тому, что предыдущий код ведет себя нормально:
_Bool б = 256 ; if ( b ) { /* сделать что-нибудь */ }
Тип _Bool также гарантирует, что истинные значения всегда сравниваются равными друг другу:
_Bool а = 1 , б = 2 ; if ( 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 и для представления величин , связанных с памятью. Их размер определяется в соответствии с арифметическими возможностями целевого процессора, а не возможностями памяти, такими как доступное адресное пространство. Оба этих типа определены в заголовке ( в C++).size_t
ptrdiff_t
<stddef.h>
cstddef
size_t
— это целочисленный тип без знака, используемый для представления размера любого объекта (включая массивы) в конкретной реализации. Оператор sizeof возвращает значение типа . Максимальный размер предоставляется через макроконстанту, которая определена в заголовке ( header в 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
– размер типа char в битах, обычно называемый размером байта ( не менее 8 бит).SCHAR_MIN
, SHRT_MIN
, INT_MIN
, LONG_MIN
, LLONG_MIN
(C99) – минимально возможное значение целочисленных типов со знаком: подписанный char, подписанный короткий, подписанный int, подписанный длинный, подписанный длинный длинныйSCHAR_MAX
, SHRT_MAX
, INT_MAX
, LONG_MAX
, LLONG_MAX
(C99) – максимально возможное значение целочисленных типов со знаком: подписанный char, подписанный короткий, подписанный int, подписанный длинный, подписанный длинный длинныйUCHAR_MAX
, USHRT_MAX
, UINT_MAX
, ULONG_MAX
, ULLONG_MAX
(C99) – максимально возможное значение беззнаковых целых типов: unsigned char, unsigned short, unsigned int, unsigned long, unsigned long longCHAR_MIN
– минимально возможное значение charCHAR_MAX
– максимально возможное значение charMB_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
– количество десятичных цифр, которые могут быть представлены без потери точности с помощью чисел с плавающей запятой, двойной или длинной двойной, соответственно.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 в этой степени является нормализованным числом с плавающей запятой, двойным или длинным двойным числом соответственноFLT_MAX_EXP
, DBL_MAX_EXP
, LDBL_MAX_EXP
– максимальное положительное целое число, FLT_RADIX
возведенное в степень на единицу меньше этого числа, представляет собой нормализованное число с плавающей запятой, двойное или длинное двойное число соответственно.FLT_MAX_10_EXP
, DBL_MAX_10_EXP
, LDBL_MAX_10_EXP
– максимальное положительное целое число, такое, что 10 в этой степени является нормализованным числом с плавающей запятой, двойным или длинным двойным числом соответственно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 { имя символа [ 20 ]; международный день ; внутренний месяц ; int год ; }; структура дня рождения Джона ;
Расположение структуры в памяти — это проблема реализации языка для каждой платформы с некоторыми ограничениями. Адрес памяти первого члена должен совпадать с адресом самой структуры. Структуры могут быть инициализированы или назначены с использованием составных литералов. Функция может напрямую возвращать структуру, хотя во время выполнения это часто неэффективно. Начиная с C99 , структура может также заканчиваться гибким элементом массива .
Структура, содержащая указатель на структуру своего типа, обычно используется для построения связанных структур данных :
структура узла { интервал val ; структура узла * следующий ; };
Для каждого типа T
, кроме типов void и function, существуют типы «массив N
элементов типа T
» . Массив — это совокупность значений одного типа, хранящихся в памяти последовательно. Массив размера N
индексируется целыми числами 0
от N−1
. Вот краткий пример:
интервал кот [ 10 ]; // массив из 10 элементов, каждый типа int
Массивы можно инициализировать с помощью составного инициализатора, но не присваивать. Массивы передаются функциям путем передачи указателя на первый элемент. Многомерные массивы определяются как «массив массива…» , и все измерения, кроме самого внешнего, должны иметь постоянный размер времени компиляции:
интервал а [ 10 ][ 8 ]; // массив из 10 элементов, каждый типа 'массив из 8 элементов int'
Каждый тип данных T
имеет соответствующий указательT
типа . Указатель — это тип данных , который содержит адрес места хранения переменной определенного типа. Они объявляются с помощью *
декларатора типа звездочки ( ), следующего за базовым типом хранилища и предшествующего имени переменной. Пробелы до или после звездочки не являются обязательными.
символ * квадрат ; длинный * круг ; интервал * овал ;
Указатели также могут быть объявлены для типов данных указателей, создавая таким образом несколько косвенных указателей, таких как char ** и int *** , включая указатели на типы массивов. Последние встречаются реже, чем массив указателей, и их синтаксис может сбивать с толку:
символ * ПК [ 10 ]; // массив из 10 элементов 'указателя на char' char ( * pa )[ 10 ]; // указатель на массив из 10 элементов char
Элементу pc
требуется десять блоков памяти размера указателяchar
(обычно 40 или 80 байт на обычных платформах), но элемент pa
представляет собой только один указатель (размером 4 или 8 байт), а данные, на которые он ссылается, представляют собой массив из десяти байтов. ( ).sizeof *pa == 10
Тип объединения — это специальная конструкция, которая разрешает доступ к одному и тому же блоку памяти, используя различные описания типов. Например, можно объявить объединение типов данных, позволяющее читать одни и те же данные как целое число, число с плавающей запятой или любой другой тип, объявленный пользователем:
союз { интервал я ; плавать f ; структура { беззнаковое целое число и ; двойной д ; } с ; } ты ;
Общий размер 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, который должен удовлетворять const-correctness . Остальные квалификаторы используются для низкоуровневого программирования и, хотя там широко используются, типичными программистами они используются редко. [ нужна цитата ]