Урок посвящен типам в языках программирования и описанию видов типизации: статической/динамической, сильной/слабой и явной/неявной.
- Типы в языках программирования
- Статическая и динамическая типизация
- Сильная и слабая типизация
- Явная и неявная типизация
Типы в языках программирования
Для начала определимся, что такое типы данных. Под типом данных можно понимать некоторое множество значений и набор операций, которые могут быть применены к ним.
Если вы уже знакомы с каким-нибудь языком программирования, то наверняка в нем вам встречался такой тип как “целое число”, чаще всего его называют int
. Как правило, значения этого типа можно складывать, вычитать и умножать. В зависимости от языка программирования, могут быть допустимы и другие операции. Но есть и такие операции, которые нельзя выполнять над целыми числами, например, деление: если разделить 5 на 3, мы не получим целое число, точно так же нельзя применять к ним операцию извлечения корня.
Таким образом, тип данных определяет не только то, что можно делать с элементами типа, но и что нельзя.
То, как язык работает с типами данных и какие возможности предоставляет (или не предоставляет) определяет система типов.
В некоторых языках программирования система типов используется только для контроля допустимости тех или иных операций, да и то, границы этого контроля довольно слабые. В них, например, вы можете применить операцию сложения к числу и строке (то есть складывать число со строкой) и т. п. В качестве примера такого языка можно привести C. Справедливости ради стоит заметить, что на сегодняшний день для языка C на уровне компилятора (например, GCC или CLang) выполняется большое количество проверок. Поэтому, если вы к строке прибавите число, вам, скорее всего, на это укажут, но сама возможность остается.
Существуют языки программирования, у которых система типов очень мощная. В ней, программу, по сути, можно воспринимать как некоторое математическое утверждение, которое выражено через типы, а компиляцию – как процесс доказательства этого утверждения. И если программа скомпилировалась, то, значит, утверждение доказано. К таким языкам можно отнести Haskell.
У C# довольно мощная система типов, которая позволяет представлять сложные конструкции, она более мощная, чем у C, но не такая, как у Haskell.
В зависимости от принятой системы типов языки программирования делят на следующие группы:
- со статической и динамической типизацией;
- с сильной и слабой типизацией;
- с явной и неявной типизацией.
Такая классификация не является исчерпывающей, но она дает некоторое представление о возможностях языков с точки зрения типов.
Статическая и динамическая типизация
В языках со статической типизацией тип переменной, аргумента функции или возвращаемого значения, определяется в момент объявления и в процессе работы программы не меняется.
Примеры языков со статической типизацией: C#, Java, C, C++.
Пример кода объявления переменной:
double value = 0.123;
Обратите внимание, что перед именем переменной стоит ее тип – double
. Есть языки, в которых тип можно явно не указывать, но при этом, они все равно остаются языками со статической типизацией, в них тип выводится на этапе компиляции, C# поддерживает такой подход:
var value = 0.123;
Динамическая типизация предполагает определение типа переменной во время выполнения программы, такой подход чаще всего встречается среди интерпретируемых языков.
Примеры языков с динамической типизацией: Python, PHP. Пример кода на Python:
value = 0.123
В таких языках используется подход, который называется “утиная типизация” (duck typing). Формула такая: “если крякает как утка, значит, это утка”, если её применить к переменной, то это может звучать как “если переменная связана со значением, которое ведет себя как целое число, значит, тип переменной – это “целое число” (int)”.
Рассмотрим пример:
a = 123 a = "hello"
Эти две строчки являются валидными (правильными, приемлемыми) для языка Python. Происходит следующее: после выполнения первой строки, в памяти создается некоторая область, в которой будет храниться число 123, эта область имеет определенный адрес и с ней связана переменная a
. Во второй строке будет создана другая область памяти со своим адресом для хранения строки “hello”, при этом метка a
будет отвязана от первой и привязана ко второй. Метку a
можно воспринимать как наклейку, которую можно переклеивать с ящика на ящик. Когда выполняется первая строчка, интерпретатор языка Python понимает, что это целое число, и для переменной a
фиксируется соответствующий тип. Но это не навсегда! После выполнения второй строки тип переменной изменится на str
– строковый тип.
В языках со статической типизацией такого не происходит. Если в процессе компиляции было определено, что переменная имеет тип int
,то строковое значение в нее уже не записать.
Сильная и слабая типизация
В языках с сильной типизацией операции над значениями и присваивания можно производить только над переменными одного типа. То есть числа складывать только с числами, строки со строками и т.д.
Иногда приведение одного типа к другому может выполняться неявно для ряда случаев, например, приведение типа int
к типу double
:
int v1 = 4; double v2 = v1 + 0.123;
В этом случае, при выполнении второй строки вначале будет выполнено приведение переменной v1
к типу double
, а после сложения значения v1
и числа 0.123.
А вот такой код в C# приведет к ошибке компиляции:
double v2 = "4" + 0.123;
так как “4” – это значение строкового типа, а 0.123 имеет тип double
.
К языкам с сильной типизацией относятся C#, Java, Python.
В языках со слабой типизацией таких ограничений нет, например, на C вы можете написать следующее:
char* str = "hello"; int value = str + 3;
В результате код скомпилируется, и если его запустить, то в переменной value
будет лежать некоторое численное значение.
Явная и неявная типизация
Явная типизация предполагает явное указание типа переменной:
int value = 1;
В этом примере мы объявляем переменную value
типа int
и явно это указываем.
В языке с неявной типизацией этого делать не нужно, пример на Python:
value = 1
Какой тип типизации (явный или неявный) используется, не зависит от того компилируемый этот язык или интерпретируемый, это определяется дизайном языка. Например, в C# вы можете использовать оба подхода, в первом случае код будет выглядеть так:
double value = 0.123;
Во втором так:
var value = 0.123;
Если Вы хотите больше узнать про язык C#, приглашаем Вас на наш курс “C#. Базовый уровень“.