C#. урок 2. типы данных

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);
}

Числовые литералы (Numeric Literals)

Целочисленные литералы (Integral literals)  могут использовать десятичную (decimal) или шестнадцатеричную (hexadecimal) запись; шестнадцатеричная обозначается префиксом 0x (например, = 127).  Реально-числовые литералы также используют десятичную и шестнадцатеричную запись (например, ).

По умолчанию, компилятор определяет тип числовых литералов по следующим критериям: если литерал содержит десятичную точку или знак экспоненты () — это , в противном случае — это , , , или .

Тип литерала также можно указать с помощью суффиксов, перечисленных в таблице выше. Суффикс ставиться сразу после литерала, например:

C#

decimal d = 3.5M; // M = decimal (регистрозависимо)

1 decimald=3.5M;// M = decimal (регистрозависимо)

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

C#

long i = 5; // Скрытое преобразование из int в long

1 longi=5;// Скрытое преобразование из int в long

Суффикс технически излишен, т.к. все литералы с десятичной точкой приводятся к . Суффиксы и более полезны: они необходимы при написании дробных литералов типа или . Без суффикса подобные литералы будут считаться типом , который не преобразуется скрыто к или .

C#

float f = 4.5F; // Без суффикса не скомпилируется
decimal d = -1.23M; // Без суффикса не скомпилируется

1
2

floatf=4.5F;// Без суффикса не скомпилируется

decimald=-1.23M;// Без суффикса не скомпилируется

Диапазоны значений и знак целочисленных типов данных

Как вы уже знаете из предыдущего урока, переменная с n-ным количеством бит может хранить 2n возможных значений. Но что это за значения? Это значения, которые находятся в диапазоне. Диапазон — это значения от и до, которые может хранить определенный тип данных. Диапазон целочисленной переменной определяется двумя факторами: её размером (измеряется в битах) и её знаком (который может быть signed или unsigned).

Целочисленный тип signed (со знаком) означает, что переменная может содержать как положительные, так и отрицательные числа. Чтобы объявить переменную как signed, используйте ключевое слово :

signed char c;
signed short s;
signed int i;
signed long l;
signed long long ll;

1
2
3
4
5

signedcharc;

signedshorts;

signedinti;

signedlongl;

signedlonglongll;

По умолчанию, ключевое слово пишется перед типом данных.

1-байтовая целочисленная переменная со знаком (signed) имеет диапазон значений от -128 до 127, т.е. любое значение от -128 до 127 (включительно) может храниться в ней безопасно.

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

Целочисленный тип unsigned (без знака) может содержать только положительные числа. Чтобы объявить переменную как unsigned, используйте ключевое слово :

unsigned char c;
unsigned short s;
unsigned int i;
unsigned long l;
unsigned long long ll;

1
2
3
4
5

unsignedcharc;

unsignedshorts;

unsignedinti;

unsignedlongl;

unsignedlonglongll;

1-байтовая целочисленная переменная без знака (unsigned) имеет диапазон значений от 0 до 255.

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

Теперь, когда вы поняли разницу между signed и unsigned, давайте рассмотрим диапазоны значений разных типов данных:

Размер/Тип Диапазон значений
1 байт signed от -128 до 127
1 байт unsigned от 0 до 255
2 байта signed от -32 768 до 32 767
2 байта unsigned от 0 до 65 535
4 байта signed от -2 147 483 648 до 2 147 483 647
4 байта unsigned от 0 до 4 294 967 295
8 байтов signed от -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807
8 байтов unsigned от 0 до 18 446 744 073 709 551 615

Для математиков: Переменная signed с n-ным количеством бит имеет диапазон от -(2n-1) до 2n-1-1. Переменная unsigned с n-ным количеством бит имеет диапазон от 0 до (2n)-1.

Для нематематиков: Используем таблицу

