Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по С++.doc
Скачиваний:
27
Добавлен:
20.08.2019
Размер:
2.26 Mб
Скачать

Функции, имеющие возвращаемый параметр с помощью оператора return.

Тип возвращаемого параметра функции может быть:

  • простой тип: - арифметический, символьный, логический, перечисляемый;

  • указатель базовый тип любой: - скаляр, массив, строка, структура, файл, функция.

Если в заголовке функции не указан тип возвращаемого параметра, то по умолчанию возвращается с помощью оператора return переменная типа int.

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

int big (int a, int b)

{

if (a>b) return a; // если a>b, то программа завершится здесь

else return b; // иначе здесь

}

int bigint (int a, int b, int c)

{

if ( (a>b) && (a>c) ) return = a;

else if (b>c) return = b;

else return = c;

}

Если функция не возвращает результат в точку вызова, то return может отсутствовать и выполнение функции завершается по концу тела функции.

Если функция возвращает результат в точку вызова, то вызов такой функции представляет собой:

  1. операнд в операторе:

    • в правой части оператора присвоения;

    • в списке фактических параметров;

    • в операторе условия выражением условия;

    • в выборе варианта селектором;

    • в цикле с предусловием и постусловием;

    • в методе в/в cout.

  2. самостоятельный оператор.

Формат вызова функции:

имя_функции ([список фактических параметров]);

  • имя_функции совпадает с именем вызываемой функции;

  • список фактических параметров должен совпадать со списком формальных параметров по количеству, по месту положения, по типу.

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

Пример функции, возвращающей указатель

#include <iostream>

using namespace std ;

char* fSubStr ( char*, char* ) ;

int main ( )

{

char* s = "A Toyota! Race fast... safe car: a Toyota" ;

char* ss = "Toyota" ;

cout << "String:\t\t" << s << endl ;

cout << "Substring:\t" << ss << endl ;

char* p ( s );

while ( p = fSubStr ( p, ss ) )

{

cout << "Substring entry:\t" << p << endl ;

p += strlen ( ss ) ;

}

return 0 ;

}

// определение функции поиска подстроки в строке

char* fSubStr ( char* ps, char* pss )

{

if ( ! *pss || strlen ( pss ) > strlen ( ps ) ) return 0 ;

char *p, *r ;

while ( *ps )

{

for ( p = ps, r = pss; *r && *p == *r; p++, r++ ) ;

if ( ! *r ) return ps ;

ps++ ;

}

return 0 ;

}

Объявление формальных параметров

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

cout<<”Куб числа ”<< 1.2<<” = ”<<cube(1.2);//фактический параметр 1.2 // соответствует описанию формального //параметра

double cube(double x) // double x – формальный параметр

Список формальных параметров является элементом заголовка функции. Область действия формальных параметров – тело функции, в которой они объявлены, и являются локальными. Фактические параметры передаются в вызываемую функцию при её вызове, заменяя формальные параметры на фактические (конкретные) для обработки данных. Между формальными и фактическими параметрами должно быть взаимно - однозначное соответствие:

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

  2. последовательность формальных и соответствующих им фактических параметров должна быть соответственна: первому формальному параметру соответствует первый фактический, второму – второй и т.д.;

  3. типы формальных и соответствующих им фактических параметров должны быть одинаковыми или совместимыми.

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

тип идентификатор

тип – идентификатор стандартного типа или сформированного пользователем;

формальные параметры отделяются друг от друга запятыми.

Если список формальных параметров содержит слово void, то это означает, что список формальных параметров пуст, то есть отсутствуют формальные и фактические параметры. В этом случае можно оставить только круглые скобки. В зависимости от способа приема исходных данных и возврата результата, формальные параметры делятся по категориям:

- по значению;

- по адресу;

- по ссылке.

int fun ( int a, int * h) // int a – по значению, int * h – по адресу

int l = 8;

int b = fun(5, &l);

int *p = new int;

int d = fun(b, p);

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

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

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

Пример функции, возвращающей значение

#include <iostream>

using namespace std ;

// прототипы функций

long double fact ( const int& ) ;//ссылка на константу целого типа

// главная функция

void main ( )

{

int x = 8 ;

cout << fact ( x ) << endl ;

x = fact ( x ) ; cout << x << endl ;

}

// определение вычисления без рекурсии

long double fact ( const int& n )

{

int i ( n ) ;// I инициализируется значением параметра n

long double res = 1 ;

while ( i > 1 ) res *= i-- ;

return res ; //возврат результата

}

Пример передачи параметров по значению и по ссылке

#include <iostream>

using namespace std ;

// прототипы функций

void swapV ( int, int ) ;

void swapP ( int*, int* ) ;

void swapR ( int&, int& ) ;

void reverseArray ( int*, int = 10 ) ;

void view ( const int*, int = 5 ) ;

// главная функция

int main ( )

{

int aIn, bIn ;

cout << "Enter a -> " ; cin >> aIn ;

cout << "Enter b -> " ; cin >> bIn ;

// перестановка по значению

int a = aIn, b = bIn ; swapV ( a, b ) ;

cout << "Result swapV ( ): " ; cout << a << ' ' << b << endl ;

// перестановка посредством указателей

a = aIn, b = bIn ; swapP ( &a, &b ) ;

cout << "Result swapP ( ): " ; cout << a << ' ' << b << endl ;

// перестановка посредством ссылок

int &ra = aIn, &rb = bIn ;

swapR ( ra, rb ) ; // swapR ( aIn, bIn ) ;

cout << "Result swapR ( ): " ; cout << aIn << ' ' << bIn << endl ;

// реверс массива

int ar [ 20 ] = { 1, 2, 3, 4, 5, 6 } ;

cout << "\nInitial array\n" ; view ( ar, 5 ) ;

cout << "\nReverse array\n" ;

reverseArray ( ar, 5 ) ; view ( ar, 5 ) ;

cout << "\nOne more reverse\n" ;

reverseArray ( ar, 12 ) ; view ( ar, 12 ) ;

return 0 ;

}

// перестановка по значению (только внутри функции)

void swapV ( int x, int y )

{ int t = x ; x = y ; y = t ; }

// перестановка при помощи указателей

void swapP ( int* px, int* py )

{ int t = * px ; * px = * py ; * py = t ; }

// перестановка при помощи ссылок

void swapR ( int& rx, int& ry )

{ int t = rx ; rx = ry ; ry = t ; }

// реверс массива

void reverseArray ( int* p, int size )

{

for ( int i = 0, j = size - 1; i < j; i++, j-- )

swapP ( p + i, p + j ) ; // swapR ( p [ i ], p [ j ] ) ;

}

// вывод состояния массива

void view ( const int* p, int size )

{

int n = 5 ;

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

{

for ( int j = 0; j < n && ( i + j ) < size; j++ )

cout << p [ i + j ] << '\t' ;

i += n - 1 ;

cout << endl ;

}

}

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

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

Функция swapP в качестве исходных данных принимает адреса фактических параметров, которые передаются при вызове ( swapP ( &a, &b ) ; ). При таком способе функция swapP имеет непосредственный доступ к значениям глобальных переменных. При перестановке используется разыменование указателей. После выполнения swapP значения a и b поменяются местами.

Функция swapR использует в качестве формальных параметров ссылки. В примере перед вызовом функции объявляются независимые ссылки ra и rb, после чего происходит вызов функции swapR ( ra, rb ). Объявление ссылок было необязательным. Можно было обратиться к функции swapR ( aIn, bIn ). Результат один и тот же при обоих вызовах, так как меняются местами переменные aIn и bIn. При использовании ссылок копии не создаются, и разыменование не выполняется.

Файловый ввод вывод

Функции в/в в С++ позволяют читать (копировать) данные из файлов и устройств в/в в ОП, и записывать из ОП в файл и устройства в/в. Функции в/в можно разделить на 2 группы:

  1. в/в верхнего уровня: потоковый и записями;

  2. в/в нижнего уровня: системный в/в.

Под файлом понимается либо именованная область внешней памяти ПЭВМ (ж.д. или г.д.), либо логическое устройство – потенциальный источник или приемник информации, доступ к которым обеспечивает операционная система. Поддержка ОС состоит в том, что в ней имеются свойства:

  • создание файлов;

  • удаление файлов;

  • поиск файлов на внешнем носителе;

  • открытие файлов;

  • чтение данных из файла;

  • запись данных в файл;

  • закрытие файлов;

  • перемещение указателя в нужное место файла.

Функции ввода вывода нижнего уровня не выполняют буферизации и форматирование данных. Это функции системного ввода вывода:

  • они позволяют пользоваться непосредственно средствами ввода вывода ОС;

  • их целесообразно использовать при разработке собственных подсистем ввода вывода данных.

Функции ввода вывода верхнего уровня позволяют буферизованный форматный и бесформатный ввод вывод данных. Это значит, что при чтении данных из файла и записи данных в файл, обмен данными осуществляется между программой и буфером, расположенным в ОП. Данные из буфера в файл и обратно пересылаются по заполнению или по исчерпанию данных буфера. Буферизация ускоряет выполнение программы за счет уменьшения количества пересылок данных из буфера на МД и обратно. Функции ввода вывода верхнего уровня реализуют ввод вывод потоком и записями находятся в файле stdio.h.

Поток (stream) можно определить как абстрактный канал связи, который связывается в программе для обмена данными с файлами. Его можно связать с файлом на МД или внешнем устройстве. Поток может быть текстовым или бинарным.

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

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

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

  1. создание файла;

  2. создание потока;

  3. открытие файла;

  4. «присоединение» файла с потоку;

  5. обмены с файлом с помощью потока;

  6. «отсоединение» потока от файла;

  7. закрытие файла;

  8. уничтожение файла, если это необходимо.

Высоко уровневые средства ввода вывода С++ позволяют работать с текстовыми и бинарными файлами после того как в программе установлена их взаимосвязь с текстовыми и бинарными потоками соответственно.

Файл, рассматриваемый как последовательность строк символов, разделенных непробельными символами, называется текстовым. Кроме символа «пробел » пробельными символами являются специальные символы:

  • ‘/t’ – горизонтальная табуляция;

  • ‘/v’ – вертикальная табуляция;

  • ‘/n’ – новая строка;

  • ‘/r’ – возврат каретки;

  • ‘/f’ – прогон бумаги до конца страницы;

  • ‘/b’ – возврат на один символ;

  • ‘/a’ – звуковой сигнал;

  • ‘/07’ – звуковой сигнал.

Имя текстового файл состоит из: - пути следования;

- имени;

- расширения txt.

Расширение txt позволяет просматривать содержимое файла при его открытии. При записи в файл в текстовом режиме обмена, каждый символ новой строки ‘/n’ преобразуется в пару символов – в конец строки и начало строки. В процессе чтения из файла каждая пара символов – конец строки и начало строки, преобразуются в ‘/n’. Чтение из файла продолжается до тех пор пока не обнаружен символ конец файла. В этом случае считается, что достигнут конец файла и выполнено условие EOF (End Of File)

Файл называется бинарным, если его рассматривают как последовательность байтов без учета разделения на строки, он сохраняется в машинном коде и не открывается в редакторе, имеет расширение отличное от .txt (.dat). В качестве файлов рассматривают не только именованные участки на дисках, но и любые файловые устройства, с которыми можно осуществлять операции в/в. Не все файлы имеют одинаковые поведенческие свойства. Это связано и с физической реализацией файла. Поэтому при связывании потока с определенным файлом поток приобретает свойства этого файла. Такое поведение позволяет говорить о «записи в поток» и «чтении из потока», что эквивалентно «запись в поток» и «чтение из файла». Если свойства файла конфликтуют со свойствами связываемого потока, то возникает исключительная ситуация – ошибка открытия файла.

Файл состоит из набора однотипных данных называемых элементами. Файл имеет 3 характерные особенности:

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

  2. все элементы файла одного типа кроме файлового типа;

  3. длина файла ограничивается ёмкостью диска.

Имя файла –переменная строкового типа, длина имени файла зависит от операционной системы и состоит из:

[имя диска:] [{\директория}] имя файла . расширение

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

Открытие, закрытие и удаление файла.

Для работы с файлом его надо «открыть», обработать, «закрыть». При открытии файла программы с ним связывается отдельный физический поток. Файл должен иметь указатель на структуру типа FILE (FILE *fp;). Это тип, определенный в файле stdio.h. При подключении stdio.h автоматически создаются следующие пять потоков:

  1. stdin – для стандартного ввода (клавиатура);

  2. stdout - для стандартного вывода (монитор);

  3. stderr - для стандартного вывода об ошибок (монитор);

  4. stdaux- для дополнительного стандартного вывода (монитор);

  5. stdprn - для стандартного вывода (принтер).

Когда файл открывается с помощью функции fopen, структуре типа FILE выделяется ОП, адрес которой получает указатель на структуру, и поток связывается со структурой типа FILE. Структура содержит информацию о файле или устройстве, с которым связан файл программы. Указатель типа FILE используется для операций с файлами с помощью библиотечных функций в/в. Открытие потока производится функцией . Прототип функции:

FILE *fopen (const char *filename, const char *mode);

Filename – определяет полное имя физического файла на МД, это может быть указатель-константа, указатель-переменная строки или строковая константа, которая содержит имя физического файла;

mode – определяет режим доступа к файлу, в сочетании с t - для текстовых файлов, в сочетании с b - для бинарных файлов.

Таблица режима доступа

Значение

режима

Описание режима

Позиция

в/в

r

Открытие существующего файла для чтения.

Начало ф

w

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

Начало ф

a

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

Конец ф

+

Открытие файла для обновления, допускается чтение из файла и запись в файл

Конец ф

Если открытие файла успешно, функция fopen возвращает значение – указатель на переменную типа FILE и связывает файл программы с физическим файлом, а если открытие не состоялось (файл с таким именем не найден) функция fopen возвращает значение NULL. При связывании файла с потоком соответствующим образом инициализируется и внутренняя переменная потока «позиция ввода-вывода». Значение этой переменной соответствует номеру позиции в потоке, с которой начинают записываться или считываться данные. Успешность открытия файла можно проверить с помощью оператора if. Пример:

FILE * fp;

char fn[20] = "ffff.dat";

if (fp = fopen (fn, "rb")) {выполнить блок если файл благополучно открылся}

if ((fp = fopen (fn, "rb")) == NULL) {выполнить блок если файл не открылся}

Функция fclose закрывает один заданный поток (файл). Функция fcloseall закрывает все потоки, открытые с помощью функций fopen, кроме потоков stdin, stdout, stderr. Прототипы этих функций:

int fclose (FILE *stream);

int fcloseall ();

При выполнении закрытия файла буфер потока освобождается, производится запись буферированных данных из буфера в файл. Все буферы, связанные с потоками, сбрасываются и их ОП освобождается. Функция fclose возвращает значение 0, если поток успешно закрыт. Функция fcloseall возвращает количество закрытых потоков. Если при закрытии произошла ошибка, обе функции возвращают значение EOF(-1) (признак конца файла).

С помощью функции remove можно удалить файл. Прототип функции: int remove (const char *filename);

где filename – указатель на строку с именем файла.

Функция remove возвращает 0 при успешном удалении файла и не нуль при неуспешном удалении. Для удаления файл надо предварительно закрыть.