Больше чем просто типы данных…
Welcome! Никак руки не доходили сделать эту статью, но теперь я её пишу.
Типы данных — одна из важнейших частей языка. Переменные, функции, поля класса и даже другие типы данных — ничто без данных. Без данных: перменная уже не хранит значение, а функция его не возвращает. Но что такое данные? Почему всё держится именно на них? Зачем данные разделяют на типы? И почему int ≠ 4 байта?
Хочешь узнать ответы? Бегом под кат!
Что такое данные?
— Что такое данные?
Казалось бы. Это все лишь вопрос. Как он связан с данными? Но всё не так просто.
Когда человек задаёт вопрос: он передаёт информацию, а другой человек получает информацию. Когда прозвучит ответ: человек задавший вопрос получит информацию, а человек ответивший на него передаст информацию.
Это называется обмен данными. Но что сдесь данные? Давайте заглянем в терминологию:
Да́нные — зарегистрированная информация; представление фактов, понятий или инструкций в форме, приемлемой для общения, интерпретации, или обработки человеком или с помощью автоматических средств (ISO/IEC/IEEE 24765-2010).
Зачем нужны данные? (Где используются данные?)
Теперь, когда мы знаем что такое данные, мы должны узнать с чем их едят.
На самом деле, данные используются везде. И сейчас, то, что ты читаешь — это данные. А на вид и не скажешь. Вы думали что данные — это единицы, да нолики? А вот и нет. Это ещё и двойки. 2 <- вот, видите? Двойка. Ладно, ладно, давайте без шуток. Данные вездесущи, а программирование — это работа с данными.
Почему всё держится именно на данных
Из прошлого предложения можно понять: «Все — есть данные». Соответсвенно на них же всё и держится. Когда мы создаём переменную: мы передаём в неё данные.
— Но что если мы передадим в переменную. Другую переменную?
— Ага, то есть, перменная это тоже данные?
Да, всё именно так.
— А что будет, если мы создадим пустую переменную? Мы же не передаём в неё данные?
Хорошо, давай попробуем.
Чему тогда равна переменная? Нулю? Пустоте(void)? А вот и нет.
Выходит в переменной есть данные. Но какие? Это мы должны спросить у конкретных типов данных. Например в int по стандарту(в C++) помещается значение одного из адрессов оперативной памяти.
— Но какого адресса? Он же не случайный?
Нет, конечно. Когда мы создаём переменную: под неё выделяется память, и значение которое было по адрессу нашей новой переменной, присваевается переменной.
— Хмм, хорошо. А что ты скажешь по поводу void? Это же пустота. В void 0 байт. Void ничему не равен.
Хах, давай проверим.
— Стоп, что? Но void же ничему не равен! Как он занимает 1 байт?
На самом деле тут нет ничего странного. Любая переменная в C++ хранится в ОЗУ. И void тоже. Соответственно, void должна что-то хранить. Но что она хранит — мы не можем узнать. Так как void хранит значение, мы можем получить адресс void переменной.
Зачем данные делят на типы?
Для начала, давайте разберёмся, что такое тип данных?
Тип данных — допустимое множество значений.
Это один из вариантов того, как можно описать данный термин. Этот вариант хорошо подходит для этой статьи, because я расказываю только о простых типах данных, а они как раз таки и являются множеством допустимых значений.
Для пользовательских типов данный (ака классы) лучше использовать этот вариант.
Тип данных — класс данных, характеризуемый членами класса и операциями, которые могут быть к ним применены.
— Хорошо. Мы знаем что такое тип данных. А зачем эти типы нужны?
Ответ на данный вопрос есть. Принадлежит он, конечно, не мне.
Наверное, вам известно, что в памяти компьютера все числа хранятся в виде последовательности 0 и 1. Так вот, для того чтобы записать любое число только ноликами и единичками существуют определённые правила. Эти правила довольно сильно отличаются для целых чисел и для вещественных чисел. В памяти компьютера числа «3» и «3.0» будут записаны совершенно по-разному.
int ≠ 4 байта. Почему?
Ответ на этот вопрос предельно прост. Размер и числовой диапазон типов данных задаётся процессором. Соответсвенно размер int зависит от процессора в комьютере. Для 16 битный процессоров — это 2 байта (short int), для 32 и 64 битных — 4 байта (long int). Правда это не всегда так. «Но почему?», — спросите вы меня. А ответа я не дам. Попробуйте найти ответ сами 😉
Why does an int take up 4 bytes in c or any other language? [duplicate]
This is a bit of a general question and not completely related to the c programming language but it’s what I’m on studying at the moment.
Why does an integer take up 4 bytes or How ever many bytes dependant on the system?
Why does it not take up 1 byte per integer?
For example why does the following take up 8 bytes:
![]()
![]()
4 Answers 4
I am not sure whether you are asking why int objects have fixed sizes instead of variable sizes or whether you are asking why int objects have the fixed sizes they do. This answers the former.
We do not want the basic types to have variable lengths. That makes it very complicated to work with them.
We want them to have fixed lengths, because then it is much easier to generate instructions to operate on them. Also, the operations will be faster.
If the size of an int were variable, consider what happens when you do:
When b is first assigned, only one byte is needed. Then, when the addition is performed, the compiler needs more space. But b might have neighbors in memory, so the compiler cannot just grow it in place. It has to release the old memory and allocate new memory somewhere.
Then, when we do the scanf , the compiler does not know how much data is coming. scanf will have to do some very complicated work to grow b over and over again as it reads more digits. And, when it is done, how does it let you know where the new b is? The compiler has to have some mechanism to update the location for b . This is hard and complicated and will cause additional problems.
In contrast, if b has a fixed size of four bytes, this is easy. For the assignment, write 3 to b . For the addition, add 100000 to the value in b and write the result to b . For the scanf , pass the address of b to scanf and let it write the new value to b . This is easy.
Почему числовые типы данных в C имеют такой размер?
Каждый программист, которому приходилось писать на C или C-подобных языках, наверняка сталкивался с тем, что размер переменных одного и того же типа на разных машинах может быть различным. Немного разобравшись в этом вопросе многие успокаивались, узнав, что, действительно, размер тех же указателей зависит от реализации компилятора и разрядности машины. Но почему? И почему именно столько байт занимает указатель в том или ином случае?
Ответ дает книга «Computer Science: A Programmer’s Perspective» за авторством Рендела Брайанта и Девида О’Халарона.
Компьютеры и компиляторы поддерживают различные типы для хранения данных — в частности, целых чисел и чисел с плавающей запятой. Каждый отдельный тип может отличаться от других, например, способом представления хранимой информации и объемом занимаемой памяти.
Язык программирования C поддерживает различные типы для хранения как целых чисел, так и чисел с плавающей запятой. Символьный тип char, обычно используемый для представления отдельного символа, может также хранить беззнаковые числа. Более распространенный для этих целей int образует целое семейство типов за счет возможных префиксов: short, long и long long — и все различных размеров. В таблице ниже указано, сколько байт занимает в памяти каждый тип в зависимости от разрядности системы.

