Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Понятие переменной

Содержание:

Введение

Данная тема является актуальной для меня, так как подробное изучение ее будет являться одним из этапов обучения программированию. Рассмотрим работу с переменными на одном из популярных языков программирования – C#. Ввиду того, что во многих доступных русскоязычных книгах указан запрет на воспроизводство информации в любом виде – расширим зону поиска информации, прибегнув и к англоязычным изданиям в том числе.

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

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

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

Понятие переменной

Понятие переменная является одним из ключевых в программировании. Что это такое и для чего нужно? Любая программа работает с данными (числами, строками), которые могут вводиться пользователем или жестко прописываться в коде программы. Эти данные надо где-то хранить. Где? Постоянные данные хранятся в том или ином виде на жестком диске, а временные данные - в оперативной памяти компьютера.

Под временными данными я понимаю все, что необходимо программе для расчетов. Дело в том, что процессор умеет выполнять математические операции только над регистрами процессора (это как бы переменные внутри процессора) или оперативной памятью. Поэтому, чтобы произвести расчеты над постоянными данными, их необходимо загрузить в оперативную память. Допустим, что мы загрузили данные в память, но как с ними теперь работать? В языке ассемблера для этого используются адреса памяти, где хранятся данные, а в высокоуровневых языках, к которым относится и С#, такие участки памяти имеют имена. Имя, которое служит для адресования памяти, и есть переменная. Имя проще запомнить и удобнее использовать, чем числовые адреса.[1]

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

eStatus, boxVolume, arraySize, intI.[2]

What is a variable? A typical program uses various values that change during its execution. For example, we create a program that performs some calculations on the values entered by the user.[3] The values entered by one user will obviously be different from those entered in by another user. This means that when creating the program, the programmer does not know what values will be introduced as input, and that makes it necessary to process all possible values a user may enter. When a user enters a new value that will be used in the process of calculation, we can preserve it (temporarily) in the random access memory of our computer. The values in this part of memory change (vary) throughout execution and this has led to their name - variables.[4]

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

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

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

Рисунок №1 - Иерархия типов среды .NET

Корнем иерархии является класс System.Object, от которого прямо или косвенно унаследованы все типы .NET. Объявляя или используя любой тип, вы можете быть всегда уверены в том, что его самым дальним предком является System.Object.[5]

В .NET используется общая система типов (Common Туре System, CTS). Почему именно "общая"? Дело в том, что приложения для этой платформы можно разрабатывать на разных языках. Раньше было очень сложно разрабатывать приложения сразу на нескольких языках, потому что у каждого была своя система типов, и хранение строки, скажем, в Delphi и в С++ происходило по-разному. Благодаря общности типов в .NET, на каком бы языке вы ни писали программу, типы будут одинаковые, и они одинаково будут[6] храниться в памяти, а значит, станут одинаково интерпретироваться программой, и никаких проблем не возникнет.

В большинстве языков программирования выделяются два типа данных: простые (числа, строки, ... ) и сложные (структуры, объекты, ... ). В .NET и С# нет такого разделения. Эта технология полностью объектная, и даже простые типы данных являются объектами, хотя вы можете продолжать их использовать как простые типы в других языках. Это сделано для удобства программирования. [7]

Data types are sets (ranges) of values that have similar characteristics. For instance byte type specifies the set of integers in the range of [0 ... 255]. [8]

Каждая переменная имеет имя, тип, размер и значение. Имя переменной (идентификатор) в прямом смысле является ее названием. Тип определяет, какие числа или символы записаны в ячейку памяти под этим именем. Размер непосредственно связан с объявлением типа, определяет объем памяти и, следовательно, максимальную величину или точность задания числа. Значение определяет конкретное содержимое ячейки памяти. Идентификатор представляет собой комбинацию букв, цифр и знака подчеркивания, но не должен начинаться с цифры.

Например: Еr, rT_, yx, 6, u9t, _YU

Первым символом идентификатора может быть символ @, например:

@rt, @QQ, @D_eL

В этом случае за @ может следовать ключевое слово, например:

@new или @еlse

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

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

Пространство имен - это определенная область, внутри которой все имена должны быть уникальными. И именно благодаря использованию пространства имен уникальность переменных должна обеспечиваться только внутри определенного модуля - т.е. в разных модулях могут иметься переменные с одинаковыми именами. Так, например, в Delphi есть функция FindFirst. Такая же функция есть среди АРI-функций Windows, и описана она в модуле windows . Если нужно использовать вариант FindFirst из состава библиотеки визуальных компонентов (Visual Component Library, VCL), то можно просто вызвать эту функцию, а если нужен вариант из состава Windows API, то следует написать:

Windows.FindFirst

В .NET всё: классы, переменные, структуры и т. д. - разбито по пространствам имен, что позволяет избавиться от возможных конфликтов в именовании и, в то же время, использовать одинаковые имена в разных пространствах.[10]

Basic data types in C# are distributed into the following types:

Integer types - sbyte, byte, short, ushort, int, uint, long, ulong;

Real floating-point types - float, double;

Real type with decimal precision - decimal;

Boolean type - bool;

Character type - char;

String - string;

Object type - object.

These data types are called primitive (built-in types), because they are embedded in C# language at the lowest level. The table below represents the above[11] mentioned data types, their range and their default values: [12]

Таблица №1 - Основные типы данных CTS

Объект

Псевдоним

Описание

Object

object

Базовый класс для всех типов CTS.

String

string

Строка.

SByte

sbyte

8 - разрядное число со знаком. Возможные значения от -128 до 127.

Byte

byte

8 - разрядное число без знака. Значения от 0 до 255.

Int16

int

16 - разрядное число со знаком. Возможные значения от -32 768 до 32 767.

Uint16

uint

16 - разрядное число без знака. Возможные значения от 0 до 65 535.

Int32

int

32 - разрядное число со знаком. Возможные значения от -2 147 483 648 до 2 147 483 647.

Uint32

uint

32 - разрядное число без знака. Возможные значения от 0 до 4 294 967 295.

Int64

long

64 - разрядное число со знаком. Возможные значения от -9 223 372 036 854 775 808

до 9 223 372 036 854 775 807.

Uint64

ulong

64 - разрядное число без знака. Значения от 0 до 18 446 744 073 709 551 615.

Decimal

decimal

128 - разрядное число.

Char

char

16 - разрядный символ.

Single

float

32 - разрядное число с плавающей точкой стандарта IEEE. [13]

Double

double

64 - разрядное число с плавающей точкой

Boolean

bool

Булево значение[14]

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

Листинг 1. Минимальные и максимальные значения типов данных

using System;

namespace ConsoleApp6

{

class MinAndMax

{

static void Main(string[] args)

{

Console.WriteLine("sbyte: {0} to {1}", sbyte.MinValue, sbyte.MaxValue);

Console.WriteLine("byte: {0} to {1}", byte.MinValue, byte.MaxValue);

Console.WriteLine("short: {0} to {1}", short.MinValue, short.MaxValue);

Console.WriteLine("ushort: {0} to {1}", ushort.MinValue, ushort.MaxValue);

Console.WriteLine("int: {0} to {1}", int.MinValue, int.MaxValue);

Console.WriteLine("uint: {0} to {1}", uint.MinValue, uint.MaxValue);

Console.WriteLine("long: {0} to {1}", long.MinValue, long.MaxValue);

Console.WriteLine("float: {0} to {1}", float.MinValue, float.MaxValue);

Console.WriteLine("double: {0} to {1}", double.MinValue, double.MaxValue);

Console.WriteLine("decimal: {0} to {1}", decimal.MinValue, decimal.MaxValue); [15]

}

}

}[16]

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

Наибольшее понимание любого вопроса дает конечно же практика, для этого заводим аккаунт и устанавливаем на ПК программу Visual Studio 2017 c официального сайта https://www.microsoft.com. Далее, перейдя «File - New – Projekt – ConsoleApp(.Net Core) – ОК», создаем новый проект консольной программы, вводим необходимые команды и переходим по «Debug – Start Without Debugging». Как результат – перед нами исполнение написанной программы в виде консольного окна. Стоит также заметить, что нам часто придется использовать встроенный метод Console.WriteLine.

Объявление переменных, синтаксис элементарных типов, безопасность типов

С# является жестко типизированным языком. При его использовании вы должны объявлять тип каждого объекта, который создаете (например, целые числа, числа с плавающей точкой, строки, окна, кнопки, и т. д.), и компилятор поможет вам избежать ошибок, связанных с присвоением переменным значений только того типа, который им соответствует. Тип объекта указывает компилятору размер объекта (например, объект типа int занимает в памяти 4 байта) и его свойства (например, форма может быть видима и невидима, и т.д.).[17]

Объявление переменных осуществляем следующим образом: первым делом указываем тип, следом - ее имя:

int z;

Вторым шагом нужно провести инициализацию, то есть присвоить значение до первого использования:

z=0;

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

При желании можно сократить запись в одну строку и указать даже несколько переменных:

int z=0; w=1;

Некоторые системные типы данных требуют особого пояснения. Первый из этих типов - System.Boo1ean. В отличие от С и C++ теперь присваивать переменным этого типа действительно можно только два значения - t r u e (истинно) и false (ложно). Если в С и C++ мы вполне могли присваивать

аналогичному логическому типу данных и 0, и 1, и -1, то С# уже такого не допускает:

// Вольница с типом bool закончилась! [18]

bool b = 0; // Так в С# уже нельзя.

bool b2 = -1; // Так тоже нельзя.

bool b3 = true; // А вот так - можно.

bool b4 = false; // И так можно.

Еще одна важная вещь, о которой нельзя не упомянуть, - то, что для текстовых данных в С# теперь используются только два типа данных: string и char. Думаю, что многие программисты почувствуют невыразимое облегчение, узнав, что отпала потребность в таких замечательных типах данных, как char*, wchar_t*, LPSTR, LPCSTR, BSTR и OLECHAR. Многие программисты согласятся со мной, что операции с текстовыми данными в СОМ и Win32 производились не самым простым способом. В этом отношении .NET, где для работы с текстом предназначены только два типа данных (оба работают с Unicode), - это большой шаг вперед. [19]

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

System.Int32 = new System.Int32();

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

int а = 0;

Такой код читается намного лучше, да и компилятор в обоих случаях генерирует идентичный IL-код для System.Int32. Типы данных, которые поддерживаются компилятором напрямую, называются элементарными типами (primitive types) и отображаются им в типы из библиотеки классов NET Framework Class Library (PCL). Так, С#-типу int соответствует System.Int32. Значит, весь следующий код компилируется без ошибок и преобразуется в одинаковые команды IL: [20]

int a = 0; // Самый удобный синтаксис.

System.Int32 а = 0; // Удобный синтаксис.

int a = new int(); // Неудобный синтаксис.

System.Int32 а = new System.In32 (); // Самый неудобный синтаксис. [21]

Несколько лет назад запущенная в космос ракета разбилась через несколько минут после запуска, что стоило NАSA миллионов долларов. В результате расследования было обнаружено, что катастрофа произошла по вине программного обеспечения. В частности, ошибка была найдена в строке кода на С, и которой программист ошибочно использовал один знак равно (=) вместо требовавшихся двух (==), т.е. оператор присваивания, а не проверку на равенство. Из-за того, что С не обеспечивает безопасности типов, программа для ракеты неявно преобразовала результат присваивания в логическое значение true.

Если вы хоть немного программировали на С/С++, то наверняка совершали похожие ошибки. Такие ошибки трудно обнаружить; можно провести часы, просматривая исходный код и поисках неявного приведения типа, вызывающего проблему. Трата времени на поиски подобных ошибок неприемлема в корпоративных средах.

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

Более того, С# позволяет задавать, как определенные пользователем типы будут вести себя в контексте неявных и явных преобразований типов. [22]

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

Область действия переменных

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

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

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

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

Переменная должна быть определена до первого ее использования.

Если переменная объявлена в каком-то блоке и в этот блок после объявления переменной входит другой блок, то оговоренная переменная доступна и во вложенном блоке.

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

В листинге 2 приведена программа, систематизирующая правила доступа к переменной. Результат представлен на рис. 3.

Переменные i и j объявлены в блоке Main и доступны во всей программе. Используем i как счетчик итераций внешнего блока-тела for, а j — как суммарный счетчик итераций вложенного блока for. Вывод их окончательных значений расположен в конце программы, за границами внешнего блока for. Переменная m объявлена во внешнем блоке for и поэтому может быть использована как счетчик итераций вложенного блока for. Используется во вложенном блоке, а выведена во внешнем (в том же, где объявлена). [23]

После выхода из внешнего блока for, то есть уже в блоке Main, снова объявить переменную с именем m нельзя, так как идентификатор уже занят во внешнем блоке for, внутреннем по отношению к блоку Main.

Переменные с одинаковыми именами могут объявляться в последовательных блоках, то есть тех, которые не являются друг для друга ни внешними, ни внутренними. Такие два блока с переменой k (тела двух последовательно записанных циклов for) завершают программу в листинге 2.

Листинг 2 - Доступ к переменным

using System;

namespace ConsoleApp7

{

class Program

{

static void Main(string[] args)

{

int i = 0, j = i; // для всей программы – блока Main

for (; (i + j) < 11;)

{//внешний блок

++i; // счетчик внешнего блока

int m = 0; //переменная для внешнего и вложенного блоков

for (; (m + j) < 17;)

{//вложенный блок

m++; //счетчик одного вложенного блока

j++; //общий счетчик вложенных блоков

}// конец вложенного блока[24]

Console.WriteLine(" внутренний блок: m ="+m);

}//конец внешнего блока

Console.WriteLine("\n\n внешний блок: i ="+i);

Console.WriteLine(" все вложенные блоки: j ="+j);

Console.WriteLine("\n последовательные блоки");

i = 0; // счетчик

for (int k = 0; k < 21; k++) i++;

Console.WriteLine(" i ="+i);

i = 0; // счетчик

for (int k = 0; k < 321; k++) i++;

Console.WriteLine(" i ="+i +"\n");

}

}

}

Рисунок 3 - Результат исполнения программы в листинге 2[25]

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

Арифметические операции. Приоритеты операций. Логические операции

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

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

i = k+d*i/m%n-1;

действия выполняются в следующем порядке: умножение, деление, вычисление остатка, сложение, вычитание.

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

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

Примеры сокращенных записей:

Операции присваивания:

с+=3; равнозначно с=с+3;

с-=3; равнозначно с=с-3;

с*=3; равнозначно с=с*3;

с/=3; равнозначно с=с/З;

с%=3; равнозначно с=с%3;

Некоторые авторы считают, что +=, -=, *=, /=и %= также являются опера- торами. С этим трудно согласиться, так как здесь предполагаются сочетания двух операций.

Операция инкремента:

i++; постфиксная форма, равнозначно i=i+1; или i+=1;

++i; префиксная форма, равнозначно i=i+1; или i+=1; [26]

Операция декремента:

i--; постфиксная форма, равнозначно i=i-1; или i-=1;

--i; префиксная форма, равнозначно i=i-1; или i-=1;

Операции инкремента и декремента применяются к целым числам и к числам с плавающей точкой. Различия между постфиксной и префиксной формами иллюстрируются приведенным ниже примером.

k=a+(i++)+d*(--j);

равнозначно

--j; k=a+i+d*j; i++;

Заметим, что в приведенном примере круглые скобки не изменяют приоритеты, а отделяют друг от друга операторы * и -- во избежание их превратного толкования компилятором или программистом, хотя можно было бы ограничиться вставкой пробела. Разумеется, такие выражения, как, например:

++i; и i++;

равнозначны.

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

If (a<b) a+=b;

Если условие a<b выполняется, то есть истинно (true), производится операция сложения a+=b; . Если условие a<b не выполняется, то есть ложно (false), операция сложения не производится (игнорируется).

В С# на равенство (= =) и неравенство (!=) можно проверять все переменные, но операторы сравнения (< и >) можно применять только к переменным числовых типов и типа char. В последнем случае сравниваются коды символов. [27]

Решение может приниматься в результате анализа не обязательно одного условия. Условия связываются знаками логического сложения && (И) или логического умножения || (ИЛИ).

Например:

if (i= = j && k!=5) a=1.2;

if (i= =j || k!=5) a=1.2;

В первом случае для присваивания переменной а значения 1.2 необходимо выполнение обоих условий, во втором — достаточно соблюдения одного из условий. Второй операнд условия вычисляется только при необходимости. Например: если не выполняется условие i= = j, то в первом выражении второе условие k! =5 не проверяется, если условие i= = j выполняется, то во втором выражении условие k! =5 не проверяется.

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

Примеры операций сравнения и логических показаны в листинге 3. Результат исполнения программы см. на рисунке 4.

В первом примере производится сравнение значений целых переменных i и j. При выполнении условия i>j, то есть true, выводится соответствующая надпись.

Далее сравниваются переменные типа double. Если условие d1>d2 не выполняется, то есть false, то обусловленное им действие игнорируется.

Третий абзац демонстрирует сочетания двух условий, связанных операторами || и &&.

В последнем абзаце используются непосредственно булевы переменные (b1 и b2).

Листинг 3 - Логические операции

using System;

namespace ConsoleApp4[28]

{

class Program

{

static void Main(string[] args)

{

int i = 55, j = -12;

if (i >j) Console.WriteLine(" i>j");//выполняется

double d1 = 0.34, d2 = 99.2, d = 0.6e-15;

if (d1 > d2) Console.WriteLine(" d1>d2");//не выполняется

if (d1 < d2) Console.WriteLine("d1<d2");//выполняется

if (d1 - d2 > d || d1 - d2 < d) Console.WriteLine("||");/*выполняется втрое условие*/

if (i > j && j <= 1) Console.WriteLine(" &&");//выполн.

if (i == 55 && j <= 1) Console.WriteLine(" !! &&"); //выполн.

bool b1 = true, b2 = false;

if (b1) Console.WriteLine(" " + b1);//выполняется

if (b2) Console.WriteLine(" " + b2);//не выполняется

if (!b2) Console.WriteLine(" "+!b2);//выполняется, !b2=true

if (b1!= b2) Console.WriteLine(" b1!=b2");//выполняется

if (true) Console.WriteLine(" *true* ");//выполняется

if (false) Console.WriteLine(" *false* ");//не выполняется

if (!false) Console.WriteLine(" *!false* ");//выполняется, !false=true

if (b1 != b2) Console.WriteLine(" b1 != b2");//выполняется

}//конец Main

}//конец Program

}[29]

Capture5

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

Резюмируя написанное важно отметить, что в перспективе удобно применять сокращенные варианты арифметических операций, а также логические операторы && (И) и || (ИЛИ) при работе с переменными.

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

Подобно языкам 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]

Capture2

Рисунок 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 (общей суммы) запрещено*/

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

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

Например,

byte ib=21; int i=2147483640;

int j=ib+i; /*результат 2147483861 не может разместиться в памяти, отведенной типу int, результат не достоверен*/[45]

В процессе обработки программы транслятор выполняет явные и неявные преобразования типов данных. [46]

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

int i=16; byte ib=(byte)i; /*переменная типа int приведена к типу byte, результат присвоен ib типа byte*/

double d=1.0e+100; float f=(float)d; /*переменная типа double приведена к типу float, результат присвоен f типа float, потеря информации*/

Естественно, потеря информации при этом контролируется только самим программистом. В C# разрешено принудительное приведение любых базовых типов, кроме типа bool. Тип и значения «приводимых» переменных после[47] операции приведения не изменяются и далее в программе они используются в прежнем качестве.

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

В первом абзаце демонстрируется операция вычитания двух переменных @byte и @byte1 типа byte. Оба операнда автоматически приводятся к типу int, после чего выполняется вычитание и результат присваивается переменной @int типа int.

Далее отрицательная переменная (@int=-44) последовательно явным образом приводится к типам byte, uint и long. Только последняя операция приведения дает достоверный результат.

В третьем абзаце показаны манипуляции с символами и их кодами. Задаются два символа & и 1. Явное преобразование в тип char выявляет коды 38 и 49 соответственно. Перед сложением двух символьных переменных происходит автоматическое приведение слагаемых, результат присваивается переменной типа int. Полученная сумма явно приводится к типу char, и выясняется, что это код символа W. Если компьютер «не знает» символа с таким кодом, будет выведен вопросительный знак. [48]

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

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

first

second

byte

sbyte

short

ushort

int

uint

long

ulong

char

float

double

decimal

byte

int

int

int

int

int

uint

long

ulong

int

float

double

decimal

sbyte

int

int

int

int

int

long

long

!!!

int

float

double

decimal

short

int

int

int

int

int

long

long

!!!

int

float

double

decimal

ushort

int

int

int

int

int

uint

long

ulong

int

float

double

decimal

int

int

int

int

int

long

long

!!!

int

float

double

decimal

uint

ulong

long

long

ulong

long

ulong

long

ulong

uint

float

double

decimal

long

long

long

long

long

long

long

!!!

long

float

double

decimal

ulong

ulong

!!!

!!!

ulong

!!!

ulong

!!!

ulong

float

double

decimal

char

int

int

int

int

int

uint

long

ulong

int

float

double

decimal

float

float

float

float

float

float

float

float

float

float

double

!!!

double

double

double

double

double

double

double

double

double

double

double

!!!

decimal

decimal

decimal

decimal

decimal

decimal

decimal

decimal

decimal

decimal

!!!

!!!

dynamic

!!! ошибочный результат;[49]

приведение обоих операндов;

без приведения. [50]

Заключение

Любое прочтение теоретического материала лучше всего сразу же закреплять практическими навыками. Поэтому изначально было принято решение об установке среды разработки приложений Microsoft Visual Studio и отработке в ней всех листингов, встречаемых по пути ознакомления с материалом из книг и учебников.

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

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

В целом были раскрыты все поставленные задачи.

Библиография

1. Фленов М.Е. - Библия С# / М.Е. Фленов. - 3-изд., СПб.: Питер, 2016. – 546 c.

2. Ишкова Э.А. - C#. Начала программирования / Э.А.Ишкова. - 2 изд., СПб.: НиТ, 2013. – 494 с.

3. Svetlin, Nakov, Veselin, Kolev & Co. – Fundamentals of computer programming / Svetlin, Nakov, Veselin, Kolev & Co., URL: https://introprogramming.info/english-intro-csharp-book/ (Дата обращения: 11.03.2019 ), 2013. – 1122 с.

4. Дубовцев А.В. Microsoft .NET. Наиболее полное руководство. / А.В. Дубовцев.– СПб.: БХВ-Петербург, 2004. – 701 с.

5. Петцольд Ч. - Программирование для Microsoft Windows на С#. / Ч. Петцольд. - Том 1, М.: Издательско – торговый дом «Русская Редакция», 2002. -576 с.

6. Лабор В.В. - C# Создание приложений для Windows. / В.В. Лабор – Мн.: Харвест, 2003. – 384 с.

7. Троэлсен Э. - C# и платформа NET Библиотека программиста. / Э.Троэлсен. - СПб.: Питер, 2004. – 796 с.

8. Рихтер Д. – CLR via C# Программирование на платформе Microsoft .NET Framework 2.0 на языке C#. / Д.Рихтер – СПб.: Питер, 2008. – 656 с.

9. Робинсон С. - C# для профессионалов. / С.Робинсон - Tом 1, М.: Лори, 2003. –516 с.

10. Рейли Д. Создание приложений ASP.NET. / Д. Рейли, - М.: Издательско – торговый дом «Русская Редакция», 2002. -480 с.

11. Microsoft Corporation - Разработка Windows приложений на Microsoft Visual Basic NET и Microsoft Visual C# NET Учебный курс MCAD MCSD / Microsoft Corporation. - М.: Издательско – торговый дом «Русская Редакция», 2003. -512 с.

12. Фролов А.В., Фролов Г.В. Язык C#. Самоучитель. / А.В.Фролов, Г.В.Фролов – М.: ДИАЛОГ – МИФИ, 2003. – 560 с.

  1. Фленов М.Е. - Библия С#, 3-изд. – 2016. - С.50-51.

  2. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.33.

  3. Svetlin, Nakov, Veselin, Kolev & Co. – Fundamentals of computer programming. - 2013. – C.111.

  4. Svetlin, Nakov, Veselin, Kolev & Co. – Fundamentals of computer programming. - 2013. – C.111.

  5. Дубовцев А.В. Microsoft .NET. Наиболее полное руководство. – 2004. - С.34.

  6. Фленов М.Е. - Библия С#, 3-изд. – 2016. - С.51.

  7. Фленов М.Е. - Библия С#, 3-изд. – 2016. - С.51.

  8. Svetlin, Nakov, Veselin, Kolev & Co. – Fundamentals of computer programming. - 2013. – C.111.

  9. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.33.

  10. Фленов М.Е. - Библия С#, 3-изд. – 2016. - С.53.

  11. Svetlin, Nakov, Veselin, Kolev & Co. – Fundamentals of computer programming. - 2013. – C.112.

  12. Svetlin, Nakov, Veselin, Kolev & Co. – Fundamentals of computer programming. - 2013. – C.112.

  13. Фленов М.Е. - Библия С#, 3-изд. – 2016. - С.52.

  14. Фленов М.Е. - Библия С#, 3-изд. – 2016. - С.52.

  15. Петцольд Ч. - Программирование для Microsoft Windows на С#. Том 1. -2002. -C.10-11.

  16. Петцольд Ч. - Программирование для Microsoft Windows на С#. Том 1. -2002. -C.10-11.

  17. Лабор В.В. - C# Создание приложений для Windows. – 2003. - С.52.

  18. Троэлсен Э. - C# и платформа NET Библиотека программиста – 2004. - C.98.

  19. Троэлсен Э. - C# и платформа NET Библиотека программиста – 2004. - C.98.

  20. Рихтер Д. – CLR via C# Программирование на платформе Microsoft .NET Framework 2.0 на языке C# – C.111.

  21. Рихтер Д. – CLR via C# Программирование на платформе Microsoft .NET Framework 2.0 на языке C# – C.111.

  22. Робинсон С. - C# для профессионалов Tом 1. – 2004. –C.45.

  23. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.72-73.

  24. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.73.

  25. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.73-74.

  26. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.37.

  27. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.37-38, 46.

  28. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.46-47.

  29. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.47.

  30. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.47.

  31. Лабор В.В. - C# Создание приложений для Windows. – 2003. - С.52.

  32. Рейли Д. Создание приложений ASP.NET. – 2002. – С.47-49.

  33. Лабор В.В. - C# Создание приложений для Windows. – 2003. - С.52.

  34. Svetlin, Nakov, Veselin, Kolev & Co. – Fundamentals of computer programming. - 2013. – C.80.

  35. Лабор В.В. - C# Создание приложений для Windows. – 2003. - С.52.

  36. Microsoft Corporation - Разработка Windows приложений на Microsoft Visual Basic NET и Microsoft Visual C# NET Учебный курс MCAD MCSD - 2003. – С.7-8.

  37. Лабор В.В. - C# Создание приложений для Windows. – 2003. - С.52-53.

  38. Лабор В.В. - C# Создание приложений для Windows. – 2003. - С.53.

  39. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.38-39.

  40. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.39.

  41. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.39-40.

  42. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.40-41.

  43. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.40-41.

  44. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.41.

  45. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.42-43.

  46. Фролов А.В., Фролов Г.В. Язык C#. Самоучитель.– 2003. - С.70.

  47. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.43.

  48. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.43-44.

  49. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.44.

  50. Ишкова Э.А. - C#. Начала программирования, 2 изд. – 2013. - С.44.