Типы данных в языке си

Форматированный ввод

Рассмотрим форматированный ввод функцией scanf.
Функция принимает строку формата ввода (она похожа на строку формата printf) и адреса, по которым необходимо записать считанные данные. Возвращает
количество успешно проинициализированных аргументов.
Формат спецификатора ввода

Как и в printf, ширина, заданная символом * ожидает аргумента, который будт задавать ширину. Флаг длина совпадает с таким флагом функции printf.

Примеры

#include <stdio.h>
#include <conio.h>

void main() {
	int year, month, day;
	char buffer;
	int count;
	//Требует форматированного ввода, например 2013:12:12
	printf("Enter data like x:x:x = ");
	scanf("%d:%d:%d", &year, &month, &day);
	printf("year = %d\nmonth = %d, day = %d\n", year, month, day);
	//Считываем строку, не более 127 символов. При считывании в массив писать & не надо,
	//так как массив подменяется указателем
	printf("Enter string = ");
	scanf("%127s", buffer);
	printf("%s", buffer);
	getch();
}

Кроме функций scanf и printf есть ещё ряд функций, которые позволяют получать вводимые данные

int getch()

#include <stdio.h>
#include <conio.h>

void main() {
	char c = 0;
	do {
		c = getch();
		printf("%c", c);
	} while (c != 'q');
}

char * fgets ( char * str, int num, FILE * stream ) — функция позволяет считывать строку с пробельными символами.
Несмотря на то, что она работает с файлом, можно с её помощью считывать и из стандартного потока ввода. Её преимущество относительно gets в
том, что она позволяет указать максимальный размер считываемой строки и заканчивает строку терминальным символом.

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

void main() {
	char buffer;
	//Считываем из стандартного потока ввода
	fgets(buffer, 127, stdin);
	printf("%s", buffer);
	//Этим можно заменить ожидание ввода символа
	scanf("1");
}

Это не полный набор различных функций символьного ввода и вывода.
Таких функций море, но очень многие из них небезопасны, поэтому перед использованием внимательно читайте документацию.

Примеры переполнения

Рассмотрим переменную unsigned, которая состоит из 4-х бит. Любое из двоичных чисел, перечисленных в таблице выше, поместится внутри этой переменной.

«Но что произойдет, если мы попытаемся присвоить значение, которое занимает больше 4-х бит?». Правильно! Переполнение. Наша переменная будет хранить только 4 наименее значимых (те, что справа) бита, все остальные — потеряются.

Например, если мы попытаемся поместить число 21 в нашу 4-битную переменную:

Десятичная система Двоичная система
21 10101

Число 21 занимает 5 бит (10101). 4 бита справа (0101) поместятся в переменную, а крайний левый бит (1) просто потеряется. Т.е. наша переменная будет содержать 0101, что равно 101 (нуль спереди не считается), а это уже число 5, а не 21.

Примечание: О конвертации чисел из двоичной системы в десятичную и наоборот будет отдельный урок, где мы всё детально рассмотрим и обсудим.

Теперь рассмотрим пример в коде (тип short занимает 16 бит):

#include <iostream>

int main()
{
unsigned short x = 65535; // наибольшее значение, которое может хранить 16-битная unsigned переменная
std::cout << «x was: » << x << std::endl;
x = x + 1; // 65536 — это число больше максимально допустимого числа из диапазона допустимых значений. Следовательно, произойдет переполнение, так как переменнная x не может хранить 17 бит
std::cout << «x is now: » << x << std::endl;
return 0;
}

1
2
3
4
5
6
7
8
9
10

#include <iostream>

intmain()

{

unsignedshortx=65535;// наибольшее значение, которое может хранить 16-битная unsigned переменная

std::cout<<«x was: «<<x<<std::endl;

x=x+1;// 65536 — это число больше максимально допустимого числа из диапазона допустимых значений. Следовательно, произойдет переполнение, так как переменнная x не может хранить 17 бит

std::cout<<«x is now: «<<x<<std::endl;

return;

}

Результат выполнения программы:

Что случилось? Произошло переполнение, так как мы попытались присвоить переменной  значение больше, чем она способна в себе хранить.

Для тех, кто хочет знать больше: Число 65 535 в двоичной системе счисления представлено как 1111 1111 1111 1111. 65 535 — это наибольшее число, которое может хранить 2-байтовая (16 бит) целочисленная переменная без знака, так как это число использует все 16 бит. Когда мы добавляем 1, то получаем число 65 536. Число 65 536 представлено в двоичной системе как 1 0000 0000 0000 0000, и занимает 17 бит! Следовательно, самый главный бит (которым является 1) теряется, а все 16 бит справа — остаются. Комбинация 0000 0000 0000 0000 соответствует десятичному 0, что и является нашим результатом.

Аналогичным образом, мы получим переполнение, использовав число меньше минимального из диапазона допустимых значений:

#include <iostream>

int main()
{
unsigned short x = 0; // наименьшее значение, которое 2-байтовая unsigned переменная может хранить
std::cout << «x was: » << x << std::endl;
x = x — 1; // переполнение!
std::cout << «x is now: » << x << std::endl;
return 0;
}

1
2
3
4
5
6
7
8
9
10

#include <iostream>

intmain()

{

unsignedshortx=;// наименьшее значение, которое 2-байтовая unsigned переменная может хранить

std::cout<<«x was: «<<x<<std::endl;

x=x-1;// переполнение!

std::cout<<«x is now: «<<x<<std::endl;

return;

}

Результат выполнения программы:

Переполнение приводит к потере информации, а это никогда не приветствуется. Если есть хоть малейшее подозрение или предположение, что значением переменной может быть число, которое находится вне диапазона допустимых значений используемого типа данных — используйте тип данных побольше!

Правило: Никогда не допускайте возникновения переполнения в ваших программах!

Деление целочисленных переменных

В языке C++ при делении двух целых чисел, где результатом является другое целое число, всё довольно предсказуемо:

#include <iostream>

int main()
{
std::cout << 20 / 4 << std::endl;
return 0;
}

1
2
3
4
5
6
7

#include <iostream>

intmain()

{

std::cout<<204<<std::endl;

return;

}

Результат:

Но что произойдет, если в результате деления двух целых чисел мы получим дробное число? Например:

#include <iostream>

int main()
{
std::cout << 8 / 5 << std::endl;
return 0;
}

1
2
3
4
5
6
7

#include <iostream>

intmain()

{

std::cout<<85<<std::endl;

return;

}

Результат:

В языке C++ при делении целых чисел результатом всегда будет другое целое число. А такие числа не могут иметь дробь (она просто отбрасывается, не округляется!).

Рассмотрим детально вышеприведенный пример: . Но как мы уже знаем, при делении целых чисел результатом является другое целое число. Таким образом, дробная часть () значения отбрасывается и остается .

Правило: Будьте осторожны при делении целых чисел, так как любая дробная часть всегда отбрасывается.

Приоритет и ассоциативность операторовOperator precedence and associativity

В следующем списке перечислены арифметические операторы в порядке убывания приоритета:The following list orders arithmetic operators starting from the highest precedence to the lowest:

  • Постфиксный инкремент и декремент Postfix increment and decrement operators
  • Префиксный инкремент и декремент , унарные операторы и Prefix increment and decrement and unary and operators
  • Мультипликативные операторы , , и Multiplicative , , and operators
  • Аддитивные операторы и Additive and operators

Бинарные арифметические операторы имеют левую ассоциативность.Binary arithmetic operators are left-associative. То есть операторы с одинаковым приоритетом вычисляются в направлении слева направо.That is, operators with the same precedence level are evaluated from left to right.

Порядок вычисления, определяемый приоритетом и ассоциативностью операторов, можно изменить с помощью скобок ().Use parentheses, , to change the order of evaluation imposed by operator precedence and associativity.

Полный список операторов C#, упорядоченный по уровню приоритета, можно найти в разделе статьи Операторы C#.For the complete list of C# operators ordered by precedence level, see the section of the C# operators article.

Общие понятия

Типом данных в программировании называют совокупность двух множеств: множество значений и множество операций, которые можно применять к ним. Например, к типу данных целых неотрицательных чисел, состоящего из конечного множества натуральных чисел, можно применить операции сложения (+), умножения (*), целочисленного деления (/), нахождения остатка (%) и вычитания (−).

Язык программирования, как правило, имеет набор примитивных типов данных — типы, предоставляемые языком программирования как базовая встроенная единица. В C++ такие типы создатель языка называет фундаментальными типами. Фундаментальными типами в C++ считаются:

  • логический ();
  • символьный (напр., );
  • целый (напр., );
  • с плавающей точкой (напр., );
  • перечисления (определяется программистом);
  • .

Поверх перечисленных строятся следующие типы:

  • указательные (напр., );
  • массивы (напр., );
  • ссылочные (напр., );
  • другие структуры.

Перейдём к понятию литерала (напр., 1, 2.4F, 25e-4, ‘a’ и др.): литерал — запись в исходном коде программы, представляющаясобой фиксированное значение. Другими словами, литерал — это просто отображение объекта (значение) какого-либо типа в коде программы. В C++ есть возможность записи целочисленных значений, значений с плавающей точкой, символьных, булевых, строковых.

Литерал целого типа можно записать в:

  • 10-й системе счисления. Например, ;
  • 8-й системе счисления в формате 0 + число. Например, ;
  • 16-й системе счисления в формате 0x + число. Например, .

24, 030, 0x18 — это всё записи одного и того же числа в разных системах счисления.
Для записи чисел с плавающей точкой используют запись через точку: 0.1, .5, 4. — либо в
экспоненциальной записи — 25e-100. Пробелов в такой записи быть не должно.

Имя, с которым мы можем связать записанные литералами значения, называют переменной. Переменная — это поименованная либо адресуемая иным способом область памяти, адрес которой можно использовать для доступа к данным. Эти данные записываются, переписываются и стираются в памяти определённым образом во время выполнения программы. Переменная позволяет в любой момент времени получить доступ к данным и при необходимости изменить их. Данные, которые можно получить по имени переменной, называют значением переменной.
Для того, чтобы использовать в программе переменную, её обязательно нужно объявить, а при необходимости можно определить (= инициализировать). Объявление переменной в тексте программы обязательно содержит 2 части: базовый тип и декларатор. Спецификатор и инициализатор являются необязательными частями:

const int example = 3;
// здесь const — спецификатор
// int — базовый тип
// example — имя переменной
// = 3 — инициализатор.

Имя переменной является последовательностью символов из букв латинского алфавита (строчных и прописных), цифр и/или знака подчёркивания, однако первый символ цифрой быть не может. Имя переменной следует выбирать таким, чтобы всегда было легко догадаться о том, что она хранит, например, «monthPayment». В конспекте и на практиках мы будем использовать для правил записи переменных нотацию CamelCase. Имя переменной не может совпадать с зарезервированными в языке словами, примеры таких слов: if, while, function, goto, switch и др.

Декларатор кроме имени переменной может содержать дополнительные символы:

  • — указатель; перед именем;
  • — константный указатель; перед именем;
  • — ссылка; перед именем;
  • — массив; после имени;
  • — функция; после имени.

Инициализатор позволяет определить для переменной её значение сразу после объявления. Инициализатор начинается с литерала равенства (=) и далее происходит процесс задания значения переменной. Вообще говоря, знак равенства в C++ обозначает операцию присваивания; с её помощью можно задавать и изменять значение переменной. Для разных типов он может быть разным.

Спецификатор задаёт дополнительные атрибуты, отличные от типа. Приведённый в примере спецификатор const позволяет запретить последующее изменение значение переменной. Такие неизменяемые переменные называют константными или константой.

Объявить константу без инициализации не получится по логичным причинам:

const int EMPTY_CONST; // ошибка, не инициализована константная переменная
const int EXAMPLE = 2; // константа со значением 2
EXAMPLE = 3; // ошибка, попытка присвоить значение константной переменной

Для именования констант принято использовать только прописные буквы, разделяя слова символом нижнего подчёркивания.

Целые типы фиксированной длины