Можно заметить, что размер зависит от разрядности машины (строго говоря, еще и от реализации компилятора). Но почему размеры именно такие? Обратите внимание: short, «обычный» int и long long всегда имеют постоянные размеры в 2, 4 и 8 байт соответственно. Префикс long же означает, что для хранения данной переменной нужно использовать одно машинное слово — 4 байта на 32-битной машине и 8 байт на 64-битной соответственно.
Та же ситуация и с указателями (char*): в системе для хранения адресов используется все то же одно машинное слово, размер которого зависит от разрядности.
Некоторые особенности типа int в C++
Операционная система: «Windows 7 Профессиональная» (SP1), 64-разрядная. Компилятор среды «Visual Studio Community 2017».
int — основной в языке тип целого числа со знаком. signed int — синоним int.
В 8-разрядных и 16-разрядных системах int занимал 2 байта, в современных 32- и 64-разрядных системах занимает обычно 4 байта. Предполагая, что в байте 8 бит (существуют системы, в которых в байте другое количество бит), получаем, что 4 байта — это 32 бита.
Если в программе мы работаем только с положительными целыми числами, то можно использовать тип unsigned int (целое без знака). При этом в 4 байта можно поместить 2^n (два в степени n ) значений, где n = 32 (количество разрядов или битов). Из-за того, что в диапазон представляемых значений необходимо включить число 0 (ноль), диапазон представляемых типом unsigned int значений будет не 0..2^n , как можно было бы подумать, а
0..(2^n — 1)
или 0..(2^32 — 1)
или 0..(4’294’967’296 — 1)
или 0..4’294’967’295
(границы входят в диапазон представляемых значений)
Теперь рассмотрим случай, когда в программе мы работаем и с положительными целыми числами, и с отрицательными целыми числами. Для этого можно использовать тип int (или синоним signed int, как уже упоминалось выше). Теперь в 4 байта необходимо помещать и отрицательные, и положительные целые числа.
Самый старший бит (тот, который находится слева) отводится для хранения знака числа ( 0 — число положительное, 1 — число отрицательное). Значит, у нас остается для представления чисел 31 бит: 31 бит для отрицательных чисел и 31 бит для положительных чисел. То есть мы можем представить с помощью типа int 2^(n-1) отрицательных чисел и 2^(n-1) положительных чисел ( n = 32 , как мы помним). Однако, как и для типа unsigned int, для типа int требуется куда-то включить число 0 (ноль). Его включают в положительные числа, хотя оно, как мы помним из математики, не является ни положительным, ни отрицательным. Исходя из этого, диапазон представляемых типом int отрицательных чисел будет -2^(n-1)..-1 , а диапазон представляемых типом int положительных чисел будет 0..2^(n-1)-1 , то есть
-2^(n-1)..-1 и 0..2^(n-1)-1
или -2^(n-1)..2^(n-1)-1
или -2^(32-1)..2^(32-1)-1
или -2^31..2^31-1
или -2’147’483’648..2’147’483’648-1
или -2’147’483’648..2’147’483’647
(границы входят в диапазон представляемых значений)
Вот почему компилятор позволяет такой диапазон представляемых типом int значений, несмотря на то, что в стандарте языка Си установлен диапазон значений, определяемый по формуле -(2^(n-1)-1)..2^(n-1)-1 , то есть
-2’147’483’64 7 ..2’147’483’647 (согласно стандарта)
-2’147’483’64 8 ..2’147’483’647 (фактическая реализация)
Кстати, в тексте программ позволяется использовать апострофы в качестве разделителей прямо в числовых литералах, что, по-моему, очень удобно. Например:
Перейдем к интересным неочевидным штукам. Например, в результате выражения
переменной b присваивается не указанное значение, а это же значение, только со знаком минус: -2’147’483’648 . Это происходит потому, что значение справа от знака присваивания считается компилятором значением типа unsigned long, а не значением типа int (как должно быть по умолчанию для числового литерала), так как оно выходит за рамки диапазона представляемых типом int значений. Далее компилятор пытается записать в переменную типа int значение, которое туда не может поместиться, и бит значения числа записывается в самый старший бит, который предназначен для хранения знака числа, а не битов значения числа.
Еще один казус. Попытка использовать числовой литерал с наименьшим возможным для типа int значением -2’147’483’648 приведет к ошибке C4146:
Почему это происходит? Потому что компилятор не включает знак минуса в понятие числового литерала, то есть сначала рассматривается значение 2’147’483’648 , которое не может быть значением типа int, как уже указывалось выше, и считается значением типа unsigned long (целое без знака). После этого компилятор пытается применить унарную операцию отрицания к целому без знака, которое не может быть отрицательным, что логично является ошибкой.
Что делать? Рекомендуется применять вместо значения -2’147’483’648 встроенную константу INT_MIN с этим значением, определенную в заголовочном файле limits.h , либо писать вот так: