Константы 3 страница
Выход из оператора switch может быть организован с помощью операторов break или return.
Обозначение оператора в алгоритмах. Блок-схема алгоритма часть программы, соответствующей оператору switch, представлена на рис. 3.9.
Рисунок 3.9 — Блок-схемы алгоритма оператора switch
В качестве примера использования оператора switch можно привести программу реализации простейшего калькулятора:
#include <iostream.h>
void main ()
{
int a, b, result ;
char operation ;
bool f = true ;
cout<<” \n Введите 1-й операнд: “ ; cin>>a ;
cout<<” \n Введите знак операции: “ ; cin>>operation ;
cout<<” \n Введите 2-й операнд: “ ; cin>>b ;
switch (operation)
{
case ‘+’ : result=a + b; break;
case ‘–’ : result=a – b; break;
case ‘*’ : result=a * b; break;
case ‘/’ : result=a / b; break;
default : cout<<” Неизвестная операция “ ; f=false;
}
if (f) cout<<” \n Результат: “<<result;
return ;
}
3.7.2. Операторы цикла
Цикл — многократно повторяющиеся вычисления или действия.
Один проход цикла называют итерацией.
Различают циклы с предусловием и с пост условием.
Блок-схемы таких циклов изображены на рис. 3.10.
Рисунок 3.10 — Блок-схемы алгоритма операторов цикла: а) с предусловием; б) с постусловием
3.7.2.1. Цикл с предусловием (while)
Формат записи оператора:
while (выражение) оператор ;
Вначале вычисляется выражение, если выражение равно true, то выполняется оператор (тело цикла, в котором в большинстве случаев происходит модификация параметров цикла), после чего происходит возвращение к вычислению выражения.
Когда выражение равно false, то оператор не выполняется (пропускается) и происходит выход из цикла.
После завершения цикла программа переходит к операторам, следующим за циклом.
Выход из цикла часто организуется изменением параметров цикла, которым присваиваются начальные значения до цикла (блок начальных установок в алгоритме (рис. 3.10)). Параметры цикла изменяются в теле цикла и входят в выражение цикла.
В качестве примера использования оператора while можно привести программу, которая печатает таблицу значений функции y=x2+1 :
#include <stdio.h>
int main ()
{
float Xn, Xk, Dx ;
printf ( “Введите диапазон и шаг изменения аргумента: ” ) ;
scanf ( “%f%f%f”, &Xn, &Xk, &Dx ) ;
printf ( “| x | y | \n” ) ;
float X=Xn;
while ( X<=Xk ) {
printf ( “| %-5.2f | %-5.2f | \n“, X, X*X+1) ;
}
return 0 ;
}
Результат выполнения программы:
| x | y |
| 0.00 | 1.00 |
| 0.40 | 1.16 |
| 0.80 | 1.64 |
| 1.20 | 2.44 |
| 1.60 | 3.56 |
| 2.00 | 5.00 |
3.7.2.2. Цикл с постусловием (do while)
Формат записи:
do оператор while (выражение) ;
Вначале выполняется оператор (простой или составной), а затем вычисляется выражение. Если оно true, то тело цикла (оператор) выполняется еще раз, если false — цикл завершается. После завершения цикла программа переходит к операторам, следующим за циклом.
В качестве примера использования оператора do while можно привести программу проверки ввода символа в цикле:
#include <iostream.h>
void main ()
{
char answer ;
do { cout<<” \n Купи слоника! “ ; cin>>answer ;
} while (answer!=’Y’) ;
cout<<“\Спасибо за покупку!” ;
return ;
}
3.7.2.3. Цикл с параметрами ( for )
Формат записи:
for ( инициализация ; выражение ; модификации ) оператор ;
Инициализация — объявление и присвоение начальных значений величин, используемых в цикле.
В инициализаторе можно записать несколько операторов, разделенных запятой:
for (int i=0, j=1;…) … ;
или
int i, j ;
for (i=0, j=1;…) … ;
Выражение — условие выполнения цикла; если выражение равно true, то тело цикла выполняется, если false — цикл заканчивается.
Модификации — изменения переменных, которые входят в Выражение; модификации выполняются после каждой итерации цикла.
Оператор — может быть простым или составным (тело функции).
Через запятую можно записать несколько операторов, тогда оператор является составным.
В записи цикла может быть опущена любая часть операторов for.
Пример:
for (int i=1, S=0; i<=50; i++) s+=i ; //сумма чисел от 1 до 50
Рекомендации по избеганию ошибок использования цикла:
1. проверить, все ли начальные значения инициализированы;
2. изменяется ли хоть одна переменная в цикле, входящая в условие выхода из цикла;
3. предусмотреть «аварийный» выход из цикла (например, при слишком большом количестве итераций);
4. не забывать использовать фигурные скобки { } для составных операторов в теле цикла.
В качестве примера можно привести программу, которая печатает таблицу значений функции y=x2+1:
#include <stdio.h>
int main ()
{
float Xn, Xk, Dx ;
printf ( “Введите диапазон и шаг изменения аргумента: ” ) ;
scanf ( “%f%f%f”, &Xn, &Xk, &Dx ) ;
printf ( “| x | y | \n” ) ;
for ( float X=Xn; X<=Xk ; X+=Dx )
printf ( “| %-5.2f | %-5.2f | \n”, X, X*X+1) ;
return 0 ;
}
3.7.3. Операторы передачи управления
Операторы передачи управления — это операторы изменяющие естественный порядок выполнения программы:
— goto — безусловный переход;
— break — прерывание;
— continue — пропуск;
— return — возврат из функции.
3.7.3.1. Оператор безусловного перехода goto
Формат записи:
goto метка ;
Кроме данной записи в теле той же функции должна присутствовать конструкция вида:
метка : оператор;
Метка — идентификатор, видимый внутри функции, представляющий из себя адрес программного кода, т.е. оператора, на который осуществляется переход.
Примечания.
Рекомендуется избегать применения оператора goto.
Функция goto используется:
— при принудительном выходе в нижнюю часть программы, которая состоит из нескольких вложенных циклов или переключателей;
— переход из нескольких мест функции в одно.
3.7.3.2. Оператор прерывания break
Оператор break используется внутри циклов, операторов if и switch для прерывания оператора и выхода в точку программы, находящуюся непосредственно за оператором, см. пример в п. 3.5.1.2., где описывался оператор switch.
Пример (фрагмент из примера (см. пункт 3.7.1.2.)):
switch (operation)
{
case ‘+’ : result=a + b; break;
case ‘–’ : result=a – b; break;
case ‘*’ : result=a * b; break;
case ‘/’ : result=a / b; break;
default : cout<<” Неизвестная операция “ ; f=false;
}
if (f) cout<<… ; //<--- точка выхода по оператору break
3.7.3.3. Оператор пропуска continue
Оператор continue — оператор пропуска переводит к следующей итерации цикла, пропуская оставшиеся в тебе цикла операции.
В качестве примера можно привести программу вычисления суммы чисел, больших нуля:
#include <stdio.h>
void main ()
{
float i, b[100], c=0 ;
for ( i=0; i<100; i++)
{ printf (“Введите b:”) ; scanf (“%f”, b[i]) ;
if (b[i]<=0) continue;
c+=b[i]; //сложение чисел, больших 0
printf (“%-5.2f \n”, c) ;
}
return ;
}
3.7.3.4. Оператор возврата из функции return
Оператор return — оператор возврата из функции завершает выполнение функции и передает управление в точку вызова функции.
Формат записи:
return [выражение] ;
Примечания:
— выражение должно иметь скалярный тип.
— если тип функции void, то выражение должно отсутствовать, как показано в следующем отрезке программы:
Примеры:
//– – – – – – – – – – – – – – – – – – – – – – – –
int sum (int a, int b) {
return (a+b) ; }
//– – – – – – – – – – – – – – – – – – – – – – – –
int summa (int a, int b)
{
int c ;
c=a+b ;
return c ;
}
//– – – – – – – – – – – – – – – – – – – – – – – –
void verifying (int a, int b)
{
int c ;
if (a<0) return ;
else if (b>10) return ;
else { c=a+b ;
if ((c/2-2*b)==7) return ;
}
…
}
//– – – – – – – – – – – – – – – – – – – – – – – –
4. Модульное программирование
4.1. Лекция 11. Функции
С увеличением объёма и усложнением программ можно бороться только разбиением её на части.
В различных языках программирования существуют подпрограммы, процедуры и функции.
В С/С++ применяются только функции.
Функция — последовательность описаний и операторов, выполняющая какое-либо законченное действие, вызываемая по имени и возвращающая в точку вызова предписанное значение.
Операции, которые производят над функциями: объявление, определение и вызов.
4.1.1. Объявление функций
Объявление функции — это создание прототипа функции.
При объявлении выполняются следующие действия:
— задаётся имя функции;
— задаётся тип возвращаемого значения (тип функции);
— указывается список передаваемых параметров.
Формат:
[класс] тип имя_функции ( [список_параметров] ) ;
Класс и список параметров, заключенные в квадратные скобки могут быть опущены.
Здесь:
— класс — задаёт область видимости функции:
extern — глобальная видимость во всех модулях программы (принимается по умолчанию);
static — видимость только в пределах модуля, в котором объявлена функция;
— тип — определяет тип значения, возвращаемого функцией: может быть любым, кроме массива и функции, но может быть указателем на массив или функцию; если функция не возвращает значения, то указывается тип void;
— имя_функции — имя функции, идентификатор;
— список_параметров— определяет величины, которые требуется передать в функцию при вызове: элементы списка разделяются запятыми, указываются типы и имена, в объявлении имена можно опускать.
4.1.2. Определение функций
Определение функции содержит, кроме объявления, тело функции, представляющее собой последовательность операторов.
Формат:
[класс] тип имя ( [список_параметров] ) [throw(исключения)]
{тело функции}
Здесь:
— исключения — список исключений, которые может порождать функция (т.е. ошибки).
4.1.3. Вызов функций
Формат:
имя_функции ( [список_параметров] ) ;
Примечание:
1. В определении, в объявлении и при вызове одной и той же функции типы и порядок следования параметров должны совпадать.
2. На имена параметров ограничений по соответствию не накладывается, так как функцию можно вызывать с различными аргументами.
3. В прототипах имена списка параметров игнорируются и служат исключительно для улучшения читаемости программы.
Пример:
#include <iostream.h>
//объявление функции:
int sum (int x, int y) ; // <– – Вариант 1
//int sum (int, int) ; <– – Вариант 2
main() {
int a=2 , b=3 , c , d ;
c=sum(a , b) ; // вызов функции
cin>> d ;
cout<< sum(c , d) ; // вызов функции
cout<< sum(a*10+5 , b+3-a/2) ;// вызов функции
return 0 ;
}
// определение функции
int sum (int x , int y)
{
return (x+y) ; // <– – Здесь x и y – локальные переменные
}
В следующем примере все величины, описанные внутри функций являются локальными.
4.1.4. Передача параметров в функцию
Параметры, перечисленные в заголовке описания функции, называют формальными, а записанные в операторе вызова функции — фактическими параметрами, или аргументами.
Кроме этого в функцию можно передавать параметры специальным образом, например:
sum ( a*10+5 , b+3–a/2 ) ;
Параметры, перечисленные в заголовке описания функции, называют формальными, а записанные в операторе вызова функции — фактическими параметрами, или аргументами.
Существует два основных способа передачи параметров в функцию (см. рис. 4.1.): по значению и по адресу.
Рисунок 4.1 — Способы передачи параметров в функцию
Пример:
#include <iostream.h>
void increm (int a, int *b, int &c) ; // объявление функции
main() {
int a=1 , b=2 , c=3 ;
cout<<''a b c \n'' ;
cout<<a<<' '<<b<<' '<<c<<'\n' ;
increm(a , &b, c) ; //вызов функции
cout<< a<<' '<<b<<' '<<c<<'\n' ;
return 0 ;
}
void increm(int a , int *b , int &c)
{ // определение функции
a++, (*b)++, c++
}
Результат выполнения программы:
a b c
1 2 3
1 3 4
В примере:
— первый параметр (а) передаётся в функцию по значению, его изменение в функции не влияет на исходное значение переменной;
— второй параметр (b) передаётся по адресу с помощью указателя; передача фактического параметра (аргумента) осуществляется с помощью операции взятия адреса (&); в самой функции используется операция реадресации (разыменования, *);
— третий параметр (с) передаётся в функцию с помощью ссылки; при этом происходит неявное разыменование; такой способ передачи параметров в функцию улучшает читаемость и понятность кода программы.
4.1.5. Передача массивов в функцию
При использовании массива в качестве параметра функции, в функцию передаётся указатель на тип элемента массива, а точнее — на первый элемент массива.
Рассмотрим пример передачи одномерного массива:
/* Передача одномерного массива в функцию */
#include <iostream.h>
int sum (const int *mas, const int n) ; //объявление функции
const int n=10; //объявление константы – кол-ва элементов массива
main()
{
int marks[n] = {1, 2, 3, 4, 5, 6} ; //объявление и иниц-ция массива
cout<< “Сумма эл-тов массива: ”<<sum(marks, n) ; //вызов ф-ции
return 0;
}
int sum(const int *mas, const int n) //определение функции
{ // или int sum(int mas[], int n)
// или int sum(int mas[n], int n)
int s=0;
for (int i=0; i<n; i++) s += mas[i] ; //суммирование элементов м.
return s ;
}
Результат выполнения программы:
Сумма эл-тов массива: 21
Рассмотрим пример передачи двумерного массива в функцию:
/* Передача двумерного массива в функцию */
#include <iostream.h>
int sum (int **a, const int n_str, const int n_slb) ; //1
main()
{ int n_str, n_slb;
int **a, i, j ;
cout<< “Кол-во строк: ” ; cin>>n_str; //2
cout<< “Кол-во столбцов: ” ; cin>>n_slb; //2
a = new int *[n_str] ; //3
for (i=0; i<n_str; i++) a[i] = new int [n_slb] ; //3
for (i=0; i<n_str; i++) //4
for (j=0; j<n_slb;j++) //4
{ cout<<“a[”<<i<<“,”<<j<<“]=”; //4
cin>>a[i][j] ; //4
}
cout<<“Сумма эл-тов массива: ”<<sum(a, n_str, n_slb) ; //5
...
return 0 ;
}
int sum(int **a, const int n_str, const int n_slb) //6
{ for(int s=0, i=0; i<n_str; i++) //7
for(int j=0; j<n_slb; j++) s+=a[i][j] ; //7
return s ; //8
}
Результат выполнения программы при вводе кол-ва строк — 2 и количества столбцов —2, и значений элементов массива a[0,0]=1, a[0,1]=2, a[1,0]=3, a[1,1]=4:
Сумма эл-тов массива: 10
В примере:
//1 — объявление функции; модификатор const указывает на то, что параметры в функции не изменяются;
//2 — ввод размерности массива;
//3 — выделение памяти под динамический массив;
//4 — ввод значений элементов массива;
//5 — вызов функций и вывод результата выполнения функции на экран;
//6 — определение функции;
//7 — суммирование элементов массива;
//8 — передача результата суммирования в точку вызова функции.
4.1.6. Функции с переменным числом параметров
В функциях с переменным числом параметров список формальных параметров заканчивается троеточием.
Для доступа к необязательным параметрам внутри функции используются следующие макросы библиотеки <stdarg.h>:
va_start,
va_arg,
va_end и va_list .
va_start — инициализирует указатель;
va_list — хранит указатель на очередной аргумент;
va_arg — возвращает значение очередного аргумента;
va_end — после перебора всех элементов необходимо обратиться к этому макросу до выхода из функции.
Пример:
/* функция с переменным числом параметров */
#include <stdio.h>
#include <stdarg.h>
main () {
int n;
int sred_znach(int , …) ; // объявление функции
n=sred_znach(2,3,4,-1) ; // вызов с 4-мя параметрами
print(''n=%d'' , n) ;
n=sred_znach(5,6,7,8,9,-1) ; // вызов с 6-ю параметрами
printf(''\n n=%d'' , n) ;
return 0 ;
}
int sred_znach(int x , …) // определение функции
{ int i=0 ; j=0 ; sum=0 ;
va_list uk_arg ; // объявл-е указателя uk_arg на эл-ты
// списка необязательных параметров
va_start (uk_arg , x) ; // установка указателя uk_arg на
// первый необязательный параметр х
/* выборка очередного параметра и проверка на конец списка: */
while ((i=va_arg(uk_arg , int))!=–1)
{
sum+=i ; // сумма элементов
j++ ; // кол-во элементов
}
va_end(uk_arg) ; // закрытие списка параметров
return (sum/j) ; // передача сред. знач. в точку вызова
}
Результат выполнения программы:
n=3
n=7
Примечание:
Функция в данном примере имеет один обязательный параметр, который обязательно должен быть равен ‘–1' и является признаком (флагом) окончания списка передаваемых в функцию параметров.
4.1.7. Рекурсивные функции
Рекурсивная функция — функция, вызывающая сама себя (прямая рекурсия). Если несколько функций вызывают друг друга — это косвенная рекурсия.
Рекурсивная функция должна иметь хоть одну нерекурсивную ветвь, заканчивающуюся оператором возврата.
Пример:
/* вычисление факториала (n!): */
long factorial (long n) {
if (n==0 || n==1) return 1;
return (n*factorial(n–1));
}
Положительные стороны использования рекурсивной функции — компактность программного кода, отрицательные — расход памяти и времени, опасность переполнения стека.
4.1.8. Функция main()
Функция main()— функция, которой передаётся управление после запуска программы и которая может передавать значение в вызывавшую систему и принимать параметры из внешнего окружения.
Существует два формата записи функции:
— без параметров
тип main() { … }
— с двумя параметрами
тип main( int argc , char* argv[ ] ) { … }
Имена параметров могут быть любые, но общепринятыми являются указанные ниже.
Здесь:
— argc — определяет количество параметров, передаваемых функции;
— argv — является указателем на массив указателей типа char; каждый элемент массива содержит указатель на отдельный параметр командной строки, хранящейся в виде строки.
Указатели массива argv[ ] выполняют следующие функции:
argv[0] — ссылается на полное имя запускаемого на исполнение файла;
argv[1] — ссылается на первый параметр командной строки;
…
argv[argc] — указывает на «нуль символ» (окончание строки) и равен '\n';
Пример:
#include <iostream.h>
void main ( int argc , char *argv[ ] )
{
for( int i=0 ; i<argc ; i++ )
cout<< argv[i]<< “\n” ;
}
Результат выполнения программы:
Если данный файл имеет название main.exe и запускается из строки:
d:\BC\main.exe one two three
то на экране будет выведено:
D:\BC\MAIN.EXE
one
two
three
4.1.9. Перегрузка функций
Использование нескольких функций с одним и тем же именем, но с различными типами параметров называется перегрузкой функции.
Компилятор по типу фактических параметров (аргументов) определяет, какую именно функцию следует вызвать. Если точного соответствия не найдено, то выполняется продвижение типов в соответствии с правилами (см. пункт 3.6.12, тема 3). После этого выполняются преобразования типов и т.д.
Если соответствие на одном этапе получается более чем одним способом, то возникает неоднозначность вызова.
Избежать неоднозначности можно используя явное преобразование типов аргументов.
Примеры:
/* 1) Перегрузка функций */
…
// Имеется 4-е варианта функции, определяющей наибольшее знач-е
int max(int , int) ; // (1) наибольшее из двух целых
char* max(char* , char*) ; // (2) наиболее длинная из двух строк
int max(int , char*) ; // (3) наибольшее из 1-го и длины 2-го
int max(char* , int) ; // (4) наибольшее из длины 1-го и 2-го
…
void func(int a , int b , char* c , char* d)
{ cout<<max(a , b)<<max(с , d)<<max(a , c)<< max(d , b);
} // (1) (2) (3) (4)
…
В примере:
— выбор варианта функции max осуществляется на этапе компиляции программы в соответствии с типом фактических параметров (аргументов);
— в данном примере все четыре варианта функции вызываются последовательно.
/* 2) Перегрузка функций (неоднозначность) */
#include <iostream.h>
float f(float i)
{
cout<< ''Function float f(float i)'' << endl ;
return i ;
}
double f(double i)
{
cout<< ''Function double f(float i)'' << endl ;
return i+2 ;
}
int main()
{
float x=10.09 ;
double y=10.09 ;
cout<< f(x) << endl ; // вызывается f (float)
cout<< f(y) << endl ; // вызывается f (double)
cout<< f(10) << endl ; // возникает неоднозначность
// преобразования 10 во float
// или double
return 0 ;
}
В примере:
При вызове функции f(10) возникает неоднозначность и компилятор выдает ошибку. Для устранения неоднозначности в примере требуется '10’ преобразовать явным образом к требуемому типу f(float(10)) или f(double(10)).
Правила описания перегруженных функций:
1. Перегруженные функции должны находиться в одной области видимости, иначе произойдет сокрытие функций аналогично одинаковым именам переменных во вложенных блоках (в операторах, функциях и т.п.).
2. Перегруженные функции могут иметь параметры по умолчанию, при этом значения одного и того же параметра в разных функциях должны совпадать. В различных вариантах перегруженных функций может быть различное количество параметров по умолчанию.
3. Функции не могут быть перегружены, если описание их параметров отличается только модификаторами const или использованием ссылки:
int и const int
или
int и int & .
4.1.10. Шаблоны функций
Шаблоны функций предназначены:
— для повышения эффективности программы, содержащей однотипную обработку данных различных типов, например, сортировка чисел типа int и double;
— чтобы не возникало необходимости создавать несколько перегружаемых функций с полностью одинаковыми алгоритмами обработки данных.
Шаблон функций — это программно расписанный алгоритм, применяемый к данным различных типов.
Вызов функции, который использует конкретный тип данных, приводит к созданию компилятором кода для соответствующей версии функции. Этот процесс называется созданием экземпляра (instantiation) шаблона.
Формат определения шаблона функции:
template <class список_типов> имя_функции (список параметров)
{
/* тело функции */
}
Здесь:
список_типов — список типов, которые определяются при вызове функции;
список параметров — список параметров, передаваемых в функцию.
Примечания:
1. Конкретный тип данных передаётся функции в виде параметров на этапе компиляции. Компилятор автоматически генерирует правильный код, соответствующий переданному в функцию типу, т.е. создается функция, которая автоматически перегружает сама себя.
2. В общем случае шаблон функции может содержать несколько параметров, каждый из которых может быть не только типом, но и просто переменной:
template <class TypeA , class TypeB>
void f(TypeA x, TypeB y, int i) { … }
3. При вызове шаблона функции на месте параметра шаблона, являющегося не типом, а переменной, должно указываться константное выражение.
4. Типы параметров, передаваемых в функцию при вызове шаблона функции, могут определяться автоматически. А могут быть установлены принудительно («вручную»).
Пример:
/* Автоматическое определение типа параметров функции */
#include <iostream.h>
template <class Type> void SortVybor(Type *b, int n);
main() { const int n=20;
int i, b[n] ;
double a[n] ;
for (i=1; i<n; i++) cin>>b[i] ;
SortVybor(b, n) ; //сорт-ка целочисленного массива
for (i=1; i<n; i++) cin>>a[i] ;
SortVybor(a, n) ; // сорт-ка вещественного массива
return 0;
}
/* Шаблон функции сортировки методом прямого выбора: */
template <class Type> void SortVybor (Type *b, int n)
{
Type a; // буфер для обмена элементов
int imin ; // номер наименьш. эл-та в исходн. массиве
for (int i=0 ; i<n–1 ; i++)
{ imin=i ;
for (int j=i+1 ; j<n ; j++) // выбор
if (b[j]<b[imin]) imin=j ; // наименьшего
a=b[i] ; b[i]=b[imin] ; b[imin]=a ; // и обмен
}
}
/* Принудительное определение типа параметров функции */
// Шаблон функции определен как
template <class X, class Y, class Z> void Func(Y, Z) ;
// Вызовы функции:
Func <int, char*, float> (“University”, 3.3); //1
Func <int, char*> (“University”, 3.3); //2
Func <int> (“University”, 3.3); //3
Func (“University”, 3.3); //4
Здесь:
//1 — все определено программистом;
//2 — принудительно определены X и Y, параметр Z определен автоматически как double;
//3 — принудительно определен X, автоматически определены Y — char*, Z — double;
//4 — ОШИБКА: невозможно определить тип X
4.2. Лекция 12. Директивы препроцессора
Препроцессором называется первая фаза компилятора.
Инструкции препроцессора называются директивами.
Директивы препроцессора — это команды компилятору, с помощью которых можно управлять компиляцией программы и улучшить читаемость текста программы. Все директивы начинаются с символа #. Как видно из названия директивы препроцессора обрабатываются препроцессором — специальной частью компилятора, которая обрабатывает директивы перед началом процесса компиляции кода программы.
Дата добавления: 2015-10-21; просмотров: 683;