Стандарт C99 включает определения нескольких новых целочисленных типов для повышения переносимости программ. Уже доступные целочисленные базовые типы были сочтены неудовлетворительными, так как их размер зависел от реализации. Новые типы находят широкое применение в встраиваемых системах. Все новые типы определены в заголовочном файле ( в C++) и также доступны в ( в C++). Типы можно разделить на следующие категории:

  • Целые с точно заданным размером N бит в любой реализации. Включаются, только если доступны в реализации/платформе.
  • Наименьшие целые, размер которых является минимальным в реализации, состоят минимум из N бит. Гарантируется что определены типы для N=8,16,32,64.
  • Наибыстрейшие целые типы, которые являются гарантировано наиболее быстрыми в конкретной реализации, состоят минимум из N бит. Гарантируется что определены типы для N=8,16,32,64.
  • Целые типы для указателей, которые гарантировано смогут хранить адрес в памяти. Включены, только если доступны на конкретной платформе.
  • Наибольшие целые, размер которых является максимальным в реализации.

Следующая таблица показывает эти типы (N означает число бит):

Категория типа Знаковые типы Беззнаковые типы
Тип Минимальное значение Максимальное значение Тип Минимальное значение Максимальное значение
Точный размер
Минимальный размер
Наибыстрый
Указатель
Максимальный размер

Спецификаторы формата для printf и scanf

Заголовочный файл ( в C++) расширяет возможности типов, определённых в . В них входят макросы, которые определяют спецификаторы типов для строки формата printf и scanf и несколько функций, которые работают с типами и . Этот хедер был добавлен с C99.

Строка формата printf

Макросы определены в формате . Здесь {fmt} означает формат вывода и принадлежит (десятичный), (шестнадцатиричный), (восьмиричный), (беззнаковый) или (целый). {type} определяет тип аргумента и принадлежит к , , , либо , где означает число бит.

Строка формата scanf

Макросы определены в формате . Здесь {fmt} означает формат вывода и принадлежит (десятичный), (шестнадцатиричный), (восьмиричный), (беззнаковый) или (целый). {type} определяет тип аргумента и принадлежит к , , , либо , где означает число бит.

Функции

Типизация

При динамической типизации переменная связывается с типом на момент инициализации. Получается, что переменная в разных участках кода может иметь разные типы. Динамическую типизацию поддерживают Java Script, Python, Ruby, PHP.

Статическая типизация является противоположностью динамической. При объявлении переменная получает тип, который не меняется в дальнейшем. Языки Си и Си++ являются именно такими. Этот способ наиболее удобный для написания сложного кода, а на стадии компиляции исключается много ошибок.

Языки неформально делятся на сильнотипизированный и слаботипизированный. Сильная типизация подразумевает, что компилятор выдаст ошибку при несовпадении ожидаемого и фактического типов.

Пример слабой типизации.

Проверка согласования типов осуществляется системой типобезопасности. Ошибка типизации возникает, например, при попытке использовать число как функцию. Существуют нетипизированные языки. В противоположность типизированным, они позволяют осуществлять любые операции над каждым объектом.

Данные в языке C++

Для решения задачи в любой программе выполняется обработка каких-либо данных. Они могут быть различных типов: целые и вещественные числа, символы, строки, массивы. Данные в языке C++ принято описывать в начале функции. К основным типам данных языка относят:

Для формирования других типов данных используют основные и так называемые спецификаторы. В C++ определенны четыре спецификатора типов данных:

  • short — короткий;
  • long — длинный;
  • signed — знаковый;
  • unsigned — беззнаковый.

Когда необходимо инициализировать константную переменную (значение остается неизменным), следует перед типом написать const. Например:

const int var;

Символьный тип

Данные типа char в памяти компьютера всегда занимают 1 байт. Символьный тип может быть со знаком или без него. В величинах со знаком signed char можно хранить значение от -128 до 127. Соответственно значения переменных типа unsigned char могут находиться в диапазоне от 0 до 255. При работе с символьными данными нужно помнить, что если в выражении встречается одиночный символ, то он должен быть заключен в одинарные кавычки (‘a’).

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

Переменная типа int в памяти компьютера может занимать либо 2, либо 4 байта. Это зависит разрядности процессора. По умолчанию все целые типы считаются знаковыми, то есть спецификатор signed можно не указывать. Спецификатор unsigned позволяет представлять только положительные числа. Ниже представлены некоторые диапазоны значений целого типа

Тип Диапазон Размер
int -2147483648…2147483647 4 байта
unsigned int 0…4294967295 4 байта
signed int -2147483648…2147483647 4 байта
short int -32768…32767 2 байта
long int -2147483648…2147483647 4 байта
unsigned short int 0…65535 2 байта

Вещественный тип

Число с плавающей точкой представлено в форме mE +- p, где m — мантисса (целое или дробное число с десятичной точкой), p — порядок (целое число). Обычно величины типа float занимают 4 байта, а double 8 байт. Таблица диапазонов значений вещественного типа:

float 3,4E-38…3,4E+38 4 байта
double 1,7E-308…1,7E+308 8 байт
long double 3,4E-4932…3,4E+4932 8 байт

Логический тип

Переменная типа bool может принимать только два значения true (истина) или fasle (ложь). Любоезначение, не равное нулю, интерпретируется как true. Значение false представлено в памяти как 0.

Тип void

Множество значений этого типа пусто. Он используется для определения функций, которые не возвращают значения, для указания пустого списка аргументов функции, как базовый тип для указателей и в операции приведения типов.

Summary

As can be expected from such a long-lived framework, .NET has several number types to help you with your calculations, ranging from simple integers to complex currency-based values. As always, it’s important to use the correct tool for the job:

  • Use for non-integer math where the most precise answer isn’t necessary.
  • Use for non-integer math where precision is needed (e.g. money and currency).
  • Use by default for any integer-based operations that can use that type, as it will be more performant than or .

Don’t forget to check out the sample project over on GitHub!

Also, if this post helped you, please consider buying me a coffee. Your support funds all of my projects and helps me keep traditional ads off this site. Thanks for your support!

Happy Coding!

Huge thanks to Kathleen Dollard (@kathleendollard) for providing the code samples and her invaluable insight into how to effectively explain what’s going on in these samples.  Check out her Pluralsight course for more!

Hello Dear Reader! Want to get the best C#, ASP.NET, web tech, tips, and stories anywhere on the Web?
Sign up to receive my premium newsletter The Catch Block!
Each Wednesday, you’ll get the best reads, job listings, stories, tips, and news from around the ASP.NET and C# worlds.
All for only $5/month or $50/year!
Become a subscriber today!

Характеристики целочисленных типовCharacteristics of the integral types

C# поддерживает следующие предварительно определенные целочисленные типы:C# supports the following predefined integral types:

Ключевое слово или тип C#C# type/keyword ДиапазонRange РазмерSize Тип .NET.NET type
От -128 до 127-128 to 127 8-разрядное целое число со знакомSigned 8-bit integer System.SByte
От 0 до 2550 to 255 8-разрядное целое число без знакаUnsigned 8-bit integer System.Byte
От -32 768 до 32 767-32,768 to 32,767 16-разрядное целое число со знакомSigned 16-bit integer System.Int16
От 0 до 65 5350 to 65,535 16-разрядное целое число без знакаUnsigned 16-bit integer System.UInt16
От -2 147 483 648 до 2 147 483 647-2,147,483,648 to 2,147,483,647 32-разрядное целое число со знакомSigned 32-bit integer System.Int32
От 0 до 4 294 967 2950 to 4,294,967,295 32-разрядное целое число без знакаUnsigned 32-bit integer System.UInt32
От -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 64-разрядное целое число со знакомSigned 64-bit integer System.Int64
От 0 до 18 446 744 073 709 551 6150 to 18,446,744,073,709,551,615 64-разрядное целое число без знакаUnsigned 64-bit integer System.UInt64
Зависит от платформыDepends on platform 32- или 64-разрядное целое число со знакомSigned 32-bit or 64-bit integer System.IntPtr
Зависит от платформыDepends on platform 32- или 64-разрядное целое число без знакаUnsigned 32-bit or 64-bit integer System.UIntPtr

Во всех строках таблицы, кроме двух последних, каждое ключевое слово типа C# из крайнего левого столбца является псевдонимом для соответствующего типа .NET.In all of the table rows except the last two, each C# type keyword from the leftmost column is an alias for the corresponding .NET type. Ключевое слово и имя типа .NET являются взаимозаменяемыми.The keyword and .NET type name are interchangeable. Например, следующие объявления объявляют переменные одного типа:For example, the following declarations declare variables of the same type:

Типы и в последних двух строках таблицы являются целыми числами собственного размера.The and types in the last two rows of the table are native-sized integers. В коде такие числа представлены определенными типами .NET, но в каждом случае ключевое слово и тип .NET не взаимозаменяемы.They are represented internally by the indicated .NET types, but in each case the keyword and the .NET type are not interchangeable. Для и компилятор предоставляет преобразования и операции, как для целочисленных типов. Они отличаются от преобразований и операций для типов указателей и .The compiler provides operations and conversions for and as integer types that it doesn’t provide for the pointer types and . Дополнительные сведения см. в статье о типах и .For more information, see and types.

Дополнительные сведения о целочисленных типах собственного размера см. в статье о и .For information about the native-sized integer types, see and .

По умолчанию все целочисленные типы имеют значение .The default value of each integral type is zero, . Все целочисленные типы, кроме целых чисел собственного размера, имеют константы и с минимальным и максимальным значением этого типа.Each of the integral types except the native-sized types has and constants that provide the minimum and maximum value of that type.

Используйте структуру System.Numerics.BigInteger, чтобы представить целое число со знаком без верхней и нижней границ.Use the System.Numerics.BigInteger structure to represent a signed integer with no upper or lower bounds.

Основные типы данных

Базовые типы данных в C++ можно разбить на несколько групп

Знаковый тип. Переменные знакового типа могут использоваться для хранения одного символа. Самый простой тип char, размер которого равен 1 байт. Также имеются типы для представления
знаков, размером больше одного байта

Знаковый тип
Название Назначение
char Всегда 1 байт. Минимум 8 бит
char16_t 2-х байтный символ
char32_t 4-х байтный символ
wchar_t Используется для представления самого широкого из поддерживаемых наборов символов

Вообще-то эти типы есть и в си, мы не останавливались подробно на изучении представления строк.

Целочисленные типы данных. Как и в си, могут обладать модификаторами signed и unsigned. Как и в си, основными типами являются char, int, long и long long. Ничего нового здесь
не появилось.

Числа с плавающей точкой. Представлены типами float, double и long double. Ничего нового по сравнению с си.

Все описанные выше типы называют также арифметическими. Кроме них существует ещё пустой тип – void (также ничего нового по сравнению с си) и нулевой указатель.
Теперь, вместо NULL с его удивительными свойствами, появился новый фундаментальный тип nullptr_t с единственным значением nullptr, который хранит нулевой указатель
и равен только сам себе. При этом, он может быть приведён к нулевому указателю нужного типа.

В си++ введён булев тип. Он хранит всего два возможных значения true и false.

Си++ поддерживает также множество составных типов данных, которые будут рассмотрены позднее.

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

В языке C существует несколько типов целых чисел. Они различаются между собой объемом памяти, отводимым под переменную, а также возможностью присваивания положительных и отрицательных значений. От объема памяти, т. е. от количества выделяемых байтов под переменную, зависит, каким может быть максимально возможное значение, записанное в данную переменную. Следует отметить, что в языке Си объем памяти, выделяемый под конкретный тип, может зависеть от операционной системы.

Так, если под переменную какого-либо целочисленного типа выделяется 2 байта, что составляет 16 бит, и ей можно присваивать только положительные числа и ноль, то эти числа будут в диапазоне от 0 до 65535, т. к. 216 = 65536, но одна вариация забирается на нуль. Если же тип допускает отрицательные числа, то диапазон допустимых значений уже будет лежать в пределах от -32768 до +32767.

Часто в программах используется тип int. Вот пример, где происходит объявление и определение (присваивание значений) целочисленных переменных, а также вывод их значений на экран:

#include <stdio.h>
int main() {
    int lines, i;
    int count = ;

    lines = 100;
    i = -1;
    printf("%5d %5d %5d\n", i, count+10, lines);
}

Обратите внимание, что в языке C присваивать значение можно при объявлении переменных. Обычно под переменную типа int, которая может принимать как положительные так и отрицательные значения, отводится 4 байта, что равно 32-м битам

Отсюда допустимый диапазон значений будет лежать в пределах от -2 147 483 648 до 2 147 483 647. Если в исходном коде на C мы объявим переменную int max, присвоим ей максимально допустимое значение, а потом будем его увеличивать, то сообщений об ошибке не будет ни на этапе компиляции, ни на этапе выполнения

Обычно под переменную типа int, которая может принимать как положительные так и отрицательные значения, отводится 4 байта, что равно 32-м битам. Отсюда допустимый диапазон значений будет лежать в пределах от -2 147 483 648 до 2 147 483 647. Если в исходном коде на C мы объявим переменную int max, присвоим ей максимально допустимое значение, а потом будем его увеличивать, то сообщений об ошибке не будет ни на этапе компиляции, ни на этапе выполнения.

#include <stdio.h>

int main() {
    int max = 2147483647;

    printf("%d\n", max+1);
    printf("%d\n", max+2);
    printf("%d\n", max+10);
}

Результат будет таким:

-2147483648
-2147483647
-2147483639

Чтобы понять, почему такое происходит, представьте себе числовую ось не в виде прямой, а в виде окружности. Когда мы достигаем конца, двигаясь например по часовой стрелке, то это значит, что мы пришли в начало. Поэтому, продолжая движение по часовой стрелке, следующее число, которое мы увидим за максимально возможным, – это самое минимальное. Данную особенность языка Си следует иметь в виду при выполнении арифметических действий.

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

#include <stdio.h>

int main() {
    int min = -2147483648;

    printf("%d\n", min-1);
    printf("%d\n", min-2);
    printf("%d\n", min-10);
}

Результат:

2147483647
2147483646
2147483638

Помимо типа int в языке программирования C существуют другие (модифицированные) целочисленные типы:

  • short — отводится меньше байтов, чем на int;

  • long — отводится больше байтов, чем на int (не всегда, зависит от системы);

  • unsigned — столько же байт как у int, но без отрицательных чисел; в результате чего знаковый разряд освобождается, и количество положительных значений увеличивается;

  • unsigned short;

  • unsigned long.

При выводе длинных чисел следует дополнять спецификацию формата буквой l перед буквой формата. Например:

printf("%ld\n", i);
printf("%15ld\n", i);

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

Возникает вопрос: «Сколько памяти занимают переменные разных типов данных?». Вы можете удивиться, но размер переменной с любым типом данных зависит от компилятора и/или архитектуры компьютера!

Язык C++ гарантирует только их минимальный размер:

Категория Тип Минимальный размер
Логический тип данных bool 1 байт
Символьный тип данных char 1 байт
wchar_t 1 байт
char16_t 2 байта
char32_t 4 байта
Целочисленный тип данных short 2 байта
int 2 байта
long 4 байта
long long 8 байт
Тип данных с плавающей запятой float 4 байта
double 8 байт
long double 8 байт

Фактический размер переменных может отличаться на разных компьютерах, поэтому для его определения используют оператор sizeof.

Оператор sizeof — это унарный оператор, который вычисляет и возвращает размер определенной переменной или определенного типа данных в байтах. Вы можете скомпилировать и запустить следующую программу, чтобы выяснить, сколько занимают разные типы данных на вашем компьютере:

#include <iostream>

int main()
{
std::cout << «bool:\t\t» << sizeof(bool) << » bytes» << std::endl;
std::cout << «char:\t\t» << sizeof(char) << » bytes» << std::endl;
std::cout << «wchar_t:\t» << sizeof(wchar_t) << » bytes» << std::endl;
std::cout << «char16_t:\t» << sizeof(char16_t) << » bytes» << std::endl;
std::cout << «char32_t:\t» << sizeof(char32_t) << » bytes» << std::endl;
std::cout << «short:\t\t» << sizeof(short) << » bytes» << std::endl;
std::cout << «int:\t\t» << sizeof(int) << » bytes» << std::endl;
std::cout << «long:\t\t» << sizeof(long) << » bytes» << std::endl;
std::cout << «long long:\t» << sizeof(long long) << » bytes» << std::endl;
std::cout << «float:\t\t» << sizeof(float) << » bytes» << std::endl;
std::cout << «double:\t\t» << sizeof(double) << » bytes» << std::endl;
std::cout << «long double:\t» << sizeof(long double) << » bytes» << std::endl;
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#include <iostream>

intmain()

{

std::cout<<«bool:\t\t»<<sizeof(bool)<<» bytes»<<std::endl;

std::cout<<«char:\t\t»<<sizeof(char)<<» bytes»<<std::endl;

std::cout<<«wchar_t:\t»<<sizeof(wchar_t)<<» bytes»<<std::endl;

std::cout<<«char16_t:\t»<<sizeof(char16_t)<<» bytes»<<std::endl;

std::cout<<«char32_t:\t»<<sizeof(char32_t)<<» bytes»<<std::endl;

std::cout<<«short:\t\t»<<sizeof(short)<<» bytes»<<std::endl;

std::cout<<«int:\t\t»<<sizeof(int)<<» bytes»<<std::endl;

std::cout<<«long:\t\t»<<sizeof(long)<<» bytes»<<std::endl;

std::cout<<«long long:\t»<<sizeof(longlong)<<» bytes»<<std::endl;

std::cout<<«float:\t\t»<<sizeof(float)<<» bytes»<<std::endl;

std::cout<<«double:\t\t»<<sizeof(double)<<» bytes»<<std::endl;

std::cout<<«long double:\t»<<sizeof(longdouble)<<» bytes»<<std::endl;

return;

}

Вот результат, полученный на моем компьютере:

Ваши результаты могут отличаться, если у вас другая архитектура, или другой компилятор

Обратите внимание, оператор sizeof не используется с типом void, так как последний не имеет размера

Если вам интересно, что значит в коде, приведенном выше, то это специальный символ, который используется вместо клавиши TAB. Мы его использовали для выравнивания столбцов. Детально об этом мы еще поговорим на соответствующих уроках.

Интересно то, что sizeof — это один из 3-х операторов в языке C++, который является словом, а не символом (еще есть new и delete).

Вы также можете использовать оператор sizeof и с переменными:

#include <iostream>

int main()
{
int x;
std::cout << «x is » << sizeof(x) << » bytes» << std::endl;
}

1
2
3
4
5
6
7

#include <iostream>
 

intmain()

{

intx;

std::cout<<«x is «<<sizeof(x)<<» bytes»<<std::endl;

}

Результат выполнения программы:

На следующих уроках мы рассмотрим каждый из фундаментальных типов данных языка С++ по отдельности.

Решение

когда повышен до в интегральных акциях отрицательные значения также теряются (что приводит к такому веселью, как быть правдой).

Как и большинство механизмов в C (которые наследуются в C ++), обычные арифметические преобразования следует понимать с точки зрения аппаратных операций. Создатели C были очень хорошо знакомы с языком ассемблера машин, с которыми они работали, и они написали C, чтобы иметь непосредственный смысл для себя и людей, подобных себе, при написании вещей, которые до этого были бы написаны на ассемблере (например, UNIX). ядро).

Теперь процессоры, как правило, не имеют инструкций смешанного типа (добавьте float к double, сравните int с float и т. Д.), Потому что это будет огромной тратой недвижимости на пластине — вам придется реализовать столько опкодов, сколько вы хотите, чтобы поддерживать различные типы. То, что у вас есть только инструкции для «добавить int к int», «сравнить float с float», «умножить без знака на unsigned» и т. Д., Делает обычные арифметические преобразования необходимыми в первую очередь — они представляют собой отображение двух типов на инструкцию семья, которая имеет больше всего смысла использовать с ними.

С точки зрения того, кто привык писать низкоуровневый машинный код, если у вас смешанные типы, инструкции на ассемблере, которые вы, скорее всего, рассмотрите в общем случае, — это инструкции, которые требуют наименьшего количества преобразований. Это особенно относится к плавающим точкам, где преобразования требуют больших затрат времени, особенно в начале 1970-х годов, когда разрабатывался C, компьютеры работали медленно и когда вычисления с плавающей точкой выполнялись программно. Это показано в обычных арифметических преобразованиях — только один операнд когда-либо был преобразован (за единственным исключением: /, где может быть преобразован в , что не требует ничего делать на большинстве машин. Возможно, не в любом месте, где применяется исключение).

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

Интересно отметить в этом контексте, кстати, что выше в иерархии, чем так что сравнивая с закончится в неподписанном сравнении (отсюда немного с самого начала). Я подозреваю, что это показатель того, что люди в старину считали меньше как ограничение на чем как расширение его диапазона значений: нам сейчас не нужен знак, поэтому давайте используем дополнительный бит для большего диапазона значений. Вы бы использовали его, если бы у вас были основания ожидать, что переполнится — гораздо большее беспокойство в мире 16-бит s.

68

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector