Г.В. Ваныкина, Т.О. Сундукова 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Г.В. Ваныкина, Т.О. Сундукова



Г.В. Ваныкина, Т.О. Сундукова

ПРОГРАММИРОВАНИЕ

НА ЯЗЫКЕ С++

Часть 2. Основы программирования

 

 

Тула – 2010



ГОУ ВПО Тульский государственный педагогический университет имени Л.Н. Толстого

Кафедра информатики и методики обучения информатике

Г.В. Ваныкина, Т.О. Сундукова

ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ С++

Часть 2. Основы программирования

Лабораторный практикум

 

 

Тула - 2010


ББК 32.973.26

УДК 004.438

 

 

Ваныкина Г.В., Сундукова Т.О.

Программирование на языке С++. Часть 2. Основы программирования / Г.В. Ваныкина, Т.О. Сундукова; Тулгоспедуниверситет. – Тула, Папирус, 2010. – 101 с.

 

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

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

Для студентов специальностей 351500 Математическое обеспечение и администрирование информационных систем, 030100 Информатика, направлений подготовки 540200 Физико-математическое образование (профиль 540203 Информатика) и 511900 Информационные технологии. Может быть использовано студентами и преподавателями вузов, средних профессиональных и средних общеобразовательных учреждений.

 

УДК 004.438

 

 

Рецензенты:

Есаян А.Р. – доктор педагогических наук, профессор кафедры информатики и методики обучения информатике ТГПУ им. Л.Н. Толстого;

Баженова И.Ю. – кандидат физико-математических наук, доцент факультета ВМК МГУ им. М.В. Ломоносова

 

Утверждено

редакционно-издательским

советом университета

 

 

Ó Авторы, 2010

 


СОДЕРЖАНИЕ

ПРЕДИСЛОВИЕ.. 4

 

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

Подставляемые (встраиваемые) функции. Перегрузка функций. 6

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

Производные типы. Тип указатель: указатели на объекты .. 11

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

Тип указатель: указатели на функции. 18

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

Решение задач с использованием указателей. 23

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

Символьные данные и строки. 30

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

Функции для работы со строками. 37

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

Решение задач на строки. 43

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

Массивы: одномерные массивы .. 49

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

Одномерные массивы: задачи поиска, замены и перестановок элементов массива. 60

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

Одномерные массивы: задачи сортировок элементов массива. 67

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

Двумерные массивы. Задачи поиска, замены и суммирования элементов двумерного массива73

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

Двумерные массивы. Задачи сортировок и перестановок в двумерных массивах. 82

 

ИНДИВИДУАЛЬНЫЕ ЗАДАНИЯ.. 85

 

ЛИТЕРАТУРА.. 100


ПРЕДИСЛОВИЕ

Язык программирования С++ был разработан датским ученым Бьёрном Страуструпом в начале 90-х годов, первоначально как объектно-ориентированное расширение языка С. В настоящее время язык C++ является одним из наиболее мощных и широко распространенных языков программирования. Язык характеризуется своей универсальностью, он успешно применяется для решения разнообразных задач прикладного и системного программирования с использованием различных парадигм программирования – процедурной, объектно-ориентированной, параметрической.

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

Представленный в пособии набор лабораторных работ покрывает основные синтаксические и семантические аспекты языка С++ в объеме вузовских программ по основам программирования для специальностей 351500 Математическое обеспечение и администрирование информационных систем, 030100 Информатика и направлений подготовки 540200 Физико-математическое образование (профиль 540203 Информатика), 511900 Информационные технологии.

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

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

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

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

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

Пособие написано на основе курса лекций и лабораторно-практических занятий по программированию со студентами факультета математики, физики и информатики ТГПУ им. Л.Н. Толстого. Для базовой подготовки студентов, обучающихся на основе материалов пособия, достаточно освоение школьного курса информатики и ИКТ.


Лабораторная работа 21

Подставляемые (встраиваемые) функции. Перегрузка функций

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

 

Теоретические сведения

Подставляемые функции

Некоторые функции в С++ можно определить с использованием служебного слова inline. Такая функция называется подставляемой или встраиваемой.

Например:

/*функция возвращает расстояние от точки с координатами(x1,y1) до точки с координатами (x2,y2)*/

inline float Line(float x1,float y1,float x2, float y2) {

return sqrt(pow(x1-x2)+pow(y1-y2,2));

}

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

Подставляемыми не могут быть:

· рекурсивные функции;

· функции, у которых вызов размещается до ее определения;

· функции, которые вызываются более одного раза в выражении;

· функции, содержащие циклы, переключатели и операторы переходов;

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

Перегрузка функции

При определении функций в программах необходимо указывать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. Если на языке С была написана функция с именем add_values, которая работала с двумя целыми значениями, а в программе было необходимо использовать подобную функцию для передачи трех целых значений, то тогда следовало бы создать функцию с другим именем. Например, add_two_valuesи add_three_values. Аналогично если необходимо использовать подобную функцию для работы со значениями типа float, то нужна еще одна функция с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет определять несколько функций с одним и тем же именем. В процессе компиляции C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций называется перегрузкой.

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

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

#include <iostream.h>

 

int add_values(int a,int b) {

return(a + b);

}

 

int add_values (int a, int b, int c) {

return(a + b + c);

}

 

void main(void) {

cout << "200+801=" << add_values(200,801) << "\n";

cout << "100+201+700=" << add_values(100,201,700) << "\n";

}

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

Использование перегрузки функции

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

int day_of_week(int julian_day) {

// операторы

}

 

int day_of_week(int month, int day, int year) {

// операторы

}

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

int имя_функции(int имя_аргумента);

int имя_функции(int имя_аргумента);

/*недопустимая перегрузка имени: аргументы имеют одинаковое количество и одинаковый тип*/

Преимущества перегрузки функции:

· перегрузка функций улучшает удобочитаемость программ;

· перегрузка функций C++ позволяет программам определять несколько функций с одним и тем же именем;

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

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

Пример 1.

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

#include<iostream.h>

int average(int first_number, int second_number,

int third_number);

int average(int first_number, int second_number);

 

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

int number_A = 5, number_B = 3, number_C = 10;

cout << "Целочисленное среднее чисел " << number_A << " и ";

cout << number_B << " равно ";

cout << average(number_A, number_B) << ".\n\n";

cout << "Целочисленное среднее чисел " << number_A << ", ";

cout << number_B << " и " << number_C << " равно ";

cout << average(number_A, number_B, number_C) << ".\n";

} // конец главной функции

 

 

/*функция для вычисления целочисленного среднего значения 3-х целых чисел*/

int average(int first_number, int second_number,

int third_number) {

return((first_number + second_number + third_number)/3);

} // конец функции

 

/*функция для вычисления целочисленного среднего значения 2-х целых чисел*/

int average(int first_number, int second_number) {

return((first_number + second_number)/2);

} // конец функции

Пример 2.

/*Перегруженные функции имеют одинаковые имена и типы возвращаемых значений, но разные типы параметров*/

#include<stdio.h>

float average(int first_number, int second_number,

int third_number);

float average(float first_number, float second_number,

float third_number);

 

void main() {

int A = 5, B = 3, C = 10;

float A1 = 5.1, B1 = 3.4, C1 = 10.5;

printf("Среднее значение 3-х целых чисел = %f\n",

average(A,B,C)/3.0);

printf("Среднее значение 3-х вещественных чисел = %f\n",

average(A1,B1,C1)/3.0);

}

 

/*функция для вычисления среднего значения 3-х целых чисел*/

float average(int first_number, int second_number,

int third_number) {

return ((first_number + second_number + third_number));

}

 

/*функция для вычисления среднего значения 3-х вещественных чисел*/

float average(float first_number, float second_number,

float third_number) {

return ((first_number + second_number + third_number));

}

Задания

1. Составьте программу для решения задачи. Определите значение

,

где max(x, у)есть максимальное из чисел x, у. Разработайте функции нахождения максимального из двух целых и вещественных чисел.

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

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

 

Домашние задания

1. Наберите коды программ из Примеров 1 и 2. Выполните компиляцию и запуск программ.

2. Составьте программу для решения задачи. Даны координаты трех точек: A, B, C. Составьте программу нахождения углов треугольника ABC, если он существует. В противном случае выведите соответствующий комментарий. Для решения задачи разработайте функции только для случаев двумерного и трехмерного пространств.

3. Составьте программу, которая в зависимости от входных данных переводит часы и минуты в минуты или минуты – в часы и минуты. Используйте перегруженные функции. Например, при вводе 134 мин будет выдано значение 2 час 14 мин, а при вводе 2 час 14 мин – значение 134 мин.


Лабораторная работа 22

Теоретические сведения

В языке С++ разрешено использование наряду со стандартными типами производные типы, полученные на основе более простых базовых типов. Производные типы можно условно подразделить на две группы:

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

· массивы;

· указатели;

· ссылки;

· перечисления.

Составные производные типы. В группу составных производных типов входят:

· классы;

· структуры;

· объединения.

 

Переименование типов

В некоторых программных кодах бывает удобно вводить новые обозначения имен отдельных используемых типов данных. Задавать новое имя типу можно с помощью ключевого слова typedef.

Синтаксис:

typedef Тип НовоеИмяТипа[Размерность];

Например:

typedef unsigned int UNIT;

typedef char Msg[100];

Такое имя можно затем использовать так же, как и стандартное имя типа:

UNIT a,b,c;//переменные типа unsigned int

Msg str[10];//массив из 10 строк по 100 символов

 

Рассмотрим тип указатель. Указатели являются специальными объектами в программах на С++. Они предназначены для хранения адресов памяти.

Пример: Когда компилятор обрабатывает оператор определения переменной, например, int i=10;, то в памяти выделяется участок памяти в соответствии с типом переменной (int=> 2 байта) и записывает в этот участок указанное значение. Все обращения к этой переменной компилятор заменит на адрес области памяти, в которой хранится эта переменная. Операция &a является операцией взятия адреса ее операнда.

 
 

 


Рис. Адресация в С++

 

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

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

Указатель не является самостоятельным типом, он всегда связан с каким-то другим типом. Указатели делятся на две категории:

· указатели на объекты;

· указатели на функции.

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

 

Указатели на объекты

В общем случае синтаксис определения указателя на объект:

Тип*Описатель

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

Тип задает тип объекта, адрес которого будет содержать эта переменная и может соответствовать базовому, пустому (свободному, родовому, то есть типу void), перечислению, структурному типу и типу объединения. Реально указатель на void ни на что не указывает, но обладает способностью указывать на все что угодно после его типизирования каким-либо объектом.

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

Знак '*' является унарной операцией косвенной адресации, его операнд – указатель, а результат – адрес объекта, на который указывает операнд. Адресация является косвенной, так как обращение к области памяти осуществляется не напрямую по адресу (например, 1А2В), а через объект, которому в памяти соответствует определенный участок. Объем памяти, который выделяется для хранения данных, определяется типом данных и моделью памяти. Для приведенной на рисунке модели памяти адресом переменной типа char с именем ch является 1А2В, адресом переменной типа int с именем date является 1А2C, адресом переменной типа float с именем summa является 1А2E.

 

Машинный адрес 1А2В 1А2С 1А2D 1А2E 1А2F 1А30 1А31 1А32
байт байт байт байт байт байт байт байт
Значение в памяти 'G'   2.015*10-6  
Имя ch date summa  

 

Рис. Адресация типов в С++

 

Примеры определения указателей:

int *P;

/*указатель Р может содержать адрес объекта типа int*/

float *s;

/*указатель s может содержать адрес объекта типа float*/

 

Синтаксис указателя на объект базового типа:

Тип*ИмяУказателя;

где ИмяУказателя – идентификатор.

 

Например,

char *s; //переменная s – указатель на объект типа char

double *x; /*переменная х – указатель на объект типа

double, вещественного числа с плавающей

точкой удвоенной точности*/

int *k, *ff; //k, ff – указатели на объекты целого типа

int *p, y; /*р – указатель на объект типа int,

y – целочисленная переменная и не является

указателем*/

int x, *p; /*х – целочисленная переменная и не является

указателем,

р – указатель на объект типа int*/

 

 

Указатель может быть константой или переменной, а также указывать на константу или переменную.

Например:

int i;//целая переменная

const int ci=1; //целая константа

 

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

const int *pci; //указатель на целую константу

 

int *const cpi; //указатель-константа на целую переменную

const int *const cpc;

//указатель-константа на целую константу

 

При объявлении указателя его можно сразу проинициализировать (задать значение):

int *pi=&i; //указатель на целую переменную

const int *pci=&ci; //указатель на целую константу

 

int *const cpi=&i;

//указатель-константа на целую переменную

const int *const cpc=&ci;

//указатель-константа на целую константу

 

Если модификатор const относится к указателю (т.е. находится между именем указателя и *), то он запрещает изменение значения указателя, а если он находится слева от типа (т.е. слева от *), то он запрещает изменение значения, на которое указывает указатель.

 

Способы инициализации указателя

1. Присваивание указателю адреса области памяти существующего объекта:

· с помощью операции получения адреса:

int a=5;

int *p=&a;

 

· с помощью проинициализированного указателя

int *r=p;

 

2. Присваивание указателю адреса области памяти в явном виде:

char *cp=(char*)0х В800 0000;

где 0х В800 0000 – шестнадцатеричная константа, (char*) – операция приведения типа.

 

3. Присваивание указателю пустого значения:

int *N=NULL; или int *N=0;

Спецификатор указателя при форматированном выводе

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

%p – спецификатор указателя.

Этот спецификатор формата заставляет функцию printf() выводить на экран адрес, формат которого совместим с типом адресации, принятой в компьютере.

 

Операции с указателями

С указателями можно выполнять следующие операции:

· разыменование (*) – получение значения величины, адрес которой хранится в указателе;

· взятие адреса (&);

· присваивание;

· арифметические операции

§ сложение указателя только с константой,

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

§ инкремент (++) увеличивает значение указателя на величину sizeof(тип);

§ декремент (--) уменьшает значение указателя на величину sizeof(тип);

· сравнение;

· приведение типов.

Пример 1. Указатели различных типов указывают на одно и то же место в памяти. Однако при разыменовании получаются разные результаты.

// Выбор данных из памяти с помощью разных указателей

// Использование функций приведения типов

#include <iostream.h>

void main(){

unsigned long L=12345678;

char *cp=(char*)&L;

int *ip=(int*)&L;

long *lp=(long*)&L;

 

cout <<"\n&L = "<<&L;

cout <<"\nL = "<<L;

cout <<"\n*cp = "<<*cp;

cout <<"\n*ip = "<<*ip;

cout <<"\n*lp = "<<*lp;

}

Пример 2.

//Операции над указателями

#include <iostream.h>

void main(){

int a,c,b;

int *ca, *cb;

int *sa, *sb, *sc;

cout << "a = "; cin >> a;

cout << "b = "; cin >> b;

c=a+b;

 

sb=&b;//инициализация указателей через взятие адреса

sa=&a;

*sc=c;//инициализация указателя через обращение к переменной

ca=sa;//инициализация указателей через другой указатель

cb=sb;

*sa=12;//инициализация указателя через константу

 

cout << "\n*ca = " << *ca;

cout << "\n*sa = " << *sa;

cout << "\n*cb = " << *cb;

cout << "\n*sb = " << *sb;

cout << "\n*sc = " << *sc;

 

cout << "\nca = " << ca;

cout << "\ncb = " << cb;

cout << "\nsc = " << sc;

 

cout << "\na = " << a;

cout << "\nb = " << b;

cout << "\nc = " << c;

 

cout << "\n&a = " << &a;

cout << "\n&b = " << &b;

cout << "\n&c = " << &c;

 

cout << "\n*&a = " << *&a;

 

cout << "\n*cb-*ca = " << *cb-*ca;

cout << "\n*cb+*ca = " << *cb+*ca;

*cb=+2; //сложение с константой

cout << "\ncb = " << cb;

cb++; //инкремент

cout << "\ncb = " << cb;

ca--; //декремент

cout << "\ncа = " << cа;

/*разность указателей - разность их значений, деленная

на размер типа в байтах*/

cout << "\ncb-ca = " << cb-ca;

}

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

Пример 3.

#include <iostream.h>

void main() {

int x=10;

int y=10;

int *xptr=&x;

int *yptr=&y;

 

//сравниваем указатели

if (xptr == yptr)

cout << "Указатели равны\n";

else

cout << "Указатели неравны\n";

 

//сравниваем значения, на которое указывает указатель

if (*xptr == *yptr) {

cout << "Значения равны\n";

} else {

cout << "Значения неравны\n";}

}

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

Задания

1. Наберите код программы из Примера 2. Выполните компиляцию и запуск программы.

2. В программе определите и инициализируйте переменную типа double, указатель double * и указатель типа void *. Присвойте указателям адрес переменной. Напечатайте адрес переменной, значения указателей и значения, получаемые при разыменовании указателей. Чтобы продемонстрировать роли и последовательность выполнения унарных операций получения адреса & и разыменования *, выведите на печать значение выражения *&имя_переменной.

3. Определите и инициализируйте переменные разных типов (char, int, double) и один указатель типа void *. Последовательно присваивая указателю адреса переменных разных типов, выведите значения переменных с помощью разыменования указателя.

Домашние задания

1. Наберите коды программ из Примеров 1 и 3. Выполните компиляцию и запуск программ.

2. Определите и инициализируйте переменную типа double. Определите указатели char *, int *, double *, void *, инициализируйте их адресом переменной. Напечатайте значения указателей, их размеры и длины участков памяти, которые связаны с выражениями, разыменовывающими указатели.


Лабораторная работа 23

Теоретические сведения

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

 

Синтаксис определения указателя на функцию:

тип_функции(*имя_указателя)(спецификация_параметров)

где тип_функции – определяет тип возвращаемого функцией значения;

имя_указателя – идентификатор;

спецификация_параметров – определяет состав и типы параметров функции.

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

Например,

int (*func1Prt)(char);

задает определение указателя func1Prt на функцию с параметром типа char, возвращающую значение типа int.

Важнейшим элементом в определении указателя на функцию являются круглые скобки. Так следующий фрагмент:

int *func(char);

это не определение указателя, а объявление (прототип) функции c именем func и параметром типа char, возвращающей значение указателя типа int *. В этом случае указатель указывает на значение функции.

char *(*func2Prt)(char *,int);

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

 

Синтаксис вызова функции с помощью указателя:

(*имя_указателя)(список_фактических_параметров);

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

Арифметические операции над указателями на функции запрещены.

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

Пример 1.

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

#include<iostream.h>

void f1(); //объявление (прототип)функции f1

void f2(); //объявление (прототип)функции f2

 

void main() {

void (*ptr)(); //ptr - указатель на функцию

f2(); //явный вызов функции f2

ptr=f2;//указателю присваивается адрес функции f2

(*ptr)();

//вызов функции f2 по ее адресу с разыменованием указателя

ptr=f1;//указателю присваивается адрес функции f1

(*ptr)();

//вызов функции f1 по ее адресу с разыменованием указателя

ptr(); // вызов функции f1 без разыменованием указателя

}

 

//описание функции f1 и f2

void f1() {

cout << "Выполняется f1\n";

}

void f2() {

cout << "Выполняется f2\n";

}

Пример 2.

//Вариант 1 использования указателя на функцию

#include<stdio.h>

float plus(float, float); //Объявление (прототип) функции

 

void main() {

float x=2.1, y=4.89;

float (*func)(float,float);

//определение указателя func на функцию

printf("Сумма равна %.3f\n",plus(x,y));

func=plus;

//указателю присвоить адрес func точки входа в функцию plus

printf("(Используем указатель на функцию) Сумма =

%.3f\n",func(x,y));

}

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

float plus(float a, float b) {

return a+b;

}

 

 

//Вариант 2 использования указателя на функцию

#include<stdio.h>

float plus(float, float); //Объявление (прототип)функции

 

void main() {

float x=2.1, y=4.89;

float (*func)(float, float)=&plus;

//определение указателя на функцию plus

printf("Сумма равна %.3f\n",plus(x,y));

func=plus;

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

printf("(Используем указатель на функцию) Сумма =

%.3f\n",func(x,y));

}

 

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

float plus(float a, float b) {

return a+b;

}

 

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

Пример 3: Вычислите приближенное значение интегралов с помощью формулы прямоугольников, задав пределы интегрирования (a, b) и число интервалов разбиения (N): и .

#include <stdio.h>

#include <math.h>

//Объявление (прототипы) функций:

/*функция rectangle() возвращает значение типа double, ее параметры:*/

/*pf–указатель на функцию с параметром типа double, возвращающую значение double*/

/*a, b – пределы интегрирования, величины типа double*/

double rectangle(double(*pf)(double), double a, double b);

 

 

/*функция ratio() возвращает значение типа double, ее параметр типа double*/

double ratio(double x);

 

/*функция cos4_2() возвращает значение типа double, ее параметр типа double*/

double cos4_2(double v);

 

void main () {

double a,b,c;

printf("\nВведите значения пределов интегрирования:");

printf("\na= ");

scanf("%lf",&a);

printf("\nb= ");

scanf("%lf",&b);

c=rectangle(ratio,a,b);

printf("Первый интеграл = %f\n",c);

printf("Второй интеграл = %f\n",rectangle(cos4_2,a,b));

}

 

double rectangle(double(*pf)(double), double a, double b){

/*Вычисление определенного интеграла с помощью формулы прямоугольников*/

int N, i;

double h,s=0.0;

printf("\nВведите количество интервалов разбиения: N= ");

scanf("%d",&N);

printf("\na= ");

h=(b-a)/N; //Длина интервала разбиения

for (i=0;i<N;i++)

s+=pf(a+h/2+i*h);

return h*s;

}

 

double ratio(double x) { //Подынтегральная функция

double z; //Вспомогательная переменная

z=x*x+1;

return x/(z*z);

}

 

double cos4_2(double v){ //Подынтегральная функция

double w; //Вспомогательная переменная

w=cos(v);

return 4*w*w;

}

 

 

Задания

1. Наберите код программы из Примера 3. Выполните компиляцию и запуск программы.

2. Методом половинного деления решите уравнения на отрезке [ a, b ] с данной точностью е: , . При решении считать, что на данном отрезке существует единственный корень. Решите первое уравнение на отрезке [0, 1], а второе на отрезке [100, 150].

3. Решите задачу, используя один указатель на функцию. Разработайте четыре функции над двумя целыми параметрами, соответствующие арифметическим операциям (+, -, *, /). В основной программе задавайте два целых параметра и символьный знак операции до тех пор, пока не будет введен пробел в качестве знака операции. В выходных данных выводите значения функций.

Домашние задания

1. Наберите коды программ из Примеров 1 и 2. Выполните компиляцию и запуск программ.

2. Вычислите суммы с данной точностью е. Используйте указатели на функции как параметры. и .


Лабораторная работа 24

Теоретические сведения

Указатель на указатель

Указатель на указатель является формой многочисленного перенаправления или цепочки указателей (рис.).

 
 

 


Рис. Представление одиночного и мног



Поделиться:


Последнее изменение этой страницы: 2016-09-13; просмотров: 157; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.116.42.208 (0.442 с.)