Лекции по программированию

Автор: Пользователь скрыл имя, 17 Января 2011 в 18:57, курс лекций

Описание работы

6 тем.

Работа содержит 1 файл

Лекции.doc

— 615.00 Кб (Скачать)

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

Текст программы

    #include <iostream>

    using namespace std;

    int main()

      int a=1500000000;

      a=(a*10)/10; //1 – неверный результат

      cout << "a=" <<a<<endl;

      int b=1500000000;

      b=(static_cast<double>(b)*10)/10; //2 – верный результат

      cout << "b=" <<b<<endl;

      return 0; 

    }

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

a =211509811

b =1500000000 

В строке 1 мы умножили переменную а на 10 и получили результат, равный 15000000000, который нельзя сохранить даже с помощью usigned int. В этом случае было выполнено неявное преобразование типов, которое привело к потере точности вычисления.

В строке 2 перед умножением переменной на 10 было выполнено явное преобразование значения переменной к типу double. Полученное значение было сохранено во временную переменную. Затем временная переменная умножается на 10, и поскольку результат 150000000000 попадает в диапазон допустимых значений для типа double, то переполнения не происходит. После деления на 10 тип double неявно преобразуется к типу int, и мы получаем верный результат.

Явное преобразование типов следует использовать только в случае полной уверенности в его необходимости и понимания, для чего оно делается. Т.к. в случае явного преобразования компилятор не может проконтролировать корректность действий при изменении типов данных, то повышается возможность ошибок. 

1.9 Примеры простейших программ

  1. Составить программу, которая для заданного значения х вычисляет значение выражения 

Указание  по решению задачи. Прежде чем составлять программу, перепишем данное выражение с учетом приоритета операций по правилам языка С++: ( pow ( x , 2)+ sin ( x +1))/25.

 
Текст программы

    #include <iostream>

    #include <iomanip>

    #include <math.h>

    using namespace std;

    int main()

    {

      double x, y;

      cin >>x;

      y=(pow(x,2)+sin(x+1))/25; //1

      cout << "y=" <<setprecision(5) << y <<endl;

      return 0;

    } 

Результат работы программы при х=10:

y= 3.96 

2. Определить, является  ли целое число четным.

Указание  по решению задачи. Напомним, что число является четным, если остаток от деления данного числа на 2 равен нулю, а в С++ нулевое значение трактуется как ложь. С другой стороны, нечетное число будет давать остаток 1, который будет трактоваться как истина.

    #include <iostream>

    using namespace std;

    int main()

      int x;

      cout <<" Введите x"; cin >>x;

      (x % 2)? cout << " нечетное \n";

      cout << " четное \n"; //1

      return 0; 

    }

Результат работы программы: х ответ

45 нечетное

88 четное

 

    Функции в С++  

    2.1. Основные понятия

С увеличением объема программы становится невозможным  удерживать в памяти все детали. Естественным способом борьбы со сложностью любой задачи является разделение ее на части –подпрограммы. Разделение программы на подпрограммы позволяет также избежать избыточности кода, поскольку подпрограммы описывают один раз, а вызывают на выполнение многократно из различных участков программы.

Как мы уже знаем, в  С++ любая программа может состоять из нескольких функций, но в программе обязательно должна присутствовать функция main – главная функция, с которой начинается выполнение программы. Теперь мы научимся разрабатывать программы, состоящие из нескольких функций. А также рассмотрим такие важные понятия как классы памяти и модели памяти.

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

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

    [<класс памяти>] <тип результата><имя функции> ([<список параметров>] )

      {<тело функции>}

Рассмотрим основные части определения функции:

1) Класс памяти - необязательный элемент описания (см. раздел 2.4).

2) Тип результата возвращаемого функцией может быть любым, кроме массива или функции (но может быть указателем на массив или функцию). Если функция не должна возвращать значений, то указывается типvoid.Функция main должна возвращать тип int.

3) Список параметров определяет величины, которые требуется передать в функцию при ее вызове. Элементы списка параметров разделяются запятыми. Для каждого параметра указывается его тип и имя. Список параметров может быть пустым.

4) Тело функции представляет собой последовательность описаний и операторов. Если тип результата функции не void,то тело функции должно содержать команду return<возвращаемое значение>. 

Примеры 

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

Текст программы

    #include <iostream>

    using namespace std;

    int sum(int x,int y) //определение функции

      return x+y;

    }

    int main() // главная функция

    {

      int a=5, b=3, c;

      c=sum(a,b); //1

      cout<<"sum="<<c<<endl;

      cout<<"sum="<<sum(a,b)<<endl; //2

return 0;

 
}

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

sum =8

sum =8

В данном примере функция sum сразу определена, поэтому ее предварительное объявление не требуется. Функция возвращает целочисленное значение, которое формируется выражением после команды return. На этапе определения функции были указаны два формальных целочисленных параметра. На этапе вызова функции строка 1 и 2 в функцию передаются фактические параметры,которые по количеству и по типу совпадают с формальными параметрами. Если количество фактических и формальных параметров будет различным, то компилятор выдаст соответствующее сообщение об ошибке. Если параметры будут отличаться типами, то компилятор выполнит неявное преобразование типов. Обратите внимание на то, что в строке 1 вызов функции входит в состав выражения, располагаясь справа от знака присваивания. В общем случае выражения могут быть и более сложными. А в строке 2 результат, возвращаемый функцией, сразу помещается в выходной поток.

Рассмотрим другой пример: функция находит 

наибольшее значение для двух вещественных чисел.

Текст программы

    #include <iostream>

    using namespace std;

    float max(float x, float y); // объявление функции

    int main() // главная функция

      float a=5.5, b=3.2, c=14.1, d;

      d=max(max(a,b),c); //1

      cout<<"max="<<d<<endl;

return 0; 

}

    float max(float x, float y) // определение функции

      return (x>y)?x:y; 

    } 

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

max=14.1

В данном примере функция  вначале объявлена, а затем определена. Обратите внимание на то, что в строке 1 происходит два обращения к функции max.  

    2.2. Локальные и глобальные переменные

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

Глобальные переменные описываются вне функций, в том числе и вне функции main, поэтому они видны во всех функциях программы и могут использоваться для передачи данных между всеми функциями. Если имена глобальных и локальных переменных совпадают, то внутри функций локальные переменные «заменяют» глобальные, а после выхода из функции значение глобальной переменной восстанавливается. Однако с помощью операции доступ к области видимости [::] можно получить доступ к одноименной глобальной переменной. Рассмотрим небольшой пример:

Текст программы

    #include <iostream>

    using namespace std;

    int a =100, b =20; //глобальные переменные a и b

     
    void f 1( int a ) //локальная переменная a

      a+=10; //1

      cout<<"f1:\t"<<a <<"\t" << ::a<<endl;//2

    }

     
    void f2 ( int b ) //локальная переменная b 
    {

      b*=2; //3

      cout<<"f2\t"<<b <<"\t" <<a<<endl;//4

    } 

    int main()

    {

      cout <<"main:\t"<<a <<"\t"<<b <<endl; //5

      f1(a); f2(b);//6

      cout <<"main:\t"<<a<<"\t" <<b <<endl; //7

      return 0;

    } 

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

main : 100 20

f1: 110 100

f2: 40 100

main: 100 20

В строке 1 происходит изменение значения локальной переменной а. В строке2 на экран выводится значение локальной переменной а и через обращение к области видимости значение глобальной переменной а.

В строке 3 изменяется значение локальной переменной b. В строке 4 на экран выводится значение локальной переменной и значение глобальной переменной a. Так как в функции f2 нет локальной переменной с именема,то использовать операцию :: для обращения к глобальной переменной а не нужно.

В строках 5 и на экран выводятся значения глобальных переменных а и b. В строке 6 происходит вызов вначале функции f1, затем f2. Так как тип результата данных функций void, то для вызова каждой из них достаточно указать только имя функции и список параметров.

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

    2.3. Параметры функции

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

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

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

Информация о работе Лекции по программированию