Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Приоритет переменных с файловой и локальной областями действия



14.13.4. Проблемы области действия в C++.

14.13.5. Операция уточнения области действия в C++.

14.14. Равноправность функций в языке Си.

14.15. Компиляция программ, состоящих из двух или более функций.

10. Использование функций.

Функция — самостоятельная единица программы, спроектированная для реализации конкретной задачи. Функции в языке Си играют ту же роль, какую играют функции, подпрограммы и процедуры в других языках, хотя детали их структуры могут быть разными. Вызов функций приводит к выполнению некоторых действий. Например, при обращении к функции printf() осуществляется вывод данных на экран. Другие же функции позволяют получать некоторую величину, используемую затем в программе. К примеру, функция strlen() «сообщает» программе длину конкретной строки. В общем, функции могут выполнять различные действия и получать значения величин, используемых в программе.

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

7.25. Создание и использование простой функции.

Наша первая скромная цель — создание функции, которая печатает 65 символов» в ряд. Чтобы эта функция выполнялась в некотором контексте, мы включили ее в программу, которая печатает простой титул фирменного бланка. Ниже приведена полная соответствующая программа. Она состоит из функций main() и starbar().

/* титул фирменного бланка! */

#include<stdio.h>

#define NAME "MEGATHINK, INC."

#define ADDRESS "10 Megabuck Plaza"

#define PLACE "Megapolis, CA 94904"

void starbar();

void main()

{

starbar();

printf("%s\n", NAME);

printf("%s\n", ADDRESS);

printf("%s\n", PLACE);

starbar();

}

/* далее следует функция starbar() */

#define LIMIT 65

void starbar()

{

int count;

for (count=1; count<=LIMIT; count++)

putchar('*');

putchar('\n');

}

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

При рассмотрении этой программы необходимо обратить внимание на следующие моменты:

1. Мы вызвали функцию starbar() (или, можно сказать, обратились к ней) из функции main(), используя только ее имя. Это несколько напоминает заклинание, вызывающее злого духа, но, вместо того чтобы чертить пятиугольник, мы помещаем вслед за именем функции точку с запятой, создавая таким образом оператор:

starbar();

Это одна из форм вызова функции, но далеко не единственная. При написании функции starbar() мы следовали тем же правилам, что и при написании main(): вначале указывается имя, затем идет открывающая фигурная скобка, приводится описание используемых переменных, даются операторы, определяющие работу функции, и, наконец, закрывающая фигурная скобка. Мы даже поместили перед описанием функции starbar() директивы #define и #include, требующиеся для нее, а не для функции main().

2. Мы включили функции starbar() и main() в один файл. Вообще говоря, можно было создать два отдельных файла.

7.26. Прототипы функций.

Согласно стандарту ANSI С все функции должны иметь прототипы. Прототипы могут располагаться либо в самой программе на С или C++, либо в заголовочном файле. Большинство прототипов функций находятся в самих программах. Объявление функции в С и C++ начинается с ее прототипа. Прототип функции достаточно прост; обычно он включается в начало программы для того, чтобы сообщить компилятору тип и количество аргументов, используемых некоторой функцией. Использование прототипов обеспечивает более строгую проверку типов по сравнению с той, которая была при прежних стандартах С.

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

возвращаемый_тип имя_функции(тип_аргумевта(-ов)) (имя_аргумента(-ов));

Функция может иметь тип void, int, float и так далее и определяется возвращаемым типом. Имя_функции() — это любое значимое наименование, выбранное вами для определения этой функции.

7.27. Вызов по значению и вызов по ссылке.

В предыдущих примерах аргументы передаются функциям по значению. Когда переменные передаются по значению, в функцию передается копия текущего значения этой переменной. Поскольку передается копия переменной, сама эта переменная внутри вызывающей функции не изменяется. Вызов по значению — наиболее распространенный способ передачи информации (параметров) в функцию, и этот метод в С и C++ задан по умолчанию. Главным ограничением вызова по значению является то, что функция обычно возвращает только одно значение.

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

В результате выполнения операции & определяется адрес ячейки памяти, которая соответствует переменной. Если pooh — имя переменной, то &рooh — ее адрес. Можно представить себе адрес как ячейку памяти, но можно рассматривать его и как метку, которая используется компьютером для идентификации переменной. Предположим, мы имеем оператор

pooh = 24;

Пусть также адрес ячейки, где размещается переменная pooh, — 12126. Тогда в результате выполнения оператора

printf(%d %d\n", pooh, &pooh);

получим

24 12126

Более того, машинный код, соответствующий первому оператору, словами можно выразить приблизительно так: «Поместить число 24 в ячейку с адресом 12126».

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

/* контроль адресов */

#include<stdio.h>

void mikado(int);

void main()

{

int pooh = 2, bah = 5;

printf(" B main(), pooh = %d u &pooh = %u\n", pooh, &pooh);

printf(" B main(), bah = %d u &bah = %u\n", bah, &bah);

mikado(pooh);

}

void mikado(int bah)

{

int pooh = 10;

printf(" B mikado(), pooh = %d u &pooh = %u\n", pooh, &pooh);

printf(" B mikado(), bah = %d u &bah = %u\n", bah, &bah);

}

Мы воспользовались форматом %u (целое без знака) для вывода на печать адресов на тот случай, если их величины превысят максимально возможное значение числа типа int. В нашей вычислительной системе результат работы этой маленькой программы выглядит так:

В main(), pooh = 2 и &pooh = 56002

В main(), bah = 5 и &bah = 50004

В mikado(), pooh = 10 и &pooh = 55994

В mikado(), bah = 2 и &bah = 56000

7.28. Указатели.

В языке Си имеются и переменные типа указатель. Точно так же как значением переменной типа char является символ, а значением переменной типа int — целое число, значением переменной типа указатель служит адрес некоторой величины. Если мы дадим указателю имя ptr, то сможем написать, например, такой оператор

ptr = &pooh; /* присваивает адрес pooh переменной ptr */

Мы говорим в этом случае, что ptr «указывает на» pooh. Различие между двумя формами записи: ptr и &pooh, заключается в том, что ptr — это переменная, в то время как &pooh — константа. В случае необходимости мы можем сделать так, чтобы переменная ptr указывала на какой-нибудь другой объект:

ptr = &bah; /* ptr указывает на bah, а не на pooh */

Теперь значением переменной ptr является адрес переменной bah.

7.29. Операция косвенной адресации: *.

Предположим, мы знаем, что в переменной ptr содержится ссылка на переменную bah. Тогда для доступа к значению этой переменной можно воспользоваться операцией «косвенной адресации» (*).

(Не путайте эту унарную операцию косвенной адресации с бинарной операцией умножения*.)

val = *ptr; /* определение значения, на которое указывает ptr */

Последние два оператора, взятые вместе эквивалентны следующему:

val = bah;

Использование операций получения адреса и косвенной адресации оказывается далеко не прямым путем к результату; отсюда и появление слова «косвенная» в названии операции.

Пример

nurse = 22,

ptr = &nurse, /* указатель на nurse */

val = *ptr;

Результатом выполнения этою фрагмента является присваивание значения 22 переменной val

7.30. Описание указателей.

Мы знаем, как описывать переменные типа int и других типов. Но как описать переменную типа «указатель»? На первый взгляд это можно сделать так:

pointer ptr; /* неправильный способ описания указателя */

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

int *pi; /* указатель на переменную типа целого */

char *pc; /* указатель на символьную переменную */

float *pf,*pg;/* указатели на переменные с плавающей точкой */

7.30.1. Использование указателей для связи между функциями

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

#include<stdio.h>

void interchange(int *,int *);

void main()

{

int x = 5, y = 10;

printf(" At the beginning x = %d and y = %d.\n", x, y);

interchange(&x,&y); /* передача адресов функции */

printf(" And now x = %d and y = %d.\n", x, y);

}

void interchange(int *u, int *v) /* u и v являются указателями */

{

int temp;

temp = *u; /* temp присваивается значение, на которое указывает u */

*u = *v;

*v = temp;

}

После всех встретившихся трудностей, проверим, работает ли этот вариант!

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

interchange(&x, &y);

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

interchange(u,v)

при обращении будут заменены адресами и, следовательно, они должны быть описаны как указатели. Поскольку х и у — целого типа, а u и v являются указателями на переменные целого типа, и мы вводим следующее описание:

int *u, *v;

Далее в теле функции оператор описания

int temp

используется с целью резервирования памяти. Мы хотим поместить значение переменной х в переменную temp, поэтому пишем

temp = *u;

Вспомните, что значение переменной u — это &х, поэтому переменная и ссылается на х. Это означает, что операция *u дает значение х, которое как раз нам и требуется. Мы не должны писать, например, так:

temp = u; /* неправильно */

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

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

*u = *v;

который соответствует оператору

х = у;

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

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

function1(х);

происходит передача значения переменной х. Если же мы используем форму обращения

function2(&x);

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

function1(num)

int num;

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

function2(ptr)

int *ptr;

7.31. Рекурсия.

Рекурсия возникает в программах, в которых некоторая функция вызывает сама себя. Поначалу рекурсия кажется похожей на бесконечный цикл, однако, это не так. Рекурсия поддерживается как в С, так и в C++. Рекурсивные алгоритмы позволяют получить эффективное, читабельное и компактное решение задачи. Например: в следующей программе рекурсия используется для вычисления факториала числа. Факториал числа определяется как произведение самого числа и всех предыдущих целых чисел. Например:

8*7*6*5*4*3*2*1= 40320

Необходимо внимательно выбирать тип данных, так как произведение растет очень быстро: факториал 15 равен 1307674368000.

/*08FACTR.C

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

Вычисляет факториал числа.

Пример: 7! =7x6x5x4x3x2x1= 5040*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

using namespace std;

double dfactorial(double danswer);

main()

{

double dnumber=15.0;

double dresult;

dresult=dfactorial(dnumber);

printf("The factorial of %.0lf is: %.0lf\n",dnumber,dresult);

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

double dfactorial(double danswer)

{

if(danswer <= 1.0)

return(1.0);

else

return(danswer*dfactorial(danswer-1.0));

}

Возникает рекурсия, так как внутри функции dfactorial() имеется вызов ее самой.

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

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

Рассмотрим это более конкретно. В строке, состоящей из звездочек, 65 символов, а в строке MEGATHINK, INC. — 15. Поэтому в нашем первом варианте программы вслед за этим сообщением шло 50 пробелов. Чтобы сместить текст к центру, нужно сначала напечатать 25 пробелов, а потом текст, в результате чего слева и справа от данной фразы окажется по 25 пробелов. Следовательно, необходимо иметь возможность передать величину «25» функции, печатающей пробелы. Мы применяем тот же способ, что и при передаче символа '*' функции putchar(): используем аргумент. Тогда запись space(25) будет означать, что необходимо напечатать 25 пробелов. 25 — это аргумент. Мы будем вызывать функцию sрасе() три раза: один раз для каждой строки адреса. Вот как выглядит эта программа:

/* титул фирменного бланка2 */

#include<stdio.h>

#include<string.h>

#define NAME "MEGATHINK, INC."

#define ADDRESS " 10 Megabuck Plaza"

#define PLACE "Megapolis, CA 94904"

void starbar();

void space(int);

void main()

{

int spaces;

starbar();

space(25); /* space() использует в качестве аргумента константу */

printf("%s\n", NAME);

spaces = (65 - strlen(ADDRESS))/2;

/* мы заставляем программу вычислять, сколько пропустить пробелов */

space(spaces); /* аргументом является переменная */

printf("%s\n", ADDRESS);

space((65-strlen(PLACE))/2); /* аргументом является выражение */

printf("%s\n", PLACE);

starbar();

}

/* определение функции starbar() */

#define LIMIT 65

void starbar()

{

int count;

for (count=1; count<=LIMIT; count++)

putchar('*'); putchar('\n');

}

/* определение функции space() */

void space(int number)

{

int count; /* дополнительная переменная описывается после фигурной скобки */

for (count=1; count<=number; count++)

putchar(' ');

}

Обратите внимание на то, как мы экспериментировали при вызовах функции space(): мы задавали аргумент тремя различными способами. Являются ли все они работоспособными? Да — и вот доказательство.

Определение нашей функции начинается с двух строк:

space(number)

int number;

Первая строка информирует компилятор о том, что у функции space() имеется аргумент и что его имя number. Вторая строка — описание, указывающее компилятору, что аргумент number имеет тип int. Обратите внимание: аргумент описывается перед фигурной скобкой, которая отмечает начало тела функции. Вообще говоря, вы можете объединить эти две строки в одну:

space(int number;)

Независимо от формы записи переменная number называется «формальным» аргументом. Фактически это новая переменная, и в памяти компьютера для нее должна быть выделена отдельная ячейка.

Посмотрим, как можно пользоваться этой функцией.

Задача в данном случае состоит в том, чтобы присвоить некоторую величину формальному аргументу number. После того как эта переменная получит свое значение, программа сможет выполнить свою задачу. Мы присваиваем переменной number значение фактического аргумента при вызове функции. Рассмотрим наш первый случай использования функции space():

space(25);

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

number = 25;

Короче говоря, формальный аргумент — переменная в вызываемой программе, а фактический аргумент — конкретное значение, присвоенное этой переменной вызывающей программой.

7.32.1. Формальные и фактические параметры.

Каждое описание функции содержит некоторый список аргументов, называемый списком формальных параметров. Элементы в этом списке необязательны, поэтому список может быть как пустым, так и содержать комбинацию элементов любого типа данных, например integer, float и character.

Если какая-то программа вызывает функцию, то она передает этой функции список параметров, называемый списком фактических параметров. Если программа соответствует стандарту ANSI С, то списки формальных и фактических параметров полностью совпадают, хотя на практике строгая проверка не выполняется.

Рассмотрим следующий пример на С:

printf("This is hexadecimal %x and octal %o",ians);

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

7.32.2. Аргумент типа void.

В соответствии с ANSI С, отсутствие списка аргументов функции должно быть указано явно при помощи ключевого слова void. В C++ использование void пока не обязательно, но считается целесообразным. В следующей программе имеется простая функция voutput(), не имеющая параметров и не возвращающая никакого значения. Функция main() вызывает voutput(). При выходе из voutput() управление возвращается функции main(). Трудно придумать более простую функцию

/*08FVOID.C

Программа на С печатает сообщение при помощи функции.

В функции используются параметр типа void и стандартная

библиотечная функция С sqrt()*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

void voutput(void);

main()

{

/* Программа определяет квадратный корень */

printf("This programm will find the square root\n\n\n");

voutput();

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

void voutput(void)

{

double dt=12345.0;

double du;

du=sqrt(dt);

printf("The squre root of %lf is %lf\n",dt,du);

}

Обратите внимание, что функция voutput() вызывает библиотечную функцию С, называемую sqrt(). Прототип sqrt() находится в файле math.h. У функции один параметр в формате числа двойной длины, и возвращает она результат извлечения квадратного корня тоже в виде числа двойной длины.

7.32.3. Символьные параметры.

Функции можно передавать символьные значения. В следующем примере в функции main() одиночный символ считывается с клавиатуры и передается функции voutput(). Символ считывается функцией getch(). В стандартной библиотеке С имеются другие функции, тесно связанные с функцией getch(): getc(), getcharQ и getcheQ Эти функции можно использовать и в C++, однако во многих случаях предпочтительнее пользоваться cm. Функция getch() получает символ от стандартного устройства ввода (клавиатуры) и возвращает символьное значение, не отображая его на экране:

//08FCHAR.C

/*Программа на С считывает символ с клавиатуры, передает его функции

и печатает сообщение, использующее этот символ*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

void voutput(char c);

main()

{

char cyourchar;

/* Введите один символ с клавиатуры */

printf("Enter one character from the keyboard. \n");

cyourchar=getch();

voutput(cyourchar);

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

void voutput(char c)

{

int j;

for(j=0;j<=16;j++)

/* Введен символ... */

printf("The character typed is %c \n",c);

}

7.32.4. Целочисленные параметры.

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

//08FINT.C

/*Программа на С вычисляет значения на основании введенной длины.

Функция получает параметр типа int, введенный с

клавиатуры при помощи функции scanf()*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

void vside(int is);

main()

{

int iyourlength=0;

/* Введите с клавиатуры длину как целое число */

printf("Enter the length, as an integer from the keyboard. \n");

scanf("%d",&iyourlength);

vside(iyourlength);

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

void vside(int is)

{

int iarea, ivolume,isarea;

iarea=is*is;

ivolume=is*is*is;

isarea=6*iarea;

/* Длина стороны равна */

printf("The lenth of a side is %d \n",is);

/* Квадрат будет иметь площадь */

printf("The square would have an area of %d \n",iarea);

/* Куб будет иметь объем */

printf("The cube would have a volume of %d \n",ivolume);

/* Площадь поверхности куба */

printf("The surface area of the cub would have a square of %d \n",isarea);

}

7.32.5. Параметры в формате чисел с плавающей точкой.

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

7.32.6. Параметры в формате чисел двойной длины.

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

Таблица. Математические функции, описанные в заголовочном файле Microsoft math.h

acos, acosl Арккосинус
asin, asinl Арксинус
atan, atanl Арктангенс
atan2,atan2l Арктангенс
bessel Функции Бесселя
_cabs, _cabsl Абсолютное значение комплексного числа
ceil, ceill Целочисленное максимальное значение
_chgsign Инвертирование знака
_clear87, clearfp Чтение и сброс слова состояния числа с плавающей точкой
_control87, _controlfp Чтение старого управляющего слова числа с плавающей точкой и установка нового
_copysign Возвращает число x со знаком числа y
cos,cosl Косинус
cosh, coshl Гиперболический косинус
_dieeetomsbin Преобразование IEEE-числа двойной точности в двоичный формат Microsoft
Div Деление одного целого на другое, возвращается частное и остаток
_dmsbintoieee Преобразование Microsoft-числа двойной точности в формат IEEE
exp, expl Степенная функция
fabs, fabsl Абсолютное значение
_fieeetomsbin Преобразование IEEE-числа одинарной точности в двоичный формат Microsoft
_finite Проверка числа с плавающей точкой на бесконечность
floor, floorl Нахождение наибольшего целого, меньшего или равного аргументу
fmod, fmodl Нахождение остатка
_fmsbintoieee Преобразование Microsoft-числа одинарной точности в формат IEEE
_fpclass Возвращает слово состояния с информацией о классе чисел с плавающей точкой
_ fpieee_flt Вызов описанного пользователем обработчика исключительных ситуаций для чисел с плавающей точкой IEEE-стандарта
_fpreset Повторная инициализация пакета математических функций
frexp, frexpl Вычисление экспоненциального значения
_hypot, _hypotl Вычисление гипотенузы правильного треугольника
_isnan Проверка числа с плавающей точкой на значение “не число” (NAN)
ldexp, ldexpl Произведение от аргумента
Ldiv Деление одного целого long на другое, возвращается частное и остаток
log, logl Натуральный логарифм
log10, log10l Десятичный логарифм
_logb Выделение показателя числа с плавающей точкой
_irotl, _irotr Сдвиг числа unsigned long int влево или вправо
_matherr, _matherrl Обработка математических ошибок
_max, _min Определение большего или меньшего из двух значение
modf, modfl Деление аргумента на целую и дробную части
_nextafter Определение следующего значения
pow, powl Вычисление значения, возведенного в степень
Rand Получение псевдослучайного числа
_rotl, _rotr Сдвиг числа unsigned int влево или вправо
_scalb Степень числа 2, определяемая аргументом
sin, sinl Синус
sinh, sinhl Гиперболический синус
sqrt, sqrtl Квадратный корень
Srand Инициализация датчика псевдослучайных чисел
_status87, _statusfp Получение слов состояния числа с плавающей точкой
tan, tanl Тангенс
tanh, tanhl Гиперболический тангенс

7.32.7. Массивы в качестве параметров.

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

//08FPNTR.C

/*Программа на С передает функции массив в качестве параметра.

Для передачи информации о массиве используется указатель*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

void voutput(int *pinums);

main()

{

int iyourarray[7]={2,7,15,32,45,3,1};

/* Передать информацию о массиве в функцию */

printf("Send array information to function. \n");

voutput(iyourarray);

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

void voutput(int *pinums)

{

int t;

/* Результат равен */

printf("The result is:\n");

for(t=0;t<7;t++)

printf(" #%d %d\n",t,pinums[t]);

}

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

Также допустимо передать информацию по адресу и для безразмерного массива. В следующем примере показано, как это можно сделать на C++. (Такой же подход возможен и в С.) Информация, содержащаяся в массиве iyourarray, передается путем указания адреса его первого элемента.

7.33. Возвращение значения функцией: оператор return.

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

/* abs.драйвер */

#include<stdio.h>

int abs(int);

void main()

{

int a= 10, b= 0, c= -22;

int d, e, f;

d = abs(a);

e = abs(b);

f = abs(c);

printf(" %d %d %d\n", d, e, f);

}

/* функция, вычисляющая величину числа */

int abs(int x)

{

int y;

y = (x < 0)? - x: x; /*вспомните операцию?: */

return (y); /* возвращает значение у вызывающей программе */

}

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

10 0 22

Ключевое слово return указывает на то, что значение выражения, заключенного в круглые скобки, будет присвоено функции, содержащей это ключевое слово. Поэтому, когда функция abs() впервые вызывается нашим драйвером, значением abs(a) будет число 10, которое затем присваивается переменной d.

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

d = abs(a);

по-другому можно выразить так:

abs(a);

d = у;

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

/* функция, вычисляющая абсолютную величину числа, вторая версия */

abs(x)

int x;

{

if (x < 0)

return(-x);

else

return(x);

}

Наличие оператора return препятствует тому, чтобы оператор печати printf() когда-нибудь выполнился в программе. Вы можете также использовать просто оператор

return;

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

7.34. Типы функций.

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

1. Описать тип функции в ее определении:

char pun(ch, n) /* функция возвращает символ */

int n;

char ch;

float raft(num) /* функция возвращает величину типа float */

int num;

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

main()

{

char, rch, pun();

float raft;

Запомните! Если функция возвращает величину не типа int, указывайте тип функции там, где она определяется, и там, где она используется.

7.34.1. Функции типа void.

//08VOIDF.C

/*Программа на С иллюстрирует использование функции типа void.

Программа печатает двоичное представление числа*/

#include <iostream>

#include <conio.h>

#include <stdio.h>

using namespace std;

void vbinary(int ivalue);

main()

{

int ivalue;

/* Введите число с основанием 10 для преобразования в двоичное */

printf("Enter a decimal number to conversion to binary.\n");

scanf("%d",&ivalue);

vbinary(ivalue);

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

void vbinary(int idata)

{

int t=0;

int iyourarray[50];

while(idata!= 0)

{

iyourarray[t]=(idata %2);

idata/=2;

t++;

}

t--;

printf("The binary value is:\n");

for(;t>=0;t--)

printf("%1d",iyourarray[t]);

printf("\n");

}

7.34.2. Функции типа char.

Функция С clowercase() имеет символьный параметр и возвращает также символьное значение. В данном примере некоторая заглавная буква вводится с клавиатуры и передается в функцию, в которой для преобразования символа в строчную букву используется библиотечная функция tolower() (из стандартной библиотеки с прототипом в файле ctype.h). Близкие tolower() функции: toascii() и toupper().

//08CHARF.C

/*Программа на С иллюстрирует использование функции типа char.

Функция получает символ в верхнем регистре и преобразует его

в нижний регистр*/

#include <iostream>

#include <conio.h>

#include <stdio.h>

using namespace std;

char clowercase(char c);

main()

{

char clowchar,chichar;

/* Введите символ в верхнем регистре */

printf("Enter an uppercase character.\n");

chichar=getchar();

clowchar=clowercase(chichar);

printf("Entering character is %c\nConverting character is %c\n",chichar,clowchar);

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

char clowercase(char c)

{

return(tolower(c));

}

7.34.3. Функции типа int.

Следующая функция имеет целочисленные параметры и возвращает целые значения. Функция icube() получает из main() некоторое число (0, 2, 4, 6, 8, 10 и так далее), возводит его в куб и возвращает целое значение в main(). Исходное число и его куб выводятся на экран.

//08INTF.C

/*Программа на С иллюстрирует использование функции типа int.

Функция считывает по очереди целые числа и возвращает

их значения, возведенные в куб*/

#include <iostream>

#include <conio.h>

#include <stdio.h>

using namespace std;

int icube(int ivalue);

main()

{

int k,inumbercube;

for(k=0;k<20;k+=2)

{

inumbercube=icube(k);

/* Куб числа... равен... */

printf("The cube of the number %2d is %d\n",k,inumbercube);

}

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

int icube(int ivalue)

{

return(ivalue*ivalue*ivalue);

}

7.34.4. Функции типа long.

Следующий пример представляет собой программу на C++, получающую в качестве параметра целочисленную переменную и возвращающую значение типа long.

// 08LONGF.CPP

// Программа на C++, иллюстрирующая использование функции типа long.

// Эта функция получает целые числа и возвращает значения,

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

#include <iostream>

#include <conio.h>

#include <stdio.h>

using namespace std;

long lpower(int ivalue);

main()

{

int k;

long lanswer;

for(k=0;k<21;k++)

{

lanswer=lpower(k);

// 2 в степени... равно...

cout << "2 raised to the " << k

<< " power is " << lanswer << endl;

}

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

long lpower(int ivalue)

{

int t;

long lseed=1;

for(t=0;t<ivalue;t++)

lseed*=2;

return(lseed);

}

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

7.34.5. Функции типа float.

В следующем примере массив типа float передается в качестве параметра в функцию, которая возвращает значение типа float. В этом примере на C++ определяется произведение всех элементов массива.

// 08FLOATF.CPP

// Программа на C++ иллюстрирует использование функции типа float.

// Функция получает массив чисел типа float и возвращает их

// произведение в виде числа с плавающей точкой

#include <iostream>

#include <conio.h>

#include <stdio.h>

using namespace std;

float fproduct(float farray[]);

main()

{

float fmyarray[7]={(float) 4.3,(float) 1.8,(float) 6.12,(float) 3.19,

(float) 0.01,(float) 0.1,(float) 9876.2};

float fmultiplied;

fmultiplied=fproduct(fmyarray);

// Произведение всех введенных чисел равно

cout << "The product of all array enteris is: "

<< fmultiplied << endl;

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

float fproduct(float farray[])

{

int i;

float fpartial;

fpartial=farray[0];

for(i=1;i<7;i++)

fpartial*=farray[i];

return(fpartial);

}

7.34.6. Функции типа double.

В следующем примере на С передаются и возвращаются значения типа double. Функция dtrigcosine() преобразует значение угла, выраженное в градусах, в косинус этого угла.

//08DOUBLE.C

/*Программа на С иллюстрирует использование функции типа double.

Функция считывает целые числа от 0 до 90 и возвращает значение

косинуса каждого числа*/

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <math.h>

using namespace std;

const double dPi=3.14159265359;

double dtrigcosine(double dangle);

main()

{

int j;

double dcosine;

for(j=0;j<91;j++)

{

dcosine=dtrigcosine((double) j);

/* Косинус... градусов равен */

printf("The cosine of %d degrees is %19.18lf \n",j,dcosine);

}

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

double dtrigcosine(double dangle)

{

double dpartial;

dpartial=cos((dPi/180.0)*dangle);

return(dpartial);

}

7.35. Аргументы функции main().

В обоих языках, С и C++, имеется возможность обработки аргументов командной строки, которые представляют собой параметры, вводимые вместе с именем программы при ее вызове из командной строки операционной системы. Эта возможность позволяет передавать аргументы непосредственно вашей программе без дополнительных запросов из этой программы. Например, пусть некоторая программа получает из командной строки четыре аргумента:

YOURPROGRAM Sneakers, Dumbdog, Shadow, Wonderdog

Здесь четыре значения передаются из командной строки программе YOURPROGRAM. На самом деле эта информация передается функции main(). Один аргумент, получаемый main(), argc, представляет собой целое число, определяющее количество элементов командной строки плюс 1.

Начиная с DOS 3.0, название программы считается первым элементом, передаваемым из командной строки. Второй аргумент — это указатель на массив указателей на строки, называемый argv. Так как все элементы являются символьными строками, argv имеет тип char *[argc]. Поскольку все программы имеют название, argc всегда на единицу больше числа аргументов командной строки. В дальнейшем вы познакомитесь с различными способами извлечения разных типов данных из командной строки. Названия аргументов argc и argv являются общепринятыми именами переменных, используемых во всех программах на С и C++.

7.35.1. Строки.

Аргументы командной строки передаются как символьные строки, что облегчает работу с ними.

/*08SARGV.C

Программа на С иллюстрирует ввод в программу строковых данных

с аргументом командной строки

C:\> 08SARGV.EXE 07 Nik Helen*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

int main(int argc,char *argv[])

{

int t;

if(argc<2)

{

/* Введите несколько имен в командной строке */

/* при запуске этой программы! */

printf("Enter several names on the commond line\n");

printf("when executing this program!\n");

/* Попробуйте еще раз. */

printf("Please try again.\n");

exit(0);

}

for(t=1;t<argc;t++)

printf("Entry #%d is %s\n",t,argv[t]);

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

Эта программа целиком находится в функции main() и не требует дополнительных функций. Имена, введенные с командной строки, печатаются на экране в том же порядке. Если из командной строки вводятся цифровые значения, то они интерпретируются как ASCII-строки отдельных символов и должны печататься такими как есть.

7.35.2. Целые числа.

Во многих программах желательно иметь возможность ввода из командной строки целых чисел; это может быть, к примеру, программа для вычисления средних оценок студентов. В таких случаях символьная информация в кодах ASCII должна быть преобразована в целые значения. В приведенном ниже примере на C++ из командной строки вводится одно целое число. Поскольку на самом деле это число является символьной строкой, оно преобразуется в целое при помощи библиотечной функции atoi(). Значение из командной строки ivalue передается в использованную в предыдущем примере функцию vbinary(), которая преобразует значение ivalue в строку двоичных цифр и печатает ее на экране. Когда управление возвращается функции main(), значение ivalue печатается в восьмеричном и шестнадцатеричном форматах.

// 08IARGV.CPP

// Программа на C++ иллюстрирует ввод в программу целых чисел

// из командной строки

// C:\> 08IARGV.EXE 97

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

void vbinary(int idigits);

main(int argc, char *argv[])

{

int ivalue;

if(argc!=2)

{

// Введите десятичное число в командной строке.

// Оно будет преобразовано в двоичное, восьмеричное и

// шестнадцатеричное.

cout<<"Enter a decimal number on the command line.\n";

cout<<"It will be converted to binary, octal and hexadecimal.\n";

exit(1);

}

ivalue=atoi(argv[1]);

// Десятичное значение равно

cout << "The decimal value is: " << ivalue << endl;

vbinary(ivalue);

// Восьмеричное значение равно

cout << "The octal value is: " << oct << ivalue << endl;

// Шестнадцатеричное значение равно

cout << "The hexadecimal value is: " << hex << ivalue << endl;

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

void vbinary(int idigits)

{

int t=0;

int iyourarray[50];

while(idigits!= 0)

{

iyourarray[t]=(idigits %2);

idigits/=2;

t++;

}

t--;

// Двоичное значение равно

cout << "The binary value is: ";

for(;t>=0;t--)

cout << dec << iyourarray[t];

cout << endl;

}

7.35.3. Числа с плавающей точкой.

Следующий пример на С позволяет ввести из командной строки значения нескольких углов. Вычисляются косинусы углов и печатаются на экране. Поскольку у значений углов тип float, они могут выглядеть по-разному, например: 12.0, 45.78, 0.12345 или 15.

/*08FARGV.C

Программа на С иллюстрирует ввод в программу чисел

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

C:\> 08FARGV.EXE 90 45.78 0.12345 45*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

const double dPi=3.14159265359;

main(int argc, char *argv[])

{

int t;

double ddegree;

if(argc < 2)

{

/* Введите несколько углов в командной строке. */

/* Программа вычислит и напечатает */

/* косинусы введенных углов. */

printf("Type several angels on the command line.\n");

printf("Programm will colculate and print\n");

printf("the cosine af the angles entered.\n");

exit(1);

}

for(t=1;t<argc;t++)

{

ddegree=(double) atof(argv[t]);

printf("The cosine of %f is %15.14lf\n",

ddegree, cos((dPi/180)*ddegree));

}

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

Функция atof() используется для преобразования строковых аргументов командной строки в тип float. В программе для вычисления косинуса в функции printf() используется функция cos().

7.36. Важные возможности C++.

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

7.36.1. Встраивание (inline).

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

// Программа на C++ иллюстрирует использование встроенных функций.

// Встроенные функции работают лучше всего в тех случаях, когда часто

// повторяются короткие функции. В этом примере некоторое сообщение

// выводится на экран несколько раз

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

// Это - встроенная функция

inline void voutput(void)

{

cout << "This is an inline function!" << endl;

}

main()

{

int t;

// Программа печатает сообщение несколько раз

cout << "This program prints a message several times." << endl;

for(t=0;t<3;t++)

voutput();

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

7.36.2. Перегрузка (overloading).

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

// Программа на C++ иллюстрирует перегрузку функций.

// Перегружаемая функция получает массив целых чисел или чисел с

// плавающей точкой и возвращает их сумму в виде числа int или float

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

using namespace std;

int adder(int iarray[]);

float adder(float farray[]);

main()

{

int iarray[7]={5,1,6,20,15,0,12};

float farray[7]={3.3,5.2,0.05,1.49,3.12345,31.0,2.007};

int isum;

float fsum;

isum=adder(iarray);

fsum=adder(farray);

// Сумма целых чисел равна

cout << "The sum of the integer numbers is: "

<< isum << endl;

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

cout << "The sum of the float numbers is: "

<< fsum << endl;

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

int adder(int iarray[])

{

int i;

int ipartial;

ipartial=iarray[0];

for (i=1;i<7;i++)

ipartial+=iarray[i];

return (ipartial);

}

float adder(float farray[])

{

int i;

float fpartial;

fpartial=farray[0];

for (i=1;i<7;i++)

fpartial+=farray[i];

return (fpartial);

}

7.36.3. Многоточие (..).

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

void yourfunction{int t,float u,...);

Такая запись указывает компилятору С, что за обязательными переменными t и u могут следовать, а могут и не следовать другие аргументы. Конечно же, многоточие приостанавливает проверку типов.

7.37. Сложности в правилах области действия (scope rules).

Если используются переменные с различной областью действия, то можно столкнуться с совершенно неожиданными результатами программирования, называемыми побочными эффектами. Например, как вы уже знаете, может существовать переменная (вернее две переменные с одинаковым именем) как с файловой, так и с локальной областью действия. Правила области действия констатируют, что переменная с локальной областью действия (называемая локальной переменной) имеет приоритет по сравнению с переменной с файловой областью действия (называемой глобальной переменной). Хотя это выглядит достаточно просто, давайте рассмотрим несколько проблем, которые не так очевидны и с которыми вы можете столкнуться при программировании.

7.37.1. Неопределенные символы в программе на С.

В следующем примере четыре переменные имеют локальную область действия внутри функции main(). Копии переменных il и im передаются в функцию iproduct(). Это не нарушает правила области действия. Однако, когда функция iproduct() пытается обратиться к переменной in, она ее не находит. Почему? Потому что область действия этой переменной локальна для функции main().

/*08SCOPEP.C

Программа на С иллюстрирует проблемы, связанные с правилами

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

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

переменная in не известна функции, вычисляющей произведение*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

#include <stdarg.h>

#include <string.h>

using namespace std;

int iproduct(int iw,int ix);

/* int in=10; */

main ()

{

int il=3;

int im=7;

int in=10;

int io;

io=iproduct(il,im);

/* Произведение чисел равно */

printf("The product of the numbers is: %d\n", io);

printf ("\n\nPress any key to finish\n");

_getch();

return(0);

}

int iproduct(int iw,int ix)

{

int iy;

iy=iw*ix;

/* iy=iw*ix*in; */

return (iy);

}

7.37.2. Использование переменной с файловой областью действия.

В следующем примере переменной in определена файловая область действия. Глобальная видимость переменной in во всем файле позволяет ее использовать как функции main(), так и функции iproduct(). Также надо заметить, что обе функции, main() и iproduct(), могут изменять значение переменной. Существует хорошее программистское правило: если функции должны быть действительно переносимыми, не следует позволять им менять глобальные переменные.

/*08FSCOPE.C

Программа на С иллюстрирует проблемы, связанные с правилами

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

произведение трех чисел. Предыдущая проблема решена: третьей

переменной задана файловая область действия*/

#include "stdafx.h"

#include <iostream>

#include <conio.h>

#include <stdio.h>

#include <process.h>

#include <ctype.h>

#include <stdlib.h>

#include <math.h>

#include <stdarg.h>

#include <string.h>

using namespace std;

int iproduct(int iw,int ix);

int in=10;

main()

{

int il=3;

int im=7;

int io;

io=iproduct(il,im);

printf("The product is: %d\n", io);





Дата публикования: 2015-01-13; Прочитано: 978 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.252 с)...