В языке программирования 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]Фактический размер целочисленных типов зависит от реализации. Стандарт требует только соотношения размеров между типами данных и минимальных размеров для каждого типа данных:
Требования к отношению заключаются в том, что long long
не меньше long
, которое не меньше int
, которое не меньше short
. Поскольку char
размер всегда является минимальным поддерживаемым типом данных, никакие другие типы данных (кроме битовых полей ) не могут быть меньше.
Минимальный размер для char
— 8 бит, минимальный размер для short
и int
— 16 бит, ибо long
он 32 бита и long long
должен содержать не менее 64 бит.
Тип 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 соответственно.float
double
Стандарт C99 включает новые реальные типы с плавающей запятой float_t
и double_t
, определенные в <math.h>
. Они соответствуют типам, используемым для промежуточных результатов выражений с плавающей запятой, когда FLT_EVAL_METHOD
равно 0, 1 или 2. Эти типы могут быть шире, чем long double
.
В C99 также добавлены сложные типы: float _Complex
, double _Complex
, long double _Complex
. В C11 добавлены мнимые типы (описанные в информативном приложении к C99): float _Imaginary
, double _Imaginary
, long double _Imaginary
. Включение заголовка <complex.h>
позволяет получить доступ ко всем этим типам с помощью complex
и imaginary
соответственно.
В 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 года [update]и C11 , в стандарте C есть четыре квалификатора типа: const
( C89 ), volatile
( C89 ), restrict
( C99 ) и _Atomic
( C11 ) — последний имеет частное имя, чтобы избежать конфликта с именами пользователей, [14] но чем больше atomic
можно использовать обычное имя , если <stdatomic.h>
включен заголовок. Из них, const
безусловно, самый известный и наиболее используемый, появляющийся в стандартной библиотеке и встречающийся при любом значительном использовании языка C, который должен удовлетворять const-correctness . Остальные квалификаторы используются для низкоуровневого программирования и, хотя там широко используются, типичными программистами они используются редко. [ нужна цитата ]