Начинающие программисты иногда путаются между signed и unsigned переменными. Но есть простой способ запомнить их различия. Чем отличается отрицательное число от положительного? Правильно! Минусом спереди. Если минуса нет, значит число — положительное. Следовательно, целочисленный тип со знаком (signed) означает, что минус может присутствовать, т.е. числа могут быть как положительными, так и отрицательными. Целочисленный тип без знака (unsigned) означает, что минус спереди отсутствует, т.е. числа могут быть только положительными.

Вещественные данные

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

12345,678 = 1,2345678·104

Число 0,009876 в нормированной форме можно представить как

0,009876 = 9,876·10-3

В двоичной системе счисления значащий разряд, стоящий перед разделителем целой и дробной части, может быть равен  только 1. В случае если число нельзя представить в нормированной форме (например, число 0), значащий разряд перед разделителем целой и дробной части равен 0.
Значащие разряды числа, стоящие в нормированной форме после разделителя целой и дробной части, называются мантиссой числа.
В общем случае вещественное число в разрядной сетке вычислительной машины можно представить в виде 4 полей.

  • знак — бит, определяющий знак вещественного числа (0 для положительных чисел, 1 — для отрицательных).
  • степень — определяет степень 2, на которую требуется умножить число в нормированной форме. Поскольку степень 2 для числа в нормированной форме может быть как положительной, так и отрицательной, нулевой степени 2 в представлении вещественного числа соответствует величина сдвига, которая определяется как

    2n-1,

    где n — количество разрядов, отводимых для представления степени числа.

  • целое — бит, который для нормированных чисел всегда равен 1, поэтому в некоторых представлениях типов этот бит опущен и принимается равным 1.
  • мантисса — значащие разряды представления числа, стоящие после разделителя целой и дробной части в нормированной форме.

 
Различают три основных типа представления вещественных чисел в языке Си:

Тип Обозна-
чение в Си
Кол-во бит Биты степени Мантисса Сдвиг
простое float 32 30…23 22…0 127
двойной точности double 64 62…52 51…0 1023
двойной расширен- ной точности long double 80 78…64 62…0 16383

Как видно из таблицы, бит целое у типов float и double отсутствует. При этом диапазон представления вещественного числа состоит из двух диапазонов, расположенных симметрично относительно нуля. Например, диапазон представления чисел типа float можно представить в виде:Пример: представить число -178,125 в 32-разрядной сетке (тип float).
Для представления числа в двоичной системе счисления преобразуем отдельно целую и дробную части:

17810 = 101100102.

0,12510 = 0,0012.

Тогда

178,12510 = 10110010,0012=1,0110010001·2111

Для преобразования в нормированную форму осуществляется сдвиг на 7 разрядов влево).
Для определения степени числа применяем сдвиг:

0111111+00000111 = 10000110.

Таким образом, число -178,125 представится в разрядной сетке как

 

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

Си не следит за переполнением переменных. Это значит, что постоянно увеличивая значение, скажем, переменной типа int в конце концов мы «сбросим значение»

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

void main() {
	unsigned a = 4294967295;
	int b = 2147483647;
	//Переполнение беззнакового типа
	printf("%u\n", a);
	a += 1;
	printf("%u", a);
	//Переполнение знакового типа
	printf("%d\n", b);
	b += 1;
	printf("%d", b);
	getch();
}

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

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

Базовые типы данных в 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++ такие типы создатель языка называет фундаментальными типами. Фундаментальными типами в 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; // ошибка, попытка присвоить значение константной переменной

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

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

Среди примитивных типов также есть два вещественных. Хотя это не совсем точное название. Официально они называются числа с плавающей точкой — floating point numbers. Название происходит из стандарта, когда целую и дробную часть числа разделяет точка (а не запятая).

Полезно:

В каждой стране свои стандарты для записи чисел (внезапно!).

Многие из нас привыкли писать точки для разделения тысяч и запятую для отделения дробной части: например, мы бы записали так . А вот в США, где жили создатели Java, принят другой стандарт:

В Java есть два примитивных типа с плавающей точкой: и .

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

Например, дробное число можно представить как . Поэтому в памяти оно будет представлено как два числа (мантисса — значащая часть числа) и (экспонента — степень десятки)

Тип

Само название типа происходит от floating point number. Размер этого типа совсем небольшой — всего 4 байта (32 бита), но он может хранить значения от до . Под мантиссу отдается 24 бита, под экспоненту — 8 бит. Этот тип способен хранить всего 8 значащих цифр.

Такой подход позволяет хранить гораздо большие числа, чем , используя все те же 4 байта. Но при этом мы жертвуем точностью. Часть памяти расходуется на хранение мантиссы, поэтому такие числа хранят всего 6-7 знаков после запятой, остальные отбрасываются.

Пример:

Код Значение переменной

Как видите, основной недостаток этого типа — очень маленькое количество значащих цифр и потеря точности уже в восьмой цифре. Поэтому тип не сильно популярен среди Java-программистов.

Тип

Тип является стандартным типом с плавающей точкой. Его название происходит от double floating point. Его еще называют числом с плавающей точкой двойной точности. Все вещественные литералы по умолчанию имеют тип .

Этот тип занимает 8 байт памяти (64 бита) и может хранить значения от до . Важным моментом является то, что под его мантиссу отводится 53 бита, а остальные 11 – под экспоненту.

Это позволяет хранить 15-17 значащих цифр.

Пример:

Код Значение переменной

Такая точность, особенно в сравнении с типом , является определяющей: 99% всех операций с вещественными числами выполняются с типом .

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

Код Значение переменной

Решение

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

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

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

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

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

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

68

Строки

В языке программирования С нет отдельного строкового типа данных, хотя формат вывода строки есть (%s). Строки в C – это массивы символов, последний элемент которых является первым (с номером 0) символом в таблице ASCII. В этом месте таблицы стоит «ничто», имеющее символьное обозначение ‘\0’.

С другой стороны, строки — это необычные массивы в том смысле, что работа с ними в языке программирования C несколько отличается от работы с числовыми массивами. В этом мы убедимся позже.

Выше мы объявили и определили массив vowels. Если бы мы его определили вот так:

char vowels = {'a', 'e', 'i', 'o', 'u', 'y', '\0'};

или так:

char vowels1 = "aeiouy";

то он был бы строкой. Во втором случае сами двойные кавычки «говорят» что это строка, и символ окончания строки ‘\0’ записывается в память автоматом.

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

printf("%s\n", vowels);

printf("%f\n", f_arr); // ошибка

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

В общем случае при объявлении переменной в 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# 7.0 и более поздних версиях).

В приведенном ниже коде показан пример каждого из них.

В предыдущем примере также показано использование в качестве цифрового разделителя, который поддерживается, начиная с версии C# 7.0. Цифровой разделитель можно использовать со всеми видами числовых литералов.

Тип целочисленного литерала определяется его суффиксом следующим образом:

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

    Примечание

    Литералы интерпретируется как положительные значения. Например, литерал представляет число типа , хотя он имеет то же битовое представление, что и число типа . Если вам требуется значение определенного типа, приведите литерал к этому типу. Используйте оператор , если представить значение литерала в целевом типе невозможно. Например, выдает .

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

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

    Примечание

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

  • Если у литерала есть суффикс , , , , , , или , его тип — .

Если значение, представленное целочисленным литералом, превышает UInt64.MaxValue, происходит ошибка компиляции CS1021.

Если определенный тип целочисленного литерала — , а значение, представленное литералом, находится в диапазоне целевого типа, значение можно неявно преобразовать в , , , , , , или :

Как показано в предыдущем примере, если значение литерала выходит за пределы диапазона целевого типа, возникает ошибка компилятора CS0031.

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

Characteristics of the floating-point types

