Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс_ред1 С#.doc
Скачиваний:
2
Добавлен:
01.03.2025
Размер:
1.8 Mб
Скачать

Область действия переменных и управляющие конструкции

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

int i;

for (i = 0; i < 10; i++)

{

string text = "Строка " + Convert.ToString(i);

Console.WriteLine("{0}", text);

}

Console.WriteLine("Последняя строка, напечатанная в цикле: {0}", text);

В данной программе строковая переменная text является локальной для цикла for. Такой код не пройдет компиляцию, поскольку в обращении к Console.WriteLine(), которое происходит вне этого цикла, делается попытка использовать переменную text, область действия которой не распространяется за пределы цикла. Изменим код следующим образом:

int i;

string text;

for (i = 0; i < 10; i++)

{

text = "Строка " + Convert.ToString(i);

Console.WriteLine("{0}", text);

}

Console.WriteLine("Последняя строка, напечатанная в цикле: {0}", text);

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

int i;

string text = "";

for (i = 0; i < 10; i++)

{

text = "Строка " + Convert.ToString(i);

Console.WriteLine("{0}", text);

}

Console.WriteLine("Последняя строка, напечатанная в цикле: {0}", text);

На этот раз переменная text инициализирована вне цикла, и мы имеем доступ к ее значению. Результат выполнения этой программы приведен ниже. В данном случае значение, присвоенное переменной text внутри цикла, оказывается доступным и вне этого цикла:

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

for(int i = 0; i < 10; i++)

{

. . .

}

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

Рекурсия

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

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

using System;

namespace Factorial

{

class Program

{

static void Main(string[] args)

{

for (int i = 1; i < 10; i++)

{

Console.WriteLine("Факториал числа {0} = {1}",

i, Factorial(i));

}

Console.ReadLine();

}

static int Factorial(int x)

{

if(x == 1)

return 1;

else

return x * Factorial(x - 1);

}

}

}

Результатом работы этой программы будет:

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

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

Допустим, мы ищем факториал числа 5. Тогда передача параметров экземплярам функции Factorial идет в такой последовательности: 5, 4, 3, 2, 1. А последовательность возвращаемых значений будет следующей: 1, 2, 6, 24, 120. Результатом работы функции станет число 120.

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

return x * Factorial(x + 1);

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

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