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

  • Start Debugging

  • Рекурсивные функции

  • Борис Пахомов Санкт Петербург бхв петербург 2013 удк 004. 4 Ббк 32. 973. 26018. 2 П


    Скачать 17.38 Mb.
    НазваниеБорис Пахомов Санкт Петербург бхв петербург 2013 удк 004. 4 Ббк 32. 973. 26018. 2 П
    АнкорMS Visual C.pdf
    Дата04.08.2018
    Размер17.38 Mb.
    Формат файлаpdf
    Имя файлаMS Visual C.pdf
    ТипДокументы
    #22453
    страница8 из 37
    1   ...   4   5   6   7   8   9   10   11   ...   37
    переменных
    Мы уже говорили, что существуют внешние переменные, известные во всех объявленных ниже их функциях и блоках. Эти переменные могут быть объявлены как в тексте разрабатываемой программы, таки в текстах внешних файлов, которые следует к этой программе подключать операторами
    #include
    . В таких случаях подобные переменные объявляются в программе, нос атрибутом extern
    (например, extern int и не могут быть инициализированы при объявлении в программе. Как создать внешний файл И вообще, для чего он создается Предположим, что вы — участник разработки крупного проекта, выполняемого многими группами специалистов. При постановке задач такого проекта оказалось, что имеется довольно много общих для всех групп разработчиков данных, которые надо использовать в программах, создаваемых каждой группой. Было бы неверно, если бы программисты каждой группы описывали в своих программах общие данные. Гораздо проще все эти данные описать (объявить) водном файле с расширением, чтобы каждый программист смог им пользоваться (подключать к своей программе. К тому же при таком подходе получится, что у всех программ, использующих общий файл, переменные будут иметь одинаковые наименования и смысл, что значительно облегчит дальнейшее сопровождение программ. Как создать свой внешний файл

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

    // 4.4_2010.cpp
    //
    #include "stdafx.h"
    #include
    #include //getch()
    #include "D:\\2011.h"

    70 Часть
    I. Изучение языка С/С++
    using namespace System; int main(int argc, char* argv[])
    { extern int a; // в h-file'e a=20 printf("a=%d\n",a);
    _getch();
    } Общий файл формируется с расширением h обычным Блокнотом. В данном примере редактором был записан файл с расширением h, содержащий всего одну строку. В результате работы программы в окно вывелось сообщение рис. 4.4). Рис 4.4. Результат работы программы с внешним файлом, подготовленным пользователем
    Атрибут Наряду с внешними переменными существуют, как мы видели, локальные переменные. Локальные переменные объявлены в какой-либо функции или блоке и известны только там. При этом каждый раз при вхождении в функцию (блок) такие переменные получают значения, а при выходе эти значения теряются. Как же заставить переменные сохранять свои значения после выхода из функции (блока Такая проблема существует во множестве алгоритмов. Решить ее можно, объявив переменную с атрибутом В листинге 4.7 приведен текст проверочной программы. Для простоты взята уже известная нам функция ввода строки с клавиатуры getline()
    , ив ее тело вставлено объявление статической переменной j
    , которой там же присваивается значение количества введенных символов i
    . В режиме Отладчика (Start Debugging) можно увидеть, что локальная переменная j
    сохраняет свое значение и после выхода из функции, и после нового входа в функцию. Листинг 4.7

    // 4.3_2011
    #include "stdafx.h"
    #include для getchar(),putchar(),printf()
    #include
    Глава
    4. Создание и использование функций
    #define eof -1 //Ctrl+z
    #define maxline 100 using namespace System; Ввод строки с клавиатуры- в следующую функцию вставлены "лишние" операторы, чтобы продемонстрировать действие атрибута static*/ int getline(char s[],int lim)
    { int c,i; static int j; for(i=0; i }
    //----------------------------------------- int main()
    {
    для ввода используем объявление char *s;*/ char s[maxline]; for(int i=0; i <3; i++)
    { getline(s,maxline); static int b; b=i;
    }
    _getch();
    } В функцию getline() вставлены "лишние" операторы (
    static int j и j=i
    ), чтобы продемонстрировать действие атрибута Они не меняют функциональности getline()
    . Поставьте точку останова Отладчика режим Start Debugging) на оператор j=i ив цикле работы getline()
    , убедитесь, что переменная j
    сохраняет свое значение в других циклах запуска getline()
    . Не забудьте, что чтобы попасть в отладочном режиме внутрь функции, надо при останове на ее имени нажать клавишу , а дальше двигаться внутри по строкам путем нажатия . Для ввода можно использовать объявление char *s
    (об этой записи мы поговорим в следующих главах. В этом случае перед обращением к функции ввода надо динамически выделить память функцией s=(char*)malloc(maxline)
    , а в конце программы выполнить оператор free(s)
    — освободить память.

    72 Часть
    I. Изучение языка С/С++
    Рекурсивные функции
    Рекурсивные функции — такие функции, которые могут вызывать сами себя. При этом каждый раз под каждый вызов создается совершенно новый набор локальных переменных, отличный от набора вызывающей (те. этой же) функции. Рекурсия применяется при обработке так называемых "рекуррентных" (основанных на рекурсии) формул. Одной из таких формул является, например, формула вычисления факториала числа n! = (n – 1)!

    n, где 0! = Чтобы вычислить факториал на шаге
    n, надо воспользоваться факториалом, вычисленном на шаге (n – 1). Рекурсивная функция, реализующая алгоритм для вычисления факториала, показана в листинге. Листинг 4.8
    int fact(int i)
    { if(i==0) return(i+1); else
    { i=i * fact(i-1); return(i);
    }
    } Некоторые итоговые данные по изучению функций
    Функция в C объявляется, определяется, вызывается. Определение функции состоит из заголовка и тела. Заголовок функции состоит из спецификаторов объявления, имени функции и списка параметров. Тело функции образуется блоком операторов. Вызов функции — это выражение со списком (возможно пустым) выражений в круглых скобках. При разборе выражения вызова транслятору C требуется информация об основных характеристиках вызываемой функции. К таковым, прежде всего, относятся типы параметров, а также тип возвращаемого значения функции. При этом тип возвращаемого значения оказывается актуален лишь в том случае, если выражение вызова оказывается частью более сложного выражения. Если определение функции встречается транслятору до выражения вызова, никаких проблем не возникает. Вся необходимая к этому моменту информация о функции оказывается доступной из ее определения. При этом непринципиально фактическое расположение определения функции (вначале программы или в ее конце. Главное, чтобы в момент разбора выражения вызова транслятор знал бы все необходимое об этой функции. Но как только в исходном файле возникает ситуация, при которой вызов функции появляется в тексте программы до определения функции, разбор выраже-
    Глава
    4. Создание и использование функций
    ния вызова завершается ошибкой. Итак, каждая функция, перед тем как она будет вызвана, по крайней мере, должна быть объявлена. Это обязательное условие успешной трансляции. Объявление и определение функции — разные вещи. Объект может быть много раз объявлен, но только один раз определен. Итак, в файле программы созданная функция может располагаться в любом месте файла (до main()
    , после main()
    ). Однако с одним ограничением в момент ее вызова на выполнение сведения об этой функции программе должны быть известны. Как этого добиться Можно помещать определение функции перед main()
    . Но вдруг вам это покажется по каким-то причинам неудобными вы захотите расположить определение функции где-то после конца main()
    . Тогда для компилятора вы должны оставить информацию об этой функции. Это делается с помощью так называемого прототипа функции. Прототип функции — это заголовок функции сточкой запятой после него. Например. Информации прототипа достаточно для компилятора, чтобы он создал сведения об этой функции. Когда задан прототип, спокойно можно поместить определение функции вместо файла, где вам удобно с ней работать хоть перед, хоть после main()
    . На практике обычно при создании функции до начала функции main()
    пишут прототип функции, аза ним — самоопределение функции. Чтобы не запутаться. Особенно, когда программа большая и всяких функций тьма. Даже если в дальнейшем вы переместите определение функции вконец файла программы, прототип останется дои тем самым обеспечит успешную компиляцию программы. Вот пример задания функции void ZZ(int ppp);
    void ZZ(int ppp)
    {
    Тело функции
    }
    Прототип функции — информация для транслятора. В объявлении функции сосредоточена вся необходимая транслятору информация о функции — о списке ее параметров и типе возвращаемого значения. И это все, что в момент трансляции вызова необходимо транслятору для осуществления контроля над типами. Несоответствия типов параметров в прототипе и определении функции выявляются на стадии окончательной сборки программы. Несоответствие спецификации возвращаемого значения в объявлении прототипа и определении функции также является ошибкой. Значения параметров по умолчанию — прерогатива не С, а его дальнейшего развития С. Этот вопрос мы рассматриваем в данном контексте, потому что сегодня в чистом С вряд ли кто работает. В С+ при вызове функций можно вообще опускать параметры. В этом случае компилятор будет брать значения параметров по умолчанию. Значения по умолчанию для параметров указываются в заголовке функции при ее определении. Правила работы с функциями с указанием не всех параметров такие значения по умолчанию можно задавать не для всех параметров функции если вызов функции опускает значения одного или нескольких параметров, C++ будет использовать значения по умолчанию

    74 Часть
    I. Изучение языка С/С++

    если вызов функции опускает значение определенного параметра, то должны быть опущены и значения всех последующих параметров. Параметры по умолчанию (напоминаю в С) задаются в виде тип выхода имя функции>(<тип параметра = значение параметра по умолчанию, Например, void f(int a=25, float b=12) { Операторы функции}
    Возможные формы вызова функции f(); f(1002); Статическая переменная обычно инициализируется. Инициализация выполняется один раз при первом вызове функции. Например, double average (double x)
    {
    static double count = 0;
    static double sum = 0 ;
    ++count;
    sum += x;
    return( В дальнейшем при обращении к этой функции переменные count
    , sum будут текущими и инициализироваться не будут. Рассмотрим правила, определяющие область действия идентификатора переменной. Область действия идентификатора — это часть программы, в которой на идентификатор можно ссылаться. Существуют четыре области действия идентификатора функция файл программы блок прототип функции. Идентификатор, объявленный вне любой функции (на внешнем уровне, имеет область действия — файл программы. Такой идентификатор известен всем функциям от точки его объявления до конца файла программы. Глобальные переменные, описания функции, прототипы функции, находящиеся вне функции, — у всех область действия — файл программы. Ключевое слово inline в C++. Когда выполняется программа, в которой имеется много обращений к некоторой функции в разных местах программы, то во всех местах, где ваша программа вызывает функцию, компилятор C++ (на этапе компиляции, конечно) помещает в программу специальные инструкции, которые заносят параметры функции в стеки затем выполняют переход к командам этой функции. Когда операторы функции завершаются, выполнение программы продолжается с первого оператора, который следует за вызовом функции. Помещение аргументов
    Глава
    4. Создание и использование функций в стеки переход в функцию и выход из нее вносят издержки, из-за которых ваша программа выполняется немного медленнее, чем если бы она размещала те же операторы тела функции без ее вызова прямо внутри программы при каждой ссылке на функцию. Для этой цели существует специальное ключевое слово inline
    , которое пишется перед именем функции и которое обрабатывает компилятор, формируя внутри программы не вызовы функции, а проставляя на месте ее вызова операторы тела функции с учетом значений параметров, конечно. Для программиста остается видимым только вызов функции, а на самом деле в случаев телепрограммы компилятором проставлены операторы тела функции и при выполнении программы не тратится время на дополнительные операции по вызову функции, что ускоряет процесс расчета. Функции с ключевым словом inline называются встроенными. К встроенной функции относится и возможность включения блока на языке ассемблера в вашу программу. Существует специальная конструкция asm
    {
    MOV AH,2
    MOV DL,7
    INT 21H
    } которая позволяет это делать. Конструкция состоит из ключевого слова asm
    , после него идет тело конструкции, ограниченное фигурными скобками, внутри которых помещаются команды ассемблера. Перегрузка функций
    Это тоже прерогатива С. Изучение понятия "Перегрузка функции" нам будет полезна при изучении понятия "Классы. При определении функций в своих программах вы должны указать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. В прошлом (если вы программировали на языке С, когда у вас была некая функция которая работала с двумя целыми значениями, а вы хотели бы использовать подобную функцию для сложения трех целых значений, вам следовало создать функцию с другим именем. Аналогично, если бы вы хотели использовать подобную функцию для сложения значений типа float
    , то вам была бы необходима еще одна функция с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет вам определять несколько функций с одними тем же именем. В процессе компиляции компилятор C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций с одними тем же именем, нос разным количеством и типами параметров называется перегрузкой. Для перегрузки функций просто определите две функции с одними тем же именем и типом возвращаемого значения, которые отличаются количеством параметров или их типом.

    76 Часть
    I. Изучение языка С/С++
    Перегрузка функций является особенностью языка C++, которой нет в языке С. Перегруженные функции не обязаны возвращать значения одинакового типа по той причине, что компилятор однозначно идентифицирует функцию по ее имении набору ее аргументов. Для компилятора функции с одинаковыми именами, но различными типами аргументов — разные функции, поэтому тип возвращаемого значения прерогатива каждой функции. Использование шаблонов функций
    При создании функций иногда возникают ситуации, когда две функции выполняют одинаковую обработку, но работают с разными типами данных (например, одна использует параметры типа int
    , а другая типа float
    ). Мы уже знаем, что с помощью механизма перегрузки функций можно использовать одно и тоже имя для функций, выполняющих разные действия и имеющих разные типы параметров. Однако, если функции возвращают значения разных типов, приходится использовать для них уникальные имена. Предположим, например, что у вас есть функция с именем ах, которая возвращает максимальное из двух целых значений. Если позже нам потребуется подобная функция, которая возвращает максимальное из двух значений с плавающей точкой, придется определить другую функцию, например. В С+ существуют специальные средства — шаблоны, которые помогают решить затронутую выше проблему. Оказывается, что шаблон определяет набор операторов, с помощью которых ваши программы позже могут создать несколько функций программы часто используют шаблоны функций для быстрого определения нескольких функций, которые с помощью одинаковых операторов работают с параметрами разных типов или имеют разные типы возвращаемых значений шаблоны функций имеют специфичные имена, которые соответствуют имени функции, используемому вами в программе после того как ваша программа определила шаблон функции, она в дальнейшем может создать конкретную функцию, используя этот шаблон для задания прототипа, который включает имя данного шаблона, возвращаемое функцией значение и типы параметров в процессе компиляции компилятор C++ будет создавать в вашей программе функции с использованием типов, указанных в прототипах функций, которые ссылаются на имя шаблона. Создание простого шаблона функции
    Шаблон функции определяет типонезависимую функцию. С помощью такого шаблона ваши программы в дальнейшем могут определить конкретные функции с требуемыми типами. Например, ниже определен шаблон для функции с именем ах, которая возвращает большее из двух значений
    Глава
    4. Создание и использование функций
    template} Буква
    T
    в данном случае представляет собой общий тип шаблона. После определения шаблона внутри вашей программы вы объявляете прототипы (заголовки) функций для каждого требуемого вам типа. В случае шаблона ах следующие прототипы создают функции типа float и int float max(float, float); int max(int, int); Когда компилятор C++ встретит эти прототипы, то при построении функции он заменит тип шаблона
    T
    указанным вами типом. В случае с типом float функция ах после замены примет следующий вид template b) return(a) ; else return(b);
    } В процессе компиляции компилятор C++ автоматически создает операторы для построения одной функции, работающей с типом int
    , и второй функции, работающей с типом float
    . Поскольку компилятор C++ управляет операторами, соответствующими функциям, которые вы создаете с помощью шаблонов, он позволяет вам использовать одинаковые имена для функций, которые возвращают значения разных типов. Вы не смогли бы это сделать, используя только перегрузку функций. Шаблоны, которые используют несколько типов
    Предыдущее определение шаблона для функции max использовало единственный общий тип Т. Очень часто в шаблоне функции требуется указать несколько типов. Например, следующие операторы создают шаблон для функции show_array
    , которая выводит элементы массива. Шаблон использует тип Т для определения типа массива и тип Т для указания типа параметра count
    : template void show_array(T *array,T1 count)
    {
    T1 index;

    78 Часть
    I. Изучение языка С/С++
    for (index =0; index < count; index++)
    далее идет оператор вывода array[index], который мы еще не знаем, т. к. С+ станем изучать позже */
    } Как и ранее, программа должна указать прототипы функций для требуемых типов void show_array(int *, int); void show_array(float *, unsigned);
    ГЛАВА Функции для работы
    1   ...   4   5   6   7   8   9   10   11   ...   37


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