C# supports the following predefined floating-point types:

C# type/keyword Approximate range Precision Size .NET type
±1.5 x 10−45 to ±3.4 x 1038 ~6-9 digits 4 bytes System.Single
±5.0 × 10−324 to ±1.7 × 10308 ~15-17 digits 8 bytes System.Double
±1.0 x 10-28 to ±7.9228 x 1028 28-29 digits 16 bytes System.Decimal

In the preceding table, each C# type keyword from the leftmost column is an alias for the corresponding .NET type. They are interchangeable. For example, the following declarations declare variables of the same type:

The default value of each floating-point type is zero, . Each of the floating-point types has the and constants that provide the minimum and maximum finite value of that type. The and types also provide constants that represent not-a-number and infinity values. For example, the type provides the following constants: Double.NaN, Double.NegativeInfinity, and Double.PositiveInfinity.

The type is appropriate when the required degree of precision is determined by the number of digits to the right of the decimal point. Such numbers are commonly used in financial applications, for currency amounts (for example, $1.00), interest rates (for example, 2.625%), and so forth. Even numbers that are precise to only one decimal digit are handled more accurately by the type: 0.1, for example, can be exactly represented by a instance, while there’s no or instance that exactly represents 0.1. Because of this difference in numeric types, unexpected rounding errors can occur in arithmetic calculations when you use or for decimal data. You can use instead of when optimizing performance is more important than ensuring accuracy. However, any difference in performance would go unnoticed by all but the most calculation-intensive applications. Another possible reason to avoid is to minimize storage requirements. For example, ML.NET uses because the difference between 4 bytes and 16 bytes adds up for very large data sets. For more information, see System.Decimal.

You can mix integral types and the and types in an expression. In this case, integral types are implicitly converted to one of the floating-point types and, if necessary, the type is implicitly converted to . The expression is evaluated as follows:

  • If there is type in the expression, the expression evaluates to , or to in relational and equality comparisons.
  • If there is no type in the expression, the expression evaluates to , or to in relational and equality comparisons.

You can also mix integral types and the type in an expression. In this case, integral types are implicitly converted to the type and the expression evaluates to , or to in relational and equality comparisons.

You cannot mix the type with the and types in an expression. In this case, if you want to perform arithmetic, comparison, or equality operations, you must explicitly convert the operands either from or to the type, as the following example shows:

You can use either standard numeric format strings or custom numeric format strings to format a floating-point value.

Указание типов переменных и функций

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

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

При объявлении переменной типа POD настоятельно рекомендуется инициализировать ее, т. е. указать начальное значение. Пока переменная не инициализирована, она имеет «мусорное» значение, определяемое значениями битов, которые ранее были установлены в этом месте памяти. Необходимо учитывать эту особенность языка C++, особенно при переходе с другого языка, который обрабатывает инициализацию автоматически. При объявлении переменной типа, не являющегося классом POD, инициализация обрабатывается конструктором.

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

Функция printf() и форматированный вывод

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

На прошлом уроке мы выводили строку «Hello World» вот так:

printf("Hello World\n");

Однако то же самое можно было получить так:

printf("%s\n", "Hello World");

Здесь %s — это спецификация строкового формата, т. е. вместо %s будет подставлен следующий аргумент, данные которого должны быть строкой. Вывод целого числа может выглядеть так:

printf("%d\n", 5);

Вместо числа 5 может стоять переменная целочисленного типа. Функция printf() может принимать произвольное число аргументов:

printf("%d %s, %d %s.\n", 3, "dogs", 2, "cats");

При выводе данные подставляются по очередности следования: 3 на место первой спецификации, dogs на место второй и т.д. То есть следует строго соблюдать соответствие форматов и последующих данных.

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

Напишите программу, которая выводила бы на экране данные примерно так, как на картинке. При этом используйте возможность задать ширину поля, а также выравнивание по левому и правому краям.

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

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

Adblock
detector