Файл: Типы данных, пространство имен, классификация типов среды .NET.pdf

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 23.04.2023

Просмотров: 95

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

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

Подобно языкам C++ и Java, C# подразделяет типы на два вида: встроенные типы, которые определены в языке, и определяемые пользователем типы, которые выбирает программист. С# также подразделяет типы на две другие категории: размерные и ссылочные.[31]

Размерные типы (value type) – это сравнительно небольшие по объему данные, которые представимы в виде последовательности битов. Переменные ссылочного типа (reference type) указывают на местоположение той или иной последовательности битов. [32]

Основное различие между ними - это способ, которым их значения сохраняются в памяти. Размерные типы сохраняют свое фактическое значение в стеке. Ссылочные типы хранят в стеке лишь адрес объекта, а сам объект сохраняется в куче. Куча — основная память программ, доступ к которой осуществляется на много медленнее чем к стеку. Если вы работаете с очень большими объектами, то сохранение их в куче имеет много преимуществ. С# также поддерживает и указатели на типы, но они редко употребляются. Применение указателей связано с использованием неуправляемого кода. [33]

One of the biggest advantages of the .NET Framework is the built-in automatic memory management. It protects the programmers from the complex task of manually allocating memory for objects and then waiting for a suitable moment to release it. This significantly increases the developer productivity and the quality of the programs written in C#. In the .NET Framework, there is a special component of the CLR that looks after memory management. It is called a "garbage collector" (automated memory cleaning system). The garbage collector has the following main tasks: to check when the allocated memory for variables is no longer in use, to release it and make it available for allocation of new objects. [34]

Стек - это структура данных, которая сохраняет элементы по принципу: первым пришел, последним ушел (полная противоположность очереди). Стек относится к области памяти, поддерживаемой процессором, в которой сохра­няются локальные переменные. Доступ к стеку во много раз быстрее, чем к общей области памяти, поэтому использование стека для хранения данных ускоряет работу вашей программы. [35]

Стек напоминает стопку тарелок: если нужна чистая тарелка, берут тарелку из стопки (то есть ту, которую положили последней). С переменными
программы происходит то же самое: при вызове функции все объявленные в ней переменные заталкиваются в стек (эта операция называется push); если эта функция вызовет другую функцию, то все переменные, объявленные новой функцией, тоже попадут в стек. Когда функция, вызванная последней, завершится, ее переменные выходят из области видимости (scope) программы и выталкиваются из стека (эта операция называется pop); в результате память, занимаемая этими переменными, освобождается, и выполнение программы продолжается. [36]


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

Ссылочные типы (например, объекты) располагаются в куче. Куча - это оперативная память вашего компьютера. Доступ к ней осуществляется медленнее, чем к стеку. Когда объект располагается в куче, то переменная хранит лишь адрес объекта. Этот адрес хранится в стеке. По адресу программа имеет доступ к самому объекту, все данные которого сохраняются в общем куске памяти (куче).

«Сборщик мусора» уничтожает объекты, располагающиеся в стеке, каж­дый раз, когда соответствующая переменная выходит за область видимости. [37] Таким образом, если вы объявляете локальную переменную в пределах функции, то объект будет помечен как объект для «сборки мусора».[38]

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

Приведение типов

Множество типов данных порождает проблемы при выполнении операций с операндами (участниками операций) различных типов.

Рассмотрим варианты присваивания значения переменной. Операция присваивания выполняется справа налево. Например,

int i,j;

i=j=5; равнозначно j=5; i=j;

При присваивании переменной одного типа значения константы или переменной другого типа (или инициализации переменной с помощью константы или другой переменной) происходит автоматическое приведение типов, то есть преобразование типа правого операнда в тип левого. В С# разрешено автоматическое приведение типов, как показано в таблице 2. В левом столбце указаны типы левого операнда, в верхней строке -типы правого операнда в выражении left=right;. На пересечении соответствующих строк и столбцов знаком + отмечены разрешенные сочетания. Смысл ограничения в том, что для предотвращения потери информации значение правого операнда необходимо разместить в области возможных значений левого. Компилятор фиксирует ошибки при присваивании. Переменные типа bool к числовым типам не приводятся.

В листинге 4 показаны наиболее интересные варианты присваивания, в том числе инициализация переменной типа long шестнадцатеричным числом и типа double числом в научной нотации. Результат исполнения программы приведен на рисунке 5. Следует обратить внимание на то, что при присваивании переменной числового типа значения типа char левому операнду передается код символа (выделенные ячейки в таблице 2).


Таблица 2 - Автоматическое приведение базовых типов при

присваивании[39]

left

byte

sbyte

short

ushort

int

uint

long

ulong

char

float

double

decimal

bool

byte

+

-

-

-

-

-

-

-

-

-

-

-

-

sbyte

-

+

-

-

-

-

-

-

-

-

-

-

-

short

+

+

+

-

-

-

-

-

-

-

-

-

-

ushort

+

-

-

+

-

-

-

-

-

-

-

-

-

int

+

+

+

+

+

-

-

-

+

-

-

-

-

uint

+

-

-

+

-

+

-

-

+

-

-

-

-

long

+

+

+

+

+

+

+

-

+

-

-

-

-

ulong

+

-

-

+

-

+

-

+

+

-

-

-

-

char

-

-

-

-

-

-

-

-

+

-

-

-

-

float

+

+

+

+

+

+

+

+

+

+

-

-

-

double

+

+

+

+

+

+

+

+

+

+

+

-

-

decimal

+

+

+

+

+

+

+

+

+

-

-

+

-

bool

-

-

-

-

-

-

-

-

-

-

-

-

+


+ автоматическое приведение типа right к left

- запрет автоматического приведения типа right к left.

выделение кода символа

Листинг 4 - Автоматическое приведение типов при присваивании

/ Базовые типы. Автоматическое приведение типов при присваивании*/

using System;

namespace ConsoleApp4

{

class Program[40]

{

static void Main(string[] args)

{

byte @byte = 134;

sbyte @sbyte = -125;

short @short = -4318;

ushort @ushort = 15, ushort1;

int @int = 1, @int1;

uint @uint = 99, @uint1;

long @long = 0x7db3, @long1;

ulong @ulong = 99076, @ulong1;

float @float = -0.879f, @float1; ;

double @double = -15.66e+2, @double1;

decimal @decimal = 73m, @decimal1;

char @char='%';

bool @bool = true;

Console.WriteLine("ПЕРЕМЕННЫЕ БАЗОВЫХ ТИПОВ");

Console.Write(@byte + " " + @sbyte + " " + @short + " " + @ushort + " " + @int + " ");

Console.Write(@uint + " " + @long + " " + @ulong + " " + @float + " " + @double + " ");

Console.WriteLine(@decimal + " " + @char + " " + @bool + "\n");

Console.WriteLine("ПРИСВАИВАНИЕ");

short @short1 = @byte; Console.Write(@short1 + " ");

@short1 = @sbyte; Console.Write(@short1 + "\n");

@ushort1 = @byte; Console.Write(@ushort1 + "\n");

@int1 = @byte; Console.Write(@int1 + " ");

@int1 = @sbyte; Console.Write(@int1 + " ");

@int1 = @short; Console.Write(@int1 + " ");[41]

@int1 = @ushort; Console.Write(@int1 + " ");

@int1 = @char; Console.Write(@int1 + "\n");// код символа

@uint1 = @byte; Console.Write(@uint1 + " ");

@uint1 = @ushort; Console.Write(@uint1 + "\n");

@long1 = @byte; Console.Write(@long1 + " ");

@long1 = @sbyte; Console.Write(@long1 + " ");

@long1 = @short; Console.Write(@long1 + " ");

@long1 = @ushort; Console.Write(@long1 + " ");

@long1 = @int; Console.Write(@long1 + " ");

@long1 = @uint; Console.Write(@long1 + " ");

@long1 = @char; Console.Write(@long1 + "\n");//код символа

@ulong1 = @byte; Console.Write(@ulong1 + " ");

@ulong1 = @ushort; Console.Write(@ulong1 + " ");

@ulong1 = @uint; Console.Write(@ulong1 + " ");

@ulong1 = @char; Console.Write(@ulong1 + "\n");//код симв. float

@float1 = @char; Console.Write(@float1 + "\n");//код символа

@double1 = @char; Console.WriteLine(@double1 + "\n");/*код символа*/

@decimal1 = @char; Console.Write(@ulong1 + " ");/*код символа*/

@decimal1 = @byte; Console.Write(@ulong1 + " ");

@decimal1 = @sbyte; Console.Write(@decimal1 + " ");

@decimal1 = @short; Console.Write(@decimal1 + " ");

@decimal1 = @ushort; Console.Write(@decimal1 + " ");

@decimal1 = @long; Console.Write(@decimal1 + " ");

@decimal1 = @ulong; Console.WriteLine(@decimal1);

Console.WriteLine("THE END");

}

}

}[42]

Рисунок 5 - Результат исполнения программы из листинга 4

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

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

Перечислим правила приведения типов для бинарных (с двумя операндами) операций:

если один операнд имеет тип decimal, то и второй преобразуется в decimal, но если второй операнд имеет тип double или float, результат будет ошибочным;


если один операнд имеет тип double, то второй преобразуется в тип double;

если один операнд имеет тип float, то второй преобразуется в тип float; [43]

если один операнд имеет тип ulong, то второй преобразуется в тип ulong, но если второй операнд имеет тип sbyte, short, int или long (то есть целочисленный тип со знаком), результат будет ошибочным;

если один операнд имеет тип long, то второй преобразуется в тип long;

если один операнд имеет тип uint, то второй преобразуется в тип uint, за исключением случаев, когда один операнд имеет тип uint, а второй имеет тип sbyte, short или int, то оба преобразуются в long;

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

Пункты правил приведения следует применять последовательно.

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

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

@ulong=@ulong+@int;/*правое слагаемое автоматически приводится к типу ulong*/

@long=@uint+@short;/*оба слагаемых автоматически приводится к типу long*/

@int=@byte+@byte;/*оба слагаемых автоматически приводится к типу int*/

@double=@byte+@byte+@double1;/*первое и второе слагаемые приводятся к типу int, складываются, результат приводится к @double*/[44]

@long=@char+@char1+@uint;/*первое и второе слагаемые приводятся к типу int, их сумма и третье слагаемое приводятся к типу long, общая сумма присваивается переменной типа long*/

@ulong=@char+@char1+@uint;/*вариант ошибочен, присваивание переменной типа ulong значения переменной типа long (общей суммы) запрещено*/

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

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