Главная страница
Навигация по странице:

  • Правила действия областей видимости функций

  • Локальные переменные

  • Объявление переменных в итерационных инструкциях и инструкциях выбора

  • Формальные параметры

  • Глобальные переменные

  • Шилдт c++_базовый_курс издание 3. Герберт Шилдт С базовый курс


    Скачать 9.37 Mb.
    НазваниеГерберт Шилдт С базовый курс
    АнкорШилдт c++_базовый_курс издание 3.pdf
    Дата13.02.2017
    Размер9.37 Mb.
    Формат файлаpdf
    Имя файлаШилдт c++_базовый_курс издание 3.pdf
    ТипКнига
    #2637
    страница7 из 33
    1   2   3   4   5   6   7   8   9   10   ...   33
    Глава 7:Функции, часть первая: ОСНОВЫ
    В этой главе мы приступаем к углубленному рассмотрению функций. Функции — это строительные блоки C++, а потому без полного их понимания невозможно стать успешным
    С++-программистом. Мы уже коснулись темы функций в главе 2 и использовали их в каждом примере программы. В этой главе мы познакомимся с ними более детально. Данная тема включает рассмотрение правил действия областей видимости функций, рекурсивных функций, некоторых специальных свойств функции main(), инструкции return и прототипов функций.
    Правила действия областей видимости функций
    Правила действия областей видимости определяют возможность получения доступа к
    объекту и время его существования.
    Правила действия областей видимости любого языка программирования — это правила, которые позволяют управлять доступом к объекту из различных частей программы.
    Другими словами, правила действия областей видимости определяют, какой код имеет доступ к той или иной переменной. Эти правила также определяют время "жизни"
    переменной. Как упоминалось выше, существует три вида переменных: локальные переменные, формальные параметры и глобальные переменные. На этот раз мы рассмотрим правила действия областей видимости с точки зрения функций.
    Локальные переменные
    Как вы уже знаете, переменные, объявленные внутри функции, называются локальными.
    Но в C++ предусмотрено более "внимательное" отношение к локальным переменным, чем мы могли заметить до сих пор. В C++ переменные могут быть включены в блоки. Это означает, что переменную можно объявить внутри любого блока кода, после чего она будет локальной по отношению к этому блоку. (Помните, что блок начинается с открывающей фигурной скобки и завершается закрывающей.) В действительности переменные, локальные по отношению к функции, образуют просто специальный случай более общей идеи.
    Локальную переменную могут использовать лишь инструкции, включенные в блок, в котором эта переменная объявлена. Другими словами, локальная переменная неизвестна за пределами собственного блока кода. Следовательно, инструкции вне блока не могут получить доступ к объекту, определенному внутри блока.
    Важно понимать, что локальные переменные существуют только во время выполнения программного блока, в котором они объявлены. Это означает, что локальная переменная создается при входе в "свой" блок и разрушается при выходе из него. А поскольку локальная переменная разрушается при выходе из "своего" блока, ее значение теряется.
    Самым распространенным программным блоком является функция. В C++ каждая функция определяет блок кода, который начинается с открывающей фигурной скобки этой функции и завершается ее закрывающей фигурной скобкой. Код функции и ее данные — это ее "частная собственность", и к ней не может получить доступ ни одна инструкция из любой другой функции, за исключением инструкции ее вызова. (Например, невозможно использовать инструкцию goto для перехода в середину кода другой функции.) Тело функции надежно скрыто от остальной части программы, и если в функции не
    используются глобальные переменные, то она не может оказать никакого влияния на другие части программы, равно, как и те на нее. Таким образом, содержимое одной функции совершенно независимо от содержимого другой. Другими словами, код и данные,
    определенные в одной функции, не могут взаимодействовать с кодом и данными,
    определенными в другой, поскольку две функции имеют различные области видимости.
    Поскольку каждая функция определяет собственную область видимости, переменные,
    объявленные в одной функции, не оказывают никакого влияния на переменные,
    объявленные в другой, причем даже в том случае, если эти переменные имеют одинаковые имена. Рассмотрим, например, следующую программу.
    #include
    using namespace std;
    void f1();
    int main()
    {
    char str[] = "Это - массив str в функции main().";
    cout << str << '\n';
    f1();
    cout << str << '\n';
    return 0;
    }
    void f1()
    {
    char str[80];
    cout << "Введите какую-нибудь строку: ";
    cin >> str;
    cout << str << '\n';
    }
    Символьный массив str объявляется здесь дважды: первый раз в функции main() и еще раз — в функции f1(). При этом массив str, объявленный в функции main(), не имеет никакого отношения к одноименному массиву из функции f1(). Как разъяснялось выше,
    каждый массив (в данном случае str) известен только блоку кода, в котором он объявлен.
    Чтобы убедиться в этом, достаточно выполнить приведенную выше программу. Как видите,
    несмотря на то, что массив str получает строку, вводимую пользователем при выполнении функции f1(), содержимое массива str в функции main() остается неизменным.
    Язык C++ содержит ключевое слово auto, которое можно использовать для объявления локальных переменных. Но поскольку все неглобальные переменные являются по умолчанию auto-переменными, то к этому ключевому слову практически никогда не прибегают. Поэтому вы не найдете в этой книге ни одного примера с его использованием.
    Но если вы захотите все-таки применить его в своей программе, то знайте, что размещать его нужно непосредственно перед типом переменной, как показано ниже.
    auto char ch;
    Обычной практикой является объявление всех переменных, используемых в функции, в начале программного блока этой функции. В этом случае всякий, кому придется разбираться в коде этой функции, легко узнает, какие переменные в ней используются. Тем не менее начало блока функции — это не единственно возможное место для объявления локальных переменных. Локальные переменные можно объявлять в любом месте блока кода. Переменная, объявленная в блоке, локальна по отношению к этому блоку. Это означает, что такая переменная не существует до тех пор, пока не будет выполнен вход в блок, а разрушение такой переменной происходит при выходе из ее блока. При этом никакой код вне этого блока не может получить доступ к этой переменной (даже код,
    принадлежащий той же функции).
    Чтобы лучше понять вышесказанное, рассмотрим следующую программу.
    /*
    Эта программа демонстрирует локальность переменных по отношению к блоку.
    */
    #include
    #include
    using namespace std;
    int main()
    {
    int choice;
    cout << "(1) сложить числа или ";
    cout << "(2) конкатенировать строки?: ";
    cin >> choice;
    if(choice == 1) {
    int a, b; /* Активизируются две int-переменные. */
    cout << "Введите два числа: ";
    cin >> а >> b;
    cout << "Сумма равна " << a+b << '\n';
    }
    else {
    char s1 [80], s2[80]; /* Активизируются две строки. */
    cout << "Введите две строки: ";
    cin >> s1;
    cin >> s2;
    strcat(s1, s2);
    cout << "Конкатенация равна " << s1 << '\n';
    }
    return 0;
    }
    Эта программа в зависимости от выбора пользователя обеспечивает ввод либо двух чисел, либо двух строк. Обратите внимание на объявление переменных а и b в if-блоке и переменных s1 и s2 в else-блоке. Существование этих переменных начнется с момента входа в соответствующий блок и прекратится сразу после выхода из него. Если пользователь выберет сложение чисел, будут созданы переменные а и b, а если он захочет конкатенировать строки— переменные s1 и s2. Наконец, ни к одной из этих переменных нельзя обратиться извне их блока, даже из части кода, принадлежащей той же функции.
    Например, если вы попытаетесь скомпилировать следующую (некорректную) версию программы, то получите сообщение об ошибке.

    /* Эта программа некорректна. */
    #include
    #include
    using namespace std;
    int main()
    {
    int choice;
    cout << "(1) сложить числа или ";
    cout << "(2) конкатенировать строки?: ";
    cin >> choice;
    if(choice == 1) {
    int a, b; /* Активизируются две int-переменные. */
    cout << "Введите два числа: ";
    cin >> а >> b;
    cout << "Сумма равна " << a+b << '\n';
    }
    else {
    char s1 [80], s2 [80]; /* Активизируются две строки. */
    cout << "Введите две строки: ";
    cin >> s1;
    cin >> s2;
    strcat (s1, s2);
    cout << "Конкатенация равна " << s1 << '\n';

    }
    a = 10; // *** Ошибка ***
    // Переменная а здесь неизвестна!
    return 0;
    }
    Поскольку в данном случае переменная а неизвестна вне своего if-блока, компилятор выдаст ошибку при попытке ее использовать.
    Если имя переменной, объявленной во внутреннем блоке, совпадает с именем переменной, объявленной во внешнем блоке, то "внутренняя" переменная переопределяет "внешнюю" в пределах области видимости внутреннего блока. Рассмотрим пример.
    #include
    using namespace std;
    int main()
    {
    int i, j;
    i = 10;
    j = 100;
    if(j > 0) {
    int i; // Эта переменная i отделена от внешней переменной i.
    i = j /2;
    cout << "Внутренняя переменная i: " << i << '\n';
    }
    cout << "Внешняя переменная i: " << i << '\n';
    return 0;
    }

    Вот как выглядят результаты выполнения этой программы.
    Внутренняя переменная i: 50
    Внешняя переменная i: 10
    Здесь переменная i, объявленная внутри if-блока, переопределяет, или скрывает,
    внешнюю переменную i. Изменения, которым подверглась внутренняя переменная i, не оказывают никакого влияния на внешнюю i. Более того, вне if-блока внутренняя переменная
    i больше не существует, и поэтому внешняя переменная i снова становится видимой.
    Поскольку локальные переменные создаются с каждым входом и разрушаются с каждым выходом из программного блока, в котором они объявлены, они не хранят своих значений между активизациями блоков. Это особенно важно помнить в отношении функций. При вызове функции ее локальные переменные создаются, а при выходе из нее — разрушаются.
    Это означает, что локальные переменные не сохраняют своих значений между вызовами функций. (Существует один способ обойти это ограничение — он будет рассмотрен ниже в этой книге.)
    Локальные переменные не хранят своих значений между активизациями.
    Локальные переменные хранятся в стеке, если не задан иной способ хранения.
    Поскольку стек — это динамически изменяемая область памяти, локальные переменные не могут в общем случае сохранять свои значения между вызовами функций.
    Как упоминалось выше, несмотря на то, что локальные переменные обычно объявляются в начале своего блока, это не является обязательным. Локальные переменные можно объявить в любом месте блока, главное, чтобы это было сделано до их использования.
    Например, следующая программа вполне допустима.
    #include
    using namespace std;
    int main()
    {
    cout << "Введите число: ";
    int a; // Объявляем одну переменную.
    cin >> a;
    cout << "Введите второе число: ";
    int b; // Объявляем еще одну переменную.
    cin >> b;
    cout << "Произведение равно: " << а*Ь << '\n';
    return 0;
    }
    В этом примере переменные а и b не объявляются до тех пор, пока они станут нужными.
    Все же большинство программистов объявляют все локальные переменные в начале блока, в котором они используются, но это, как говорится, вопрос стилистики (или вкуса).
    Объявление переменных в итерационных инструкциях и инструкциях выбора
    Переменную можно объявить в разделе инициализации цикла for или условном выражении инструкций if, switch или while. Переменная, объявленная в одной из этих инструкций, имеет область видимости, которая ограничена блоком кода, управляемым этой инструкцией. Например, переменная, объявленная в инструкции цикла for, будет локальной для этого цикла, как показано в следующем примере.
    #include
    using namespace std;
    int main()
    {
    // Переменная i локальная для цикла for.
    for(int i=0; i<10; i++) {
    cout << i << " ";
    cout << "в квадрате равно " << i * i << "\n";
    }
    // i = 10; // *** Ошибка *** -- i здесь неизвестна!
    return 0;
    }
    Здесь переменная i объявляется в разделе инициализации цикла for и используется для
    управления этим циклом. А за пределами цикла переменная i неизвестна.
    В общем случае, если управляющая переменная цикла for не нужна за пределами этого цикла, то объявление ее внутри for-инструкции (как показано в этом примере) хорошо тем,
    что оно ограничивает ее существование рамками цикла и тем самым предотвращает случайное использование в каком-то другом месте программы. Профессиональные программисты часто объявляют управляющую переменную цикла внутри for-инструкции.
    Но если переменная требуется коду вне цикла, ее нельзя объявлять в инструкции for.
    Важно! Утверждение о том, что переменная, объявленная в разделе инициализации
    цикла for, является локальной по отношению к этому циклу или не является таковой,
    изменилось со временем (имеется в виду время, в течение которого развивался язык C++).
    Первоначально такая переменная была доступна после выхода из цикла for. Однако
    стандарт C++ ограничивает область видимости этой переменной рамками цикла for. Но
    следует иметь в виду, что различные компиляторы и теперь по-разному "смотрят" на эту
    ситуацию.
    Если ваш компилятор полностью соблюдает стандарт C++, то вы можете также объявить переменную в условном выражении инструкций if, switch или while. Например, в следующем фрагменте кода if(int х = 20) {
    cout << "Это значение переменной х: ";
    cout << х;
    }
    объявляется переменная х, которой присваивается число 20. Поскольку это выражение оценивается как истинное, инструкция cout будет выполнена. Область видимости переменных, объявленных в условном выражении инструкции, ограничивается блоком кода,
    управляемым этой инструкцией. Следовательно, в данном случае переменная х неизвестна за пределами инструкции if. По правде говоря, далеко не все программисты считают объявление переменных в условном выражении инструкций признаком хорошего стиля программирования, и поэтому такой прием в этой книге больше не повторится.
    Формальные параметры
    Как вы знаете, если функция использует аргументы, она должна объявить переменные,
    которые будут принимать значения этих аргументов. Эти переменные называются
    формальными параметрами функции. Если не считать получения значений аргументов при вызове функции, то поведение формальных параметров ничем не отличается от поведения любых других локальных переменных внутри функции. Область видимости параметра ограничивается рамками его функции.
    Программист должен гарантировать, что тип объявляемых им формальных параметров совпадает с типом аргументов, передаваемых функции. И еще. Несмотря на то что эти переменные выполняют специальную задачу получения значений аргументов, их можно использовать подобно любым другим локальным переменным. Например, параметру внутри функции можно присвоить какое-нибудь новое значение.

    Глобальные переменные
    Глобальные переменные во многих отношениях противоположны локальным. Они известны на протяжении всей программы, их можно использовать в любом месте кода. и они сохраняют свои значения во время выполнения всего кода программы. Следовательно,
    их область видимости расширяется до объема всей программы. Глобальная переменная создается путем ее объявления вне какой бы то ни было функции. Благодаря их глобальности доступ к этим переменным можно получить из любого выражения, вне зависимости от функции, в которой это выражение находится.
    Если глобальная и локальная переменные имеют одинаковые имена, то преимущество находится на стороне локальной переменной. Другими словами, локальная переменная скроет глобальную с таким же именем. Таким образом, несмотря на то, что к глобальной переменной теоретически можно получить доступ из любого кода программы, практически это возможно только в случае, если одноименная локальная переменная не переопределит глобальную.
    Использование глобальных переменных демонстрируется в следующей программе. Как видите, переменные count и num_right объявлены вне всех функций, следовательно, они—
    глобальные. Из обычных практических соображений лучше объявлять глобальные переменные поближе к началу программы. Но формально они просто должны быть объявлены до их первого использования. Предлагаемая для рассмотрения программа— всего лишь простой тренажер по выполнению арифметического сложения. Сначала пользователю предлагается указать количество упражнений. Для выполнения каждого упражнения вызывается функция drill(), которая генерирует два случайных числа в диапазоне от 0 до 99.
    Пользователю предлагается сложить эти числа, а затем проверяется ответ. На каждое упражнение дается три попытки. В конце программа отображает количество правильных ответов. Обратите особое внимание на глобальные переменные, используемые в этой программе.
    // Простая программа-тренажер по выполнению сложения.
    #include
    #include
    using namespace std;
    void drill();
    int count; // Переменные count и num_right — глобальные.
    int num_right;
    int main()
    {
    cout << "Сколько практических упражнений: ";
    cin >> count;
    num_right = 0;
    do {
    drill(); count--;
    }while(count);
    cout << "Вы дали " << num_right<< " правильных ответов.\n";
    return 0;
    }
    void drill()
    {
    int count; /* Эта переменная count — локальная и никак не связана с одноименной глобальной.*/
    int а, b, ans;
    // Генерируем два числа между 0 и 99.
    а = rand() % 100;
    b = rand() % 100;
    // Пользователь получает три попытки дать правильный ответ.
    for(count=0; count<3; count++) {
    cout << "Сколько будет " << а << " + " << b << "? ";
    cin >> ans;
    if(ans==a+b) {
    cout << "Правильно\n";
    num_right++;
    return;
    }
    }
    cout << "Вы использовали все свои попытки.\n";
    cout << "Ответ равен " << a+b << '\n';
    }
    При внимательном изучении этой программы вам должно быть ясно, что как функция
    main(), так и функция drill() получают доступ к глобальной переменной num_right. Но с переменной count дело обстоит несколько сложнее. В функции main() используется глобальная переменная count. Однако в функции drill() объявляется локальная переменная
    count. Поэтому здесь при использовании имени count подразумевается именно локальная, а не глобальная переменная count. Помните, что, если в функции глобальная и локальная переменные имеют одинаковые имена, то при обращении к этому имени подразумевается локальная, а не глобальная переменная.
    Хранение глобальных переменных осуществляется в некоторой определенной области памяти, специально выделяемой программой для этих целей. Глобальные переменные полезны в том случае, когда в нескольких функциях программы используются одни и те же данные, или когда переменная должна хранить свое значение на протяжении выполнения всей программы. Однако без особой необходимости следует избегать использования глобальных переменных, и на это есть три причины.
    ■ Они занимают память в течение всего времени выполнения программы, а не только тогда, когда действительно необходимы.
    ■ Использование глобальной переменной в "роли", с которой легко бы "справилась"
    локальная переменная, делает такую функцию менее универсальной, поскольку она полагается на необходимость определения данных вне этой функции.
    ■ Использование большого количества глобальных переменных может привести к появлению ошибок в работе программы, поскольку при этом возможно проявление неизвестных и нежелательных побочных эффектов. Основная проблема, характерная для разработки больших С++-программ, — случайная модификация значения переменной в каком-то другом месте программы. Чем больше глобальных переменных в программе, тем больше вероятность ошибки.
    1   2   3   4   5   6   7   8   9   10   ...   33


    написать администратору сайта