В чем разница между float и double?

Арифметическое переполнение и деление на нуль

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

Целочисленное арифметическое переполнение

Деление целого числа на ноль всегда вызывает исключение DivideByZeroException.

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

  • Если в проверяемом контексте переполнение возникает в константном выражении, происходит ошибка времени компиляции. В противном случае, если операция производится во время выполнения, возникает исключение OverflowException.
  • В непроверяемом контексте результат усекается путем удаления старших разрядов, которые не помещаются в целевой тип данных.

Вместе с проверяемыми и непроверяемыми операторами вы можете использовать операторы и , чтобы управлять контекстом проверки переполнения, в котором вычисляется выражение:

По умолчанию арифметические операции выполняются в непроверяемом контексте.

Арифметическое переполнение с плавающей запятой

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

Для операндов типа арифметическое переполнение всегда вызывает исключение OverflowException, а деление на нуль всегда вызывает исключение DivideByZeroException.

Преобразование строки в число, NumberStyles

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

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

C#

int error = int.Parse («(2)»); // Генерация исключения
int minusTwo = int.Parse («(2)», NumberStyles.Integer |
                                 NumberStyles.AllowParentheses); // OK
decimal fivePointTwo = decimal.Parse («£5.20», NumberStyles.Currency,
                       CultureInfo.GetCultureInfo («en-GB»));

1
2
3
4
5

interror=int.Parse(«(2)»);// Генерация исключения

intminusTwo=int.Parse(«(2)»,NumberStyles.Integer|

                 NumberStyles.AllowParentheses);// OK

decimalfivePointTwo=decimal.Parse(«£5.20»,NumberStyles.Currency,

            CultureInfo.GetCultureInfo(«en-GB»));

Комбинируемые члены :

  •  — допускает наличие в начале входной строки пробелов
  •  — допускает наличие в конце входной строки пробелов
  •  — допускает наличие в начале входной строки символов
  •  — допускает наличие в конце входной строки символов
  •  — допускает наличие во входной строке скобок
  •  — допускает наличие во входной строке десятичной точки
  •  — допускает наличие во входной строке разделителя разрядов
  •  — допускает наличие во входной строке экспоненты
  •  — допускает наличие во входной строке символа валют
  •  — допускает наличие во входной строке символа шестнадцатиричной записи

Составные члены :

  • — любые символы кроме цифр в входной строке не допустимы
  • — допустимы символы для целых чисел
  • — допустимы символы для чисел с плавающей точкой
  • — допустимы символы для любых чисел
  • — допустимы символы для шестнадцатиричной записи
  • — допустимы символы для валюты
  • — допустимы любые символы

Если входная строка содержит символы отличные от цифр, а  не методу не передан, будет сгенерировано исключение.

Терминология

Переменная: символическое имя количества данных, чтобы имя можно было использовать для доступа к данным, на которые он ссылается в области кода, где он определен. В C++ переменная обычно используется для ссылки на экземпляры скалярных типов данных, тогда как экземпляры других типов обычно называются объектами.

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

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

Тип void

Тип описывает пустой набор значений. Невозможно указать переменную типа . Тип используется в основном для объявления функций, которые не возвращают значения, или для объявления универсальных указателей на нетипизированные или произвольные типизированные данные. Любое выражение может быть явно преобразовано или приведено к типу . Однако такие выражения можно использовать только в следующих операторах и операндах:

  • в операторе выражения (Дополнительные сведения см. в разделе выражения.)

  • в левом операнде оператора запятой (Дополнительные сведения см. в разделе оператор-запятая.)

  • во втором и третьем операндах условного оператора (). (Дополнительные сведения см. в разделе выражения с условным оператором.)

Целочисленный тип данных integer

Мы можем использовать тип данных для определения объектов, значение которых может быть целым числом. Например, следующие строки определяют сигнал типа и присваивают ему целое число 4.

Как показано на рисунке 2, тип данных относится к категории «стандартных типов», которая определена в пакете “” из библиотеки “”. Как обсуждалось в предыдущей статье, нам не нужно явно делать пакет “” и библиотеку “” видимыми для проекта.

Следующий код показывает простой пример, когда два входа типа , и , складываются вместе, и результат присваивается .

На рисунке 3 показан результат ISE симуляции приведенного выше кода. На этом рисунке показан десятичный эквивалент значений ввода/вывода. Например, от 200 нс до 300 нс, входы и равны 3 и -1 соответственно. Таким образом, выход, , равен 3 + (-1) = 2.

Рисунок 3 – Результаты симуляции

При использовании целочисленного типа данных мы не принимаем непосредственного участия в определениях на уровне битов, однако ясно, что реализация для представления определенных сигналов будет использовать несколько бит. Сколько бит будет использоваться для представления целочисленных сигналов в приведенном выше коде? VHDL не указывает точное количество бит, но любая реализация VHDL должна поддерживать как минимум 32-разрядрую реализацию типа . Согласно стандарту, эта 32-разрядная реализация позволяет присваивать объекту типа целое число в диапазоне от -(231-1) до +(231-1).

Иногда мы имеем дело с ограниченными значениями, и для представления небольшого значения неэффективно использовать 32-разрядный сигнал. Например, предположим, что вход принимает значение от до 45. Таким образом, мы можем использовать 6-разрядный сигнал вместо 32-разрядного представления, потому что 4510=1011012. Более того, предположим, что другой вход, , имеет значение в диапазоне от -45 до 45, поэтому должно использоваться знаковое () представление. Учитывая бит знака, нам нужно всего семь битов вместо 32 битов по умолчанию, потому что представление двух -45 равно 1010011. Чтобы добиться значительного сокращения использования ресурсов FPGA, мы можем просто указать диапазон значений сигналов, как в следующем коде:

Данный код предполагает, что входы и находятся в диапазонах от 0 до 45 и от -45 до 45 соответственно. Поскольку равен , диапазон будет от -45 до 90. Ограничение диапазона целых чисел уменьшает объем ресурсов FPGA, необходимых для реализации проекта. Более того, это дает возможность проверить на ошибки на ранних этапах проектирования. Например, предположим, что представляет собой угол, и из системных спецификаций мы знаем, что значение этого угла ограничено диапазоном от -45 до 90.

Как указано в приведенном выше коде, мы можем применить этот диапазон к определению объекта . Теперь, если мы допустим ошибку, которая заставляет значение находиться за пределами указанного диапазона, программное обеспечение симулятора выдаст ошибку и идентифицирует строку кода, которая включает недопустимое присваивание. Например, если мы укажем диапазон как от -45 до 89, а затем присвоим значение 45 и , и , ISIim симулятор прекратит моделирование со следующей ошибкой (ISim – это название симулятора, который включен в программное обеспечение ISE):

(В моем коде моделирования строка 17 содержит присваивание .) Обратите внимание, что симулятор ISIM по умолчанию не отлавливает эти ошибки, связанные с диапазоном; вы должны включить опцию «value range check» (проверка диапазона значений). Если данная опция не включена, симуляция не остановится, и целому числу, объявленному с ограниченным диапазоном, сможет быть присвоено любое значение

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

Аналогичным образом, третье и четвертое объявления должны иметь четыре бита

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

Указатели

Для любого типа T существует тип «указатель на T».

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

char letterC = 'C';
char *letter = &letterC; //взятие адреса переменной letterC и присваивание в переменную letter
printf("This code is written in %c.", *letter); //"This code is written in C."

Поскольку указатель — тоже тип переменной, правило «для любого типа T» выполняется и для них: можно объявлять указатели на указатели. К примеру, можно пользоваться :

int w = 100;
int *x = &w;
int **y = &x;
int ***z = &y;
printf("w contains %d.", ***z); //"w contains 100."

Существуют также указатели на массивы и на функции. Указатели на массивы имеют следующий синтаксис:

char *pc10; // массив из 10 указателей на char
char (*pa)10; // указатель на массив из 10 переменных типа char

 — массив указателей, занимающий байт (на распространённых платформах — обычно 40 или 80 байт), а  — это один указатель; занимает он обычно 4 или 8 байт, однако позволяет обращаться к массиву, занимающему 10 байт: , но .
Указатели на массивы отличаются от указателей на первый элемент арифметикой. Например, если указатели указывает на адрес 2000, то указатель будет указывать на адрес 2010.

char (*pa)10;
char array10 = "Wikipedia";
pa = &array;
printf("An example for %s.\n", *pa); //"An example for Wikipedia."
printf("%c %c %c", (*pa)1, (*pa)3, (*pa)7); //"i i i"

sizeof

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

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

int main() {
	char c;
	short s;
	int i;
	long l;
	long long L;

	//Вызов sizeof как "функции"
	printf("sizeof(char)  = %d\n", sizeof(c));
	printf("sizeof(short) = %d\n", sizeof(s));
	printf("sizeof(int)   = %d\n", sizeof(i));
	printf("sizeof(long)  = %d\n", sizeof(l));
	printf("sizeof(long long) = %d\n", sizeof(L));

	//Вызов как оператора
	printf("sizeof(char)  = %d\n", sizeof c);
	printf("sizeof(short) = %d\n", sizeof s);
	printf("sizeof(int)   = %d\n", sizeof i);
	printf("sizeof(long)  = %d\n", sizeof l);
	printf("sizeof(long long) = %d\n", sizeof L);
	
	_getch();
}

(Я думаю ясно, что переменные могут иметь любое валидное имя). Эту программу можно было написать и проще

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

int main() {

	printf("sizeof(char)  = %d\n", sizeof(char));
	printf("sizeof(short) = %d\n", sizeof(short));
	printf("sizeof(int)   = %d\n", sizeof(int));
	printf("sizeof(long)  = %d\n", sizeof(long));
	printf("sizeof(long long) = %d\n", sizeof(long long));
	//нельзя произвести вызов sizeof как оператора для имени типа
	//sizeof int - ошибка компиляции

	_getch();
}

В си один и тот же тип может иметь несколько названий
short === short int
long === long int
long long === long long int
unsigned int === unsigned

Диапазоны значений простых типов данных в C++ для IBM PC-совместимых компьютеров

Q: Что означает термин IBM PC-совместимый компьютер?
A: IBM PC-совместимый компьютер (англ. IBM PC compatible) — компьютер, архитектурно близкий к IBM PC, XT и AT. IBM PC-совместимые компьютеры построены на базе микропроцессоров, совместимых с Intel 8086 (а, как известно, все выпущенные позднее процессоры Intel имеют полную обратную совместимость с 8086). По сути это практически все современные компьютеры.

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

Тип Диапазон значений Размер (байт)
bool true и false 1
signed char -128 … 127 1
unsigned char 0 … 255 1
signed short int -32 768 … 32 767 2
unsigned short int 0 … 65 535 2
signed long int -2 147 483 648 … 2 147 483 647 4
unsigned long int 0 … 4 294 967 295 4
float 3.4e-38 … 3.4e+38 4
double 1.7e-308 … 1.7C+308 8
long double 3.4e-4932 … 3.4e+4932 10

Для вещественных типов в таблице приведены абсолютные величины минимальных и максимальных значений.

Объявление и инициализация переменных

В общем случае при объявлении переменной в C#, вначале указывается тип данных переменной, затем ее имя:

int nVal;
string strVal;

Задание значения переменной можно произвести в момент инициализации:

int radius = 10;
string name = "John";

либо после инициализаций:

string name;
name = "John";

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

int notInitedVal;
Console.Write(notInitedVal);

В примерах мы не будем приводить код импорта и объявления класса. В конце главы будет приведен листинг программы со всеми примерами из данного урока.

Ключевое слово new

Ключевое слово new, как правило, используется при инициализации переменных, которые имеют ссылочный тип данных. О том, что это такое мы расскажем чуть ниже. Пусть у нас есть класс Rectangle

class Rectangle
{
    public double Width = 0;
    public double Height = 0;
}

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

Создадим переменную класса Rectangle

Rectangle rect = new Rectangle();
Console.WriteLine($"Rectangle Width={rect.Width}, Height={rect.Height}");

Переменные типа int, double и т.п. также можно проинициализировать с помощью ключевого слова new, в этом случае будет присвоено значение по умолчанию:

int newInitedValue = new int();
Console.WriteLine("Default int value: " + newInitedValue);

Ключевое слово var. Неявная типизация

При объявлении переменной вместо явного задания типа можно поставить ключевое слово var. В этом случае будет использована система вывода типов для определения типа переменной по ее значению.

int v1 = 12345;
var v2 = 12345;

Console.WriteLine($"Type of v1: {v1.GetType()}\nType of v2: {v2.GetType()}");

При работе с var необходимо помнить следующее:

  • использовать var можно только для объявления локальных переменных;
  • var нельзя использоваться для объявления типа возвращаемого значения, типов полей и параметров;
  • при объявлении переменной с использованием var она обязательно должна быть проинициализирована, при этом использовать для этого null запрещено;
  • объявлять переменную допускающую null-значение с использованием лексемы ? через var нельзя.

Математические операции в C++

В С++ есть пять базовых математических операций:

  1. Сложение (+).
  2. Вычитание (-).
  3. Умножение (*).
  4. Деление (/).
  5. Остаток от деления (%).

Используются они следующим образом:

Важно! Сначала выполняется правая часть выражения после знака =, а потом левая. То есть переменной не будет присвоено значения, пока не выполнены все вычисления

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

Если вам нужно провести какую-то операцию с переменной, а потом записать значение в неё же, используйте следующие операторы:

Во время работы с С++ вы будете часто прибавлять или отнимать единицу от какой-нибудь переменной. Для этого тоже есть сокращённая запись:

Инкремент и декремент могут быть префиксными (++x) и постфиксными (x++). Префиксный инкремент сначала прибавляет к переменной единицу, а потом использует эту переменную, а постфиксный — наоборот.

Размер основных типов данных в 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;

}

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

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

Массивы

Для каждого типа T, кроме void и типов функций, существует тип «массив из элементов N типа T». Массив — это коллекция значений одного типа, хранящихся последовательно в памяти. Массив размера N индексируется целым числом от до N-1.

int cat10 = {5,7,2};  // массив из 10 элементов, каждый типа int

Массивы могут быть инициализированы с помощью списка инициализации, но не могут быть присвоены друг к другу.

int a108;  // массив из 10 элементов, каждый типа 'массив из 8 int элементов'
float f32 = {{},{4,5,6}};

В C99 добавлены динамические многомерные массивы:

double (*A)n = malloc(sizeof(doublenn));
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
 
void Fill(int n, int m, int am) {
    int x = ;
    for (int i = ; i < n; ++i) {
        for (int j = ; j < m; ++j) {
            aij = ++x;
        }
    }
}
 
void Print(int n, int m, int am) {
    for (int i = ; i < n; ++i) {
        for (int j = ; j < m; ++j) {
            printf("%3d", aij);
        }
        putchar('\n');
    }
}
 
 
int main() {
    int n, m;
    scanf("%d %d", &n, &m);
 
    int (*a)m = malloc(sizeof(intnm));
    Fill(n, m, a);
    Print(n, m, a);
    free(a);
 
    int bnm;
    Fill(n, m, b);
    Print(n, m, b);
    return ;
}

Строковые типы

Строго говоря, язык C++ не имеет встроенного строкового типа; и хранения одиночных символов. необходимо объявить массив этих типов для приблизительной строки, добавив завершающее значение null (например, ASCII ) к элементу массива, который находится за последним допустимым символом (также называется строкой в стиле C). Строки в стиле C требовали написания гораздо большего объема кода или использования внешних библиотек служебных функций. Но в современных C++ у нас есть стандартные библиотеки типов (для символьных строк 8-разрядных типов) или (для строк символов 16-разрядного типа). Эти контейнеры стандартной библиотеки C++ можно рассматривать как собственные строковые типы, так как они являются частью стандартных библиотек, включенных в совместимую среду сборки C++. Просто используйте директиву , чтобы эти типы были доступны в программе. (Если используется MFC или ATL, класс также доступен, но не является частью стандарта C++.) Использование массивов символов, заканчивающихся нулем (приведенных выше строк в стиле C), не рекомендуется в современных C++.

Переменные в C++

Теперь попробуем создать свои переменные.

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

Код Как читается
int x; Объявить целочисленную переменную x без значения.

Так создаётся переменная без значения. Если вы хотите, чтобы в ней сразу было какое-то число, то нужно использовать знак присваивания (=):

Код Как читается
int y = 5; Объявить целочисленную переменную y со значением 5.

Теперь в любое время можно менять значения переменных:

Код Как читается
x = 6; Присвоить переменной x значение 6.

Математический знак равенства (=) в программировании называется знаком присваивания.

Важно! Указывать тип данных нужно только при объявлении переменной. Давайте попробуем вывести значение какой-нибудь переменной на экран

Для этого напишем следующий код:

Давайте попробуем вывести значение какой-нибудь переменной на экран. Для этого напишем следующий код:

Внимательно прочтите этот код, а потом скомпилируйте и запустите программу:

Nullable-типы (нулевые типы) и операция ??

Объявление и инициализация Nullable-переменных

В работе с типами-значениями есть одна особенность, они не могут иметь значение null. При наличии любой из следующих строк кода, компиляция программы не будет выполнена:

int nv = null;
bool bv = null;

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

Можно объявить переменную с использованием символа ? после указания типа, тогда она станет nullable-переменной – переменной поддерживающей null-значение:

int? nv1 = null;
bool? bv1 = null;

Использование символа ? является синтаксическим сахаром для конструкции Nullable<T>, где T – это имя типа. Представленные выше примеры можно переписать так:

Nullable<int> nv1 = null;
Nullable<bool> bv1 = null;

Проверка на null. Работа с HasValue и Value

Для того чтобы проверить, что переменная имеет значение null можно воспользоваться оператором is с шаблоном типа:

bool? flagA = true;

if(flagA is bool valueOfFlag)
{
    Console.WriteLine("flagA is not null, value: {valueOfFlag}");
}

Также можно воспользоваться свойствами класса Nullable

  • Nullable<T>.HasValue

    Возвращает true если переменная имеет значение базового типа. То есть если она не null.

  • Nullable<T>.Value

    Возвращает значение переменной если HasValue равно true, иначе выбрасывает исключение InvalidOperationException.

bool? flagB = false;

if(flagB.HasValue)
{
    Console.WriteLine("flagB is not null, value: {flagB.Value}");
}

Приведение Nullable-переменной к базовому типу

При работе с Nullable-переменными их нельзя напрямую присваивать переменным базового типа. Следующий код не будет скомпилирован:

double? nvd1 = 12.3;
double nvd2 = nvd1; // error

Для приведения Nullable-переменной к базовому типу можно воспользоваться явным приведением:

double nvd3 = (double) nvd1;

В этом случае следует помнить, что если значение Nullable-переменной равно null, то при выполнении данной операции будет выброшено исключение InvalidOperationException.

Второй вариант – это использование оператора ??, при этом нужно дополнительно задаться значением, которое будет присвоено переменной базового типа если в исходной лежит значение null

double nvd4 = nvd1 ?? 0.0;
Console.WriteLine(nvd4);


bool? nvb1 = null;
bool nvb2 = nvb1 ?? false;
Console.WriteLine(nvb1);
Console.WriteLine(nvb2);

Второй вариант позволяет более лаконично обрабатывать ситуацию, когда вызов какого-то метода может возвращать null, а результат его работы нужно присвоить типу-значению, при этом заранее известно, какое значение нужно присвоить переменной в этой ситуации:

static int? GetValue(bool flag)
{
    if (flag == true)
        return 1000;
    else
        return null;
}

static void Main(string[] args)
{
    int test1 = GetValue(true) ?? 123;
    Console.WriteLine(test1);
    int test2 = GetValue(false) ?? 123;
    Console.WriteLine(test2);
}

Общая система типов (CTS)

Под типом в C# понимается: класс, интерфейс, структура, перечисление и делегат. При разработке программного обеспечения на этом языке, фактически вы будет создавать и организовывать определенным образом взаимодействие между различными типами данных. Так как .NET – это платформа, под которую можно разрабатывать на разных языках, то существует так называемая общая система типов CTS (Common Type System), которая определяет как должны быть описаны типы, чтобы вы могли с ними работать в других языках, то есть как они должны быть представлены в CLR. CLR – это аббревиатура от Common Language Runtime – общеязыковая исполняющая среда, она отвечает за обнаружение, загрузку и управление типами, управляет памятью, отвечает за безопасность, обеспечивает работу многопоточных приложений и т.п.

Вступление

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

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

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

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

int i1 = 11;
int i2 = 3;
float x = i1 / i2;

1
2
3

inti1=11;

inti2=3;

floatx=i1i2;

Значением переменной будет . Как сообщить компилятору, что мы хотим использовать деление типа с плавающей точкой вместо целочисленного деления? Правильно! Использовать один из операторов явного преобразования типов данных, чтобы указать компилятору выполнить явное преобразование.

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

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

Язык программирования, как правило, имеет набор примитивных типов данных — типы, предоставляемые языком программирования как базовая встроенная единица. В 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; // ошибка, попытка присвоить значение константной переменной

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

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

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

Adblock
detector