Типы данных в Си - класс данных, значения которых имеют схожие характеристики. Тип определяет внутреннее представление данных в памяти. Самые основные типы данных: логический, целочисленный, числа с плавающей точкой, строковые, указатели.
Типизация
При динамической типизации переменная связывается с типом на момент инициализации. Получается, что переменная в разных участках кода может иметь разные типы. Динамическую типизацию поддерживают Java Script, Python, Ruby, PHP.
Статическая типизация является противоположностью динамической. При объявлении переменная получает тип, который не меняется в дальнейшем. Языки Си и Си++ являются именно такими. Этот способ наиболее удобный для написания сложного кода, а на стадии компиляции исключается много ошибок.
Языки неформально делятся на сильнотипизированный и слаботипизированный. Сильная типизация подразумевает, что компилятор выдаст ошибку при несовпадении ожидаемого и фактического типов.
x = 1 + “2”; //ошибка - нельзя прибавить к числу символьный знак
Пример слабой типизации.
x = 1 + “2”; // 3
Проверка согласования типов осуществляется системой типобезопасности. Ошибка типизации возникает, например, при попытке использовать число как функцию. Существуют нетипизированные языки. В противоположность типизированным, они позволяют осуществлять любые операции над каждым объектом.
Классы памяти
Переменные, независимо от их типа, имеют свою область видимости и время существования.
Классы памяти:
- auto;
- static;
- extern;
- register.
Все переменные в языке Си по умолчанию являются локальными. Они могут использоваться только внутри функции или блока. По завершении функции их значение уничтожается.
Статическая переменная также является локальной, но вне своего блока может иметь другое значение, а между вызовами функции значение сохраняется.
Внешняя переменная является глобальной. Она доступна в любой части кода и даже в другом файле.
Регистровая переменная рекомендует компилятору сохранять значение в оперативную память.
Спецификаторы типов данных в Си могут не указываться в таких случаях:
- Все переменные внутри блока не являются переменными, соответственно, если предполагается использование именно этого класса памяти, то спецификатор auto не указывается.
- Все функции, объявленные вне блока или функции, являются по умолчанию глобальными, поэтому спецификатор extern не обязателен.
Базовые типы
Для указания простых типов указываются спецификаторы int, char, float или double. К переменным могут подставляться модификаторы unsigned (беззнаковый), signed (знаковый), short, long, long long.
По умолчанию все числа являются знаковыми, соответственно, могут находиться в диапазоне только положительных чисел. Чтобы определить переменную типа char как знаковую, пишется signed char. Long, long long и short указывают, как много места в памяти отводится для хранения. Наибольшее - long long, наименьшее - short.
Char - самый маленький тип данных в Си. Для хранения значений выделяется всего 1 байт памяти. Переменной типа character обычно присваиваются символы, реже - цифры. Символьные значения берутся в кавычки.
char a = “a”;char b = 2;
Тип int хранит целые числа, его размер не определен - занимает до 4 байт памяти, в зависимости от архитектуры компьютера.
int a = 12;
Явное преобразование беззнаковой переменной задается так:
unsigned int a = 12;
Неявное выглядит так:
int a = 12u;
Float и double определяют числа с точкой. Числа float представляются в виде -2.3 или 3.34. Double используется для большей точности - после разделителя целой и дробной части указывается больше цифр. Этот тип занимает больше места в памяти, чем float.
double a = 12.4567;
Void имеет пустое значение. Он определяет функции, которые ничего не возвращают. С помощью этого спецификатора указывается пустое значение в аргументах методов. Указатели, которые могут принимать любой тип данных, также определяются как void.
Логический тип Bool
Применяется в проверках условий и циклах. Имеет всего два значения:
- истина;
- ложь.
Булевые значения могут преобразовываться в значение типа int. True эквивалентно единице, false - нулю. Преобразование типов предусмотрено только между bool и int, в противном случае компилятор выдаст ошибку.
int x = 123;if (x) { //Error: "Cannot implicitly convert type 'int' to 'bool'"};if (x != 0) // The C# way{}
Строки и массивы
Массивы относятся к сложными типам даным в Си. ЯП не работает со строками так же, как это делает Джаваскрипт или Руби. В Си все строки являются массивами элементов символьного значения. Строки оканчиваются нулевым байтом “ ”.
Объявление строки в Си: char b[] = { 's', 't', 'r', 'i', 'n', 'g', ' '};
Строка также объявляется в кратком виде.
char a[] = "string";a[1] === b[1]; // ‘t’
Целочисленный или символьные массивы объявляются одинаковым образом: тип данных имя переменной[размер массива].
int a[100];char b[5];
Подобный синтаксис указывает, что размер значения int не может быть больше, чем 100 символов. При создании массива бронируется некоторое количество ячеек в памяти для хранения элементов. Поэтому для обработки массивов данных с неизвестной размерностью или такой, которая будет меняться, динамически выделяется память.
Многомерные массивы объявляются так:
int a[2][3];
При создании этого массива выделяется место, в данном случае, для хранения двух вложенных массивов, которые состоят из трех элементов.
Инициализация многомерного массива:
int a[2][3] = { 1, 2, 3, 4, 5, 6 };a[1]; // [1, 2, 3]a[2]; // [4, 5, 6]
Ссылки и указатели
Указатели - главная особенность языка, они открывают широкие возможности для работы с памятью. Они передают информацию о расположении другой переменной в памяти. Перед использованием они, подобно другим переменным, объявляются в виде type *pointer. Type - тип переменной, pointer - имя указателя.
Чтобы задать указателю значение другой переменной, используются ссылки. Они передают адрес переменной и используются в качестве безопасного указателя.
int a = 2;int *pointer;pointer = &a;
Ссылки позволяют работать с указателем, как с объектом.
В данном случае &a вернет 0x7340cad2a25c, *pointer - 2, но pointer без “*” - 0x7340cad2a25c.
Изменяя значение, мы не меняем его адрес, поэтому pointer так же будет ссылаться на 0x7340cad2a25c, но при этом изменит свое значение.
Указатель на другой указатель объявляется в виде:
int w = 100;int *x = &w;int **z = &x;
Указатель на массив работает немного другим способом.
int c[5] = [1, 2, 3, 4, 5], *a;
Мы объявили массив целых чисел и указатель int a.
В данном случае указатель указывает не на сам массив, а только на его первый элемент. Таким образом,
a = c;иa = &c[0]; //1
эквивалентны между собой.
Теперь можно сослаться на третий элемент массива с помощью выражения
*(a + 3)
или
a[3];
На примере выше можно заметить, что сложение в указателях работает другим образом. К ним могут применяться различные арифметические операции.
Указатель на массив указывается так:
char (*pa)[10];
Но массив указателей выглядит так:
char *pc[10];
Структуры
Структура - такой тип данных в Си, который облегчает написание и понимание программ, помогает группировать данные.
Структура наподобие массива представляет совокупность данных, но ее элементы могут быть различного типа, а обращение производится по имени, а не по индексу.
{тип1 имя_переменной1;тип1 имя_переменной1;//другие члены данных;}
Объявление структурной переменной происходит после закрывающих фигурных скобок.
struct book{char title[20];char autor[30];double cast;} book1, book2, *ptr_bk;
Доступ к полям осуществляется с помощью оператора “.”. Чтобы обратиться к переменной title, пишем:
book1.title;
Таким образом, инициализируем переменную
book1.title = “String”;
Для обращения к указателям используется оператор “->”.
ptr_bk->cast;
или оператор ".".
(*(ptr_bk)).cast;
Вторая разновидность списков с данным - enum (перечисление). Он содержит целочисленные переменные.
enum { red, blue, green };
В примере объявлено анонимное перечисление, содержащее три члена red, blue, green. Перед обращением к элементам объявляется перечислительная переменная.
enum name1 { red, blue, red } varname;
В этом случае name1 является именем перечисления, а varname - имя переменной. В моменте создания структуры можно задать несколько переменных. Они перечисляются через запятую.
enum name1 { red, blue, red } varname1, varname2, varname3;
Доступ к членам перечисления задается при помощи оперетора ".".
varname.red = "red";
Заключение
Язык Си предоставляет большой набор типов. Они формируются с помощью соответствующих спецификаторов.
Базовые типы делятся на числовые (double, float) и знаковые (int, char). Модификаторы signed и unsigned указывают на наличие знака перед символом. Модификаторы short и long отвечают за размер ячеек в памяти для хранения значений переменных. Логический тип данных имеет два значения: true и false. Массивы, структуры относятся к сложным типам данных. Указатель работает с адресом переменной, на который указывает.
А ЧТО ВЫ ДУМАЕТЕ ОБ ЭТОМ?