Мегаобучалка Главная | О нас | Обратная связь


Double f (int x, int sum)



2019-11-21 154 Обсуждений (0)
Double f (int x, int sum) 0.00 из 5.00 0 оценок




Лабораторная работа №3

Основы программирования на СИ. Организация пользовательских функций.

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

Теоретическая информация

Функция — это именованный фрагмент программы. Данные могут передаваться в функцию и функция может возвращать значение.

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

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

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

Программы легче читаются, так как детали “скрыты” внутри функций.

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

Определение функции может иметь одну из двух форм:

Возвращаемый_тип имя_функции (список объявлений формальных параметров)

{

объявления /* тело функции */

опреаторы

}

Возвращаемый_тип имя_функции (список формальных параметорв)

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

{

объявления /* тело функции */

опреаторы

}

Рекомендуется первая форма, соответствующая Стандарт ANSI языка Си. Вторая форма использует стандарт K&R языка Си и обеспечивает совместимость с существующими программами на языке Си.

Тело функции – это составной оператор.

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

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

Важным различием между определением функции в стандарте ANSI (называемым также определением прототипа функции) и определением функции в стандарте K&R является то, что использовании формы ANSI требует, чтобы при вызове функции ее аргументы были преобразованы к типу, ожидаемому функцией. Для того, чтобы эти соглашения выполнялись, вызову функции должно предшествовать определение прототипа функции или объявление прототипа функции. Если тип фактического параметра не может быть преобразован к требуемому типу аргумента, компилятор выдает сообщение об ошибке.

При использовании определения или объявления функции в форме K&R Си выполняются предполагаемые по умолчанию преобразования типа.

Пример 1

/*возвращает площадь круга, заданного радиусом*/

Double area_circle(double radius)

{

return 3.14159* radius * radius ;

}

Оператор объявления функции (объявление прототипа функции) имеет следующий вид:

Возвращаемый_тип имя_функции (список объявлений типов параметров);

В стандарте ANSI языка Си типы возвращаемого значения функции и типы каждого из аргументов указываются в определении функции.

Пример 1

double area_circle(double rad) /* это определение функции*/

{

...

}

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

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

Пример 2

Int main()

{

double area_circle(double); /* это объявление функции */

...

}

Объявление функции area_circle() известно только той функции, в которой оно сделано, то есть функции main().

Пример 3

double area_circle(double); /* и это объявление функции*/

Int main ()

{

...

}

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

В объявлении прототипа функции могут указываться необязательные (фиктивные) имена.

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

X = f();

во время компиляции будет выдано сообщение, указывающее на ошибку в операции присваивания (правая часть операции присваивания имеет тип void).

Пример 4

#include <stdio.h>

Int main( )

{

double area_circle(double); /* это объявление функции*/

double rad = 50.5, area;

area=area_circle(rad);

printf("Area is %f\n",area);

}

/*возвращает площадь круга, заданного радиусом*/

double area_circle(double radius) /* это определение функции */

{

return 3.14159*radius*radius;

}

Аргументы функции

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

Параметры, перечисленные в заголовке описания функции, называются формальными параметрами, или просто параметрами, а записанные в операторе вызова функции – фактическими параметрами, или аргументами.

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

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

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

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

 

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

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

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

#include "stdafx.h"

#include <iostream>

using namespace std;

void f(int a, int* b, int& c)

{

a++;

(*b)++;

c++;

}

void main()

{

 

int a = 1, b = 1, c = 1;

cout << "a b c" << endl;

cout << a << ' ' << b << ' ' << c << endl;

f(a, &b, c);

cout << a << ' ' << b << ' ' << c << endl;

return ;

}

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

a b c

1 1 1

1 2 2

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

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

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

Если требуется запретить изменение параметра внутри функции, используется модификатор const:

int f( const char *);

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

По умолчанию параметры любого типа, кроме массива и функции (например, вещественного, структурного, перечисление, объединение, указатель), передаются в функцию по значению.

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

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

Составить программу вычисления значения функции

#include "stdafx.h"

#include <iostream>

#include <iomanip>

#include <math.h>

using namespace std;

float ax(float a, float x, float eps = 0.001f)

{

 

float xn = 1, y, y0;

 

unsigned int n = 1, nf = 1;

 

y = 1;

 

do

{

nf *= n;

xn *= x*log(a);

y0 = y;

y += xn/nf;

n++;

}

while (abs(y - y0) > eps);

 

return y;

}

void main()

{

setlocale(LC_ALL, "Russian");

 

const float eps = 0.1f;

 

float a, x, y;

 

cout << "Введите a, x:" << endl;

cin >> a >> x;

//передаем значение точности вычислений при вызове

y = ax(a, x, eps);

 

cout <<\

"Результат вычисления с точностью 0.1 (передается явно): "\

<< setw(8) << setprecision(5) << y <<endl;

 

//значение esp опускается

//внутри функции используется значение точности вычислений по умолчанию

y = ax(a, x);

 

cout <<\

"Результат вычисления с точностью 0.001 (по умолчанию): "\

<< setw(8) << setprecision(5) << y <<endl;

 

//вычисляем при помощи стандартных функций

y = pow(a, x);

 

cout <<\

"Результат вычисления с использованием стандартных функций: "\

<< setw(8) << setprecision(5) << y <<endl;

}

Результат выполнения:

Введите a, x:

2.5

3

Результат вычисления с точностью 0.1 (передается явно): 15.591

Результат вычисления с точностью 0.001 (по умолчанию): 15.625

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

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

Пример 1

#include <stdio.h>

Int main()

{

void print_growth (float, float);     /* объявление прототипа функции */

float amount = 250.,                 /* начальный вклад */

  interest = .075;               /* процент 7.5% */

print_growth (amount, interest);      /* вызов функции */

printf ("main: amount is %.2f \n", amount);

}

void print_growth (float val, float rate) /* определение прототипа функции */

 /* val = 250., rate=.075 */

{

val = (1+rate)* val;

printf ("Value after 1 year: %.2f\n", val);

}

Результат выполнения программы:

Value after 1 year: 268.75

Main: amount is 250.00

При вызове в функцию print_growth() передаются значения фактических параметров: в данном случае значения переменных amount и interest. Эти значения присваиваются новым переменным (формальным параметрам) val и rate, имеющим свои собственные адреса в памяти. Значение переменных amount и interest не изменяются вызовом функции print_growth().

Стек — это область памяти, используемая выполняемой программой для временного запоминания значений. При вызове функции все значения фактических параметров функции помещаются на стек. Затем запоминаются значения существенных регистров, таких как, например, указатель текущей команды. Значения регистров восстанавливаются при завершении управления вызванной функцией. При передаче управления вызванной функции значения фактических параметров связываются с соответствующими областями памяти на стеке, выделенными под формальные параметры. Затем определяется расположение на стеке для локальных (автоматических) переменных. Этот набор параметров, регистров и автоматических переменных для вызова функции называется кадром (frame). Если вызванная функция выполняет вызов другой функции, на стек помещается еще один кадр. Только один кадр является активным в любой момент времени. Активная часть стека "растет" при вызове функции и "сокращается" при выходе из функции. Стековое пространство все время повторно используется, и это является причиной того, почему автоматические переменные имеют до инициализации неопределенные значения. Они зависят от того, какие значения запоминались в соответствующем месте памяти до данного вызова функции.

Пример 1

Int main()

{

int x = 10, y = 100;

f(5, 10);

. . .

}

double f (int x, int sum)

{

double total;

g(20.5);

. . .

}

Float g(double val)

{

int count;

. . .

}

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

Рис. 1. Диаграмма построения стека

Функция может передавать одно значение обратно в вызывающую функцию.

Тип возвращаемого значения определяется в определении прототипа функции и объявляется в объявлении прототипа функции.

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

Синтаксис оператора возврата return:

return выражение;

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

Функция может иметь более одного оператора return.

Пример 1

Int func1(void)

{

...

if (n==0 || n==1)

return 1;

Else

return 0;

}

Пример 2

Void func2(void)

{

...

return ;

}

В прим. 1 функция func1() возвращает значение целого типа. В прим. 2 функция func2() не возвращает никакого значения.

Функция main() может использовать оператор return для возврата некоторого целого значения в родительский процесс.

Пример 3

#include <stdio.h>

Int main ()

{

int area_rect (int, int);

int len = 50, width = 4, area;

area = area_rect (len, width);

printf ("Area is %d \n", area);

return 0;

}



2019-11-21 154 Обсуждений (0)
Double f (int x, int sum) 0.00 из 5.00 0 оценок









Обсуждение в статье: Double f (int x, int sum)

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Модели организации как закрытой, открытой, частично открытой системы: Закрытая система имеет жесткие фиксированные границы, ее действия относительно независимы...
Почему люди поддаются рекламе?: Только не надо искать ответы в качестве или количестве рекламы...
Почему двоичная система счисления так распространена?: Каждая цифра должна быть как-то представлена на физическом носителе...
Почему человек чувствует себя несчастным?: Для начала определим, что такое несчастье. Несчастьем мы будем считать психологическое состояние...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (154)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.007 сек.)