Типы данных

Последнее обновление: 24.01.2024

Переменная имеет определенный тип. И этот тип определяет, какие значения может иметь переменная и сколько байт в памяти она будет занимать. В Си определены следующие базовые типы данных:

  • char: представляет один символ. Занимает в памяти 1 байт (8 бит). Может хранить любое значение из диапазона от -128 до 127

  • unsigned char: представляет один символ. Занимает в памяти 1 байт (8 бит). Может хранить любой значение из диапазона от 0 до 255

  • signed char: то же самое, что и char

  • short: представляет целое число в диапазоне от –32768 до 32767. Занимает в памяти 2 байта (16 бит).

    Имеет псевдонимы short int, signed short и signed short int.

  • unsigned short: представляет целое число в диапазоне от 0 до 65535. Занимает в памяти 2 байта (16 бит).

    Имеет псевдоним unsigned short int.

  • int: представляет целое число. В зависимости от архитектуры процессора может занимать 2 байта (16 бит) или 4 байта (32 бита). Если брать основные платформы - 64-разрядные Windows, Linux (вместе с Android) и MacOS, то размер int составляет 4 байта. Диапазон предельных значений соответственно также может варьироваться от –32768 до 32767 (при 2 байтах) или от −2 147 483 648 до 2 147 483 647 (при 4 байтах) и выше.

    Имеет псевдонимы signed int и signed

  • unsigned int: представляет положительное целое число. В зависимости от архитектуры процессора может занимать 2 байта (16 бит) или 4 байта (32 бита), и из-за этого диапазон предельных значений может меняться: от 0 до 65535 (для 2 байт), либо от 0 до 4 294 967 295 (для 4 байт).

    Имеет псевдоним unsigned: то же самое, что и unsigned int

  • long: представляет целое число и занимает в памяти 4 байта (32 бита) или 8 байт (64 бита). В зависимости от размера может находиться в в диапазоне от -2 147 483 648 до 2 147 483 647 (4 байта), либо в диапазоне от -9223372036854775807 до +9 223 372 036 854 775 807 (8 байт)

    Если брать распростренные платформы, то на 64-разрядном Windows long занимает 4 байта, а на 64-разрядных Linux/MacOS - 8 байт.

    Имеет псевдонимы long int, signed long int и signed long.

  • unsigned long: представляет целое число и занимает в памяти 4 байта (32 бита) или 8 байт (64 бита). В зависимости от размера может находиться в в диапазоне от 0 до 4 294 967 295 (4 байта) или в диапазоне от 0 до 18 446 744 073 709 551 615 (8 байт).

    Имеет псевдоним unsigned long int.

  • long long: представляет целое число в диапазоне от -9223372036854775807 до +9 223 372 036 854 775 807. Занимает в памяти, как правило, 8 байт (64 бита).

    Имеет псевдонимы long long int, signed long long int и signed long long.

  • unsigned long long: представляет целое число в диапазоне от 0 до 18 446 744 073 709 551 615. Занимает в памяти, как правило, 8 байт (64 бита).

    Имеет псевдоним unsigned long long int.

  • float: представляет вещественное число одинарной точности с плавающей точкой в диапазоне +/- 3.4E-38 до 3.4E+38. В памяти занимает 4 байта (32 бита)

  • double: представляет вещественное число двойной точности с плавающей точкой в диапазоне +/- 1.7E-308 до 1.7E+308. В памяти занимает 8 байт (64 бита)

  • long double: представляет вещественное число двойной точности с плавающей точкой в диапазоне +/- 3.4E-4932 до 1.1E+4932. В памяти занимает 10 байт (80 бит). На некоторых системах может занимать 96 и 128 бит.

  • void: тип без значения

Целочисленные типы

Наиболее распространенным целочисленным типом является int (имеет псевдонимы signed int и signed), представляет целое число со знаком и обычно занимает 4 байта. Переменной такого типа можно передать целое число:

int age = 38;
signed int number = 2;
signed temps = -3;

Для определения переменной некоторого типа можно использовать все псевдонимы этого типа. Так, в примере выше определяются три переменных типа int, хотя в каждом случае используются разные псевдонимы типа: int, signed int и signed.

Суффиксы целочисленных типов

Стоит учитывать, что любое десятичное число рассматривается по умолчанию как значение типов int/long int/long long int (в зависимости от размера) и при присвоении переменным другим типов будет выполняться преобразование. Чтобы указать, что число явным образом представляет определенный тип, к числу добавляется определенный суффикс:

Тип

Суффикс

unsigned int, unsigned long int и unsigned long long int

u или U

unsigned long int и unsigned long long int

ul или UL

long long int

ll или LL

unsigned long long int

ull или ULL

Как видно, не для всех типов есть отдельные суффиксы. И для некоторых типов можно применять несколько суффиксов. Применим суффикс. Например, если надо хранить только положительные числа, то можно взять тип unsigned int. Для определения чисел этого типа применяется суффикс u или U:

#include <stdio.h>
 
int main(void)
{
    unsigned number1 = 4294967294u; 
    unsigned int number2 = 22U;  
    printf("number1 = %u\n", number1);
    printf("number2 = %u\n", number2);
    return 0;
}

При выводе таких чисел на консоль применяется спецификатор %u.

Стоит отметить, что мы могли бы присвоить переменной число и без суффикса и получили бы тот же самый результат:

unsigned number1 = 4294967294;  // без суффикса u
unsigned int number2 = 22;      // без суффикса u
printf("number1 = %u\n", number1);
printf("number2 = %u\n", number2);

Зачем же нужен данный суффикс? Без этого суффикса десятичное число рассматривается как значение типов int/long int/long long int и при присвоении переменной типа unsigned int выполняется преобразование. Используя суффикс, мы можем избежать ненужного преобразования.

Пример определения данных других типов:

#include <stdio.h>
 
int main(void)
{
    unsigned short number1 = 1u; 
    unsigned short int number2 = 2u;  
    short number3 = 3; 
    short int number4 = -4;  
    signed short number5 = 5;  
    signed short int number6 = -6;  

    long number7 = -2147483648l;
    long int number8 = -2147483648L;
    signed long number9 = 2147483647l;
    signed long int number10 = 2147483647L;

    unsigned long number11 = 4294967295ul;
    unsigned long int number12 = 4294967295UL;

    long long number13 = -9223372036854775807ll;
    long long int number14 = 9223372036854775807ll;
    signed long long number15 = -9223372036854775807LL;
    signed long long int number16 = 9223372036854775807LL;

    unsigned long long number17 = 18446744073709551615ull;
    unsigned long long int number18 = 18446744073709551615ULL;
   
    printf("number1 = %hu\n", number1);
    printf("number2 = %hu\n", number2);
    printf("number3 = %d\n", number3);
    printf("number4 = %d\n", number4);
    printf("number5 = %d\n", number5);
    printf("number6 = %d\n", number6);
    printf("number7 = %ld\n", number7);
    printf("number8 = %ld\n", number8);
    printf("number9 = %ld\n", number9);
    printf("number10 = %ld\n", number10);
    printf("number11 = %lu\n", number11);
    printf("number12 = %lu\n", number12);
    printf("number13 = %lld\n", number13);
    printf("number14 = %lld\n", number14);
    printf("number15 = %lld\n", number15);
    printf("number16 = %lld\n", number16);
    printf("number17 = %llu\n", number17);
    printf("number18 = %llu\n", number18);
    return 0;
}

Обратите внимание на спецификатор, который используется для вывода числа на консоль в функции printf():

Тип

Спецификатор

unsigned short

hu

long

ld

unsigned long

lu

long long

lld

unsigned long long

llu

Определение чисел в различных системах

Си позволяет определять числа в разных числовых системых. Числа в двоичной системе начинаются с символов 0b, после которых идет набор 1 и 0, которые представляют число. Восьмеричные числа начинаются с числа 0, за которым могут идти цифры от 0 до 7. Щестнадцатеричные числа начинаются с 0x или 0X, за которыми следуют шестнадцатеричные цифры от 0 до 9 и от A до F. Например:

#include <stdio.h>
 
int main(void)
{
    int code1 = 0b1011;     // двоичная система - число 11
    int code2 = 013;        // восьмеричная система - число 11
    int code3 = 11;        // десятичная система - число 11
    int code4 = 0xB;        // шестнадцатеричная система - число 11
    printf("code1 = %d\n", code1);    //  code1 = 11
    printf("code2 = %d\n", code2);    //  code2 = 11
    printf("code3 = %d\n", code3);    //  code3 = 11
    printf("code4 = %d\n", code4);    //  code4 = 11
    return 0;
}

В данном случае определены четыре переменных, но каждая из них хранит одно и то же число - 11, записанное в разных системах исчисления.

Числа с плавающей точкой

Числа с плавающей точкой представлены тремя типами: float, double, long double. В качестве разделителя между целой и дробной частями применяется точка. По умолчанию все дробные числа представляют тип double, который занимает 8 байт:

#include <stdio.h>
 
int main(void)
{
    double number = 3.14159;
    printf("number = %f\n", number);
    return 0;
}

Для вывода значения double на консоль используется спецификаторы f и lf. Чтобы указать, что число представляет тип float, применяется суффикс f, а для long double - суффикс l:

#include <stdio.h>
 
int main(void)
{
    float number1 = 3.14f;
    long double number2 = 123456.789l;
    printf("number1 = %f\n", number1);
    printf("number2 = %Lf\n", number2);
    return 0;
}

Стоит отметить, что для вывода данных типа long double на консоль применяется спецификатор Lf, однако на некоторых платформах он может работать некорректно, например, показывать 0.

Символы

Переменным типа char можно присвоить один символ в одинарных кавычках:

char letter = 'A';

Здесь определяется переменная letter, которая хранит символ 'A'. Однако в реальности переменная типа char хранит число. И когда переменной присваивается символ, она получает числовой код этого символа из таблицы, которая сопоставляет числовые коды и символы. Наиболее распространена таблица ASCII. Она сопоставляет символы с числами от 0 до 127. Например, возьмем выше определенную переменную letter и выведем ее содержимое на консоль:

#include <stdio.h>
 
int main(void)
{
    char letter = 'A';
    printf("letter: %d \n", letter);    // letter: 65
    printf("letter: %c \n", letter);    // letter: A
    return 0;
}

Числовой код символа 'A' в таблице ASCII равен 65. Для наглядности в программе два раза выводим значение переменной letter. Но в первом случае используем спецификатор %d для вывода числового кода символа, а во втором случае применяется спецификатор %c, который позволяет вывести на консоль сам символ. То есть при выполнении программа выведет на консоль:

letter: 65
letter: A

Вместо символа в одинарных кавычках мы могли бы присвоить напрямую числовой код:

#include <stdio.h>
 
int main(void)
{
    char letter = 65;
    printf("letter: %d \n", letter);    // letter: 65
    printf("letter: %c \n", letter);    // letter: A
    return 0;
}

И мы получили бы тот же самый результат.

typedef

Оператор typedef позволяет определить для определенного типа псевдоним. Это может потребоваться, например, когда название некоторого типа довольно большое, и мы хотим его сократить.

Общая форма оператора

typedef существующий_тип псевдоним

Например, зададим для типа unsigned char псевдоним BYTE:

typedef unsigned char BYTE;

И мы сможем использовать этот тип как и любой другой:

#include <stdio.h>

typedef unsigned char BYTE;

int main(void)
{
	BYTE byte = 22;
    printf("byte = %d", byte);
}

Размер типов данных

В выше приведенном списке для каждого типа указан размер, который он занимает в памяти. Однако стоит отметить, что предельные размеры для типов разработчики компиляторов могут выбирать самостоятельно, исходя из аппаратных возможностей компьютера. Стандарт устанавливает лишь минимальные значения, которые должны быть. Например, для типов int и short минимальное значение - 16 бит, для типа long - 32 бита. При этом размер типа long должен быть не меньше размера типа int, а размер типа int - не меньше размера типа short. Но в целом для типов используются те размеры, которые указаны выше при описании типов данных.

Однако бывают ситуации, когда необходимо точно знать размер определенного типа. И для этого в C есть оператор sizeof(), который возвращает размер памяти в байтах, которую занимает переменная:

#include <stdio.h>

int main(void)
{
	int number = 2;
	printf("sizeof(number) = %d \n", sizeof(number));
	return 0;
}

Консольный вывод:

sizeof(number) = 4

При этом при определении переменных важно понимать, что значение переменной не должно выходить за те пределы, которые очерчены для ее типа. Например:

unsigned short int number = -65535;

Компилятор GCC при компиляции программы с этой строкой выдаст ошибку о том, что значение -65535 не входит в диапазон допустимых значений для типа unsigned short int.

Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850