Задачи для решения на тему «строки»
⇐ ПредыдущаяСтр 7 из 7
Вариант 1. Проверить, правильно ли в текст входят круглые скобки. Вариант 2. Удалить из текста все буквы b. Вариант 3. Удалить из текста все буквы k, идущие за буквой n. Вариант 4. Напечатать текст, удалив из него лишние пробелы, т.е. чтобы пробелы встречались по одному. Вариант 5. Подсчитать количество слов в тексте, начинающихся и заканчивающихся с одной и той же буквы. Вариант 6. Подсчитать число слов в тексте, содержащих букву b. Вариант 7. Перепечатать текст, подчеркивая в нем заглавные буквы (строкой ниже). Вариант 8. Удалить из слова повторяющиеся буквы. Вариант 9. Если в заданный текст входит каждая их букв слова key, напечатать yes, иначе no. Вариант 10. Напечатать буквы, которые идут в тексте непосредственно за буквой а. Вариант 11. Удалить из текста все пары букв оо. Вариант 12. Подсчитать число слов в тексте, оканчивающихся буквой w. Вариант 13. Проверить, является ли данное слово перевертышем. Вариант 14. Подсчитать количество слов в тексте, содержащих ровно три буквы е. Вариант 15. Удалить из слов в тексте все гласные буквы. Вариант 16. Если слово нечетной длины, удалить из него среднюю букву. Вариант 17. Заменить в тексте строчные буквы прописными, а прописные строчными. Вариант 18. Найти в тексте самое большое число. Вариант 19. Подсчитать частоты вхождения букв в текст. Вариант 20. Вывести на экран самое длинное слово в тексте.
8 Типы данных, определяемые пользователем
8.1 Переименование типов (typedef)
Для того чтобы сделать программу более ясной, можно задать типу новое имя с помощью ключевого слова typedef: typedef тип новое_имя [ размерность ]; В данном случае квадратные скобки являются элементом синтаксиса. Размерность может отсутствовать. Примеры:
typedef unsigned int UINT; typedef char Msg[100]; typedef struct{ char fio[30]; int date, code; double salary;} Worker; Введенное таким образом имя можно использовать таким же образом, как и имена стандартных типов: UINT i, j; // две переменных типа unsigned int Msg str[10]; // массив из 10 строк по 100 символов Worker staff[100]; // массив из 100 структур
8.2 Перечисления (enum)
При написании программ часто возникает потребность определить несколько именованных констант, для которых требуется, чтобы все они имели различные значения (при этом конкретные значения могут быть не важны). Для этого удобно воспользоваться перечисляемым типом данных, все возможные значения которого задаются списком целочисленных констант. Формат: enum [ имя_типа ] { список_констант }; Имя типа задается в том случае, если в программе требуется определять переменные этого типа. Компилятор обеспечивает, чтобы эти переменные принимали значения только из списка констант. Константы должны быть целочисленными и могут инициализироваться обычным образом. При отсутствии инициализатора первая константа обнуляется, а каждой следующей присваивается на единицу большее значение, чем предыдущей: enum Err {ERR_READ, ERR_WRITE, ERR_CONVERT}; Err error; ... switch (error){ case ERR_READ: /* операторы */ break; case ERR_WRITE: /* операторы */ break; case ERR_CONVERT: /* операторы */ break; } Константам ERR_READ, ERRJJRITE, ERR_CONVERT присваиваются значения 0, 1 и 2 соответственно. Другой пример: enum {two = 2, three, four, ten = 10, eleven, fifty = ten + 40}; Константам three и four присваиваются значения 3 и 4, константе eleven - 11.
8.3 Структуры (struct)
В отличие от массива, все элементы которого однотипны, структура может содержать элементы разных типов. В языке С++ структура является видом класса и обладает всеми его свойствами, но во многих случаях достаточно использовать структуры так, как они определены в языке С: struct [ имя_типа ] { тип_1 элемент_1; тип_2 элемент_2; … тип_n элемент_n;
} [ список_описателей ]; Элементы структуры называются полями структуры и могут иметь любой тип, кроме типа этой же структуры, но могут быть указателями на него. Если отсутствует имя типа, должен быть указан список описателей переменных, указателей или массивов. В этом случае описание структуры служит определением элементов этого списка: // Определение массива структур и указателя на структуру: struct { char fio[30]; int date, code; double salary; } staff[100], *ps; Если список отсутствует, описание структуры определяет новый тип, имя которого можно использовать в дальнейшем наряду со стандартными типами, например: struct Worker{ // описание нового типа Worker char fio[30]; int date, code; double salary; }; // описание заканчивается точкой с запятой // определение массива типа Worker и указателя на тип Worker: Worker staff[100], *ps; Имя структуры можно использовать сразу после его объявления (определение можно дать позднее) в тех случаях, когда компилятору не требуется знать размер структуры, например: struct List; // объявление структуры List struct Link{ List *p; // указатель на структуру List Link *prev, *succ; // указатели на структуру Link }: struct List { /* определение структуры List */}; Это позволяет создавать связные списки структур. Для инициализации структуры значения ее элементов перечисляют в фигурных скобках в порядке их описания: struct{ char fio[30]; int date, code; double salary; } worker = {"Страусенко", 31, 215, 3400.55}; При инициализации массивов структур следует заключать в фигурные скобки каждый элемент массива (учитывая, что многомерный массив - это массив массивов): struct complex{ float real, im; } compl [2][3] = { {{1, 1}, {1, 1}, {1, 1}}, // строка 1, то есть массив compl[0] {{2, 2}, {2, 2}, {2, 2}} // строка 2, то есть массив compl[1] }; Для переменных одного и того же структурного типа определена операция присваивания, при этом происходит поэлементное копирование. Структуру можно передавать в функцию и возвращать в качестве значения функции. Доступ к полям структуры выполняется с помощью операций выбора. (точка) при обращении к полю через имя структуры и -> при обращении через указатель, например: Worker worker, staff[100], *ps; … worker.fio = "Страусенко"; staff[8].code = 215; ps->salary = 0.12; Если элементом структуры является другая структура, то доступ к ее элементам выполняется через две операции выбора:
struct A {int a; double х;}; struct В {A a; double х;} х[2]; х[0].а.а = 1; х[1].х = 0.1; Как видно из примера, поля разных структур могут иметь одинаковые имена, поскольку у них разная область видимости. Более того, можно объявлять в одной области видимости структуру и другой объект (например, переменную или массив) с одинаковыми именами, если при определении структурной переменной использовать слово struct, но не советуем это делать: запутать компилятор труднее, чем себя.
8.4 Битовые поля
Битовые поля - это особый вид полей структуры. Они используются для плотной упаковки данных, например, флажков типа «да/нет». Минимальная адресуемая ячейка памяти - 1 байт, а для хранения флажка достаточно одного бита. При описании битового поля после имени через двоеточие указывается длина поля в битах (целая положительная константа): struct Options{ bool centerX:1; bool centerY:1; unsigned int shadow:2; unsigned int palette:4; }; Битовые поля могут быть любого целого типа. Имя поля может отсутствовать, такие поля служат для выравнивания на аппаратную границу. Доступ к полю осуществляется обычным способом - по имени. Адрес поля получить нельзя, однако в остальном битовые поля можно использовать точно так же, как обычные поля структуры. Следует учитывать, что операции с отдельными битами реализуются гораздо менее эффективно, чем с байтами и словами, так как компилятор должен генерировать специальные коды, и экономия памяти под переменные оборачивается увеличением объема кода программы. Размещение битовых полей в памяти зависит от компилятора и аппаратуры.
8.5 Задачи для решения на тему «структуры»
Вариант 1. Описать структуру, содержащую следующие поля: - фамилия и инициалы; - номер группы; - успеваемость (3 предмета). Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на дисплей фамилий и номеров групп для всех студентов, если средний балл студента больше 4. Вариант 2. Описать структуру, содержащую следующие поля: - фамилия и инициалы; - номер группы; - успеваемость (3 предмета).
Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на дисплей фамилий и номеров групп для всех студентов, имеющих оценки 4 и 5. Вариант 3. Описать структуру, содержащую следующие поля: - фамилия и инициалы; - номер группы; - успеваемость (3 предмета). Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур. - вывод на дисплей фамилий и номеров групп для всех студентов, имеющих хотя бы одну оценку 2. Вариант 4. Описать структуру, содержащую следующие поля: - название пункта назначения рейса; - номер рейса; - тип самолета. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран номеров рейсов и типов самолетов, вылетающих в пункт назначения, название которого совпало с названием, введенным с клавиатуры. Вариант 5. Описать структуру, содержащую следующие поля: - название пункта назначения рейса; - номер рейса; - тип самолета. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран пунктов назначения и номеров рейсов, обслуживаемых самолетом, тип которого введен с клавиатуры. Вариант 6. Описать структуру, содержащую следующие поля: - фамилия и инициалы работника; - название занимаемой должности; - год поступления на работу. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на дисплей фамилий работников, чей стаж работы в организации превышает значение, введенное с клавиатуры. Вариант 7. Описать структуру, содержащую следующие поля: - название пункта назначения; - номер поезда; - время отправления. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о поездах, отправляющихся после введенного с клавиатуры времени. Вариант 8. Описать структуру, содержащую следующие поля: - название пункта назначения; - номер поезда; - время отправления. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о поездах, направляющихся в пункт, название которого введено с клавиатуры. Вариант 9. Описать структуру, содержащую следующие поля: - название пункта назначения; - номер поезда; - время отправления. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур;
- вывод на экран информации о поезде, номер которого введен с клавиатуры. Вариант 10. Описать структуру, содержащую следующие поля: - название начального пункта маршрута; - название конечного пункта маршрута; - номер маршрута. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о маршруте, номер которого введен с клавиатуры. Вариант 11. Описать структуру, содержащую следующие поля: - название начального пункта маршрута; - название конечного пункта маршрута; - номер маршрута. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о маршрутах, которые начинаются или оканчиваются в пункте, название которого введено с клавиатуры. Вариант 12. Описать структуру, содержащую следующие поля: - фамилия, имя; - номер телефона; - дата рождения (число, месяц, год). Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о человеке, номер телефона которого введен с клавиатуры. Вариант 13. Описать структуру, содержащую следующие поля: - фамилия, имя; - номер телефона; - дата рождения (число, месяц, год). Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о людях, чьи дни рождения приходятся на месяц, значение которого введение клавиатуры. Вариант 14. Описать структуру, содержащую следующие поля: - фамилия, имя; - номер телефона; - дата рождения (число, месяц, год). Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о человеке, чья фамилия введена с клавиатуры. Вариант 15. Описать структуру, содержащую следующие поля: - фамилия, имя; - знак Зодиака; - дата рождения (число, месяц, год). Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структу; - вывод на экран информации о человеке, чья фамилия введена с клавиатуры. Вариант 16. Описать структуру, содержащую следующие поля: - фамилия, имя; - знак Зодиака; - дата рождения (число, месяц, год). Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о людях, родившихся под знаком, название которого введено с клавиатуры. Вариант 17. Описать структуру, содержащую следующие поля: - фамилия, имя; - знак Зодиака; - дата рождения (число, месяц, год). Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о людях, родившихся в месяц, значение которого введено с клавиатуры. Вариант 18. Описать структуру, содержащую следующие поля: - название товара; - название магазина, в котором продается товар; - стоимость товара в рублях. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о товаре, название которого введено с клавиатуры. Вариант 19. Описать структуру, содержащую следующие поля: - название товара; - название магазина, в котором продается товар; - стоимость товара в рублях. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о товарах, продающихся в магазине, название которого введено с клавиатуры. Вариант 20. Описать структуру, содержащую следующие поля: - название товара; - название магазина, в котором продается товар; - стоимость товара в рублях. Написать программу, выполняющую следующие действия: - ввод с клавиатуры данных в массив, состоящий из 5 структур; - вывод на экран информации о товаре с минимальной ценой.
9 Функции
9.1 Объявление и определение функций
Функция - это именованная последовательность описаний и операторов, выполняющая какое-либо законченное действие. Функция может принимать параметры и возвращать значение. Любая программа на С++ состоит из функций, одна из которых должна иметь имя main (с нее начинается выполнение программы). Функция начинает выполняться в момент вызова. Любая функция должна быть объявленаи определена. Как и для других величин, объявлений может быть несколько, а определение только одно. Объявление функции должно находиться в тексте раньше ее вызова для того, чтобы компилятор мог осуществить проверку правильности вызова. Объявление функции (прототип, заголовок, сигнатура)задает ее имя, тип возвращаемого значения и список передаваемых параметров. Определение функции содержит, кроме объявления, телофункции, представляющее собой последовательность операторов и описаний в фигурных скобках: [ класс ] тип имя ([ список_параметров ]) [throw (исключения)] { тело функции } Рассмотрим составные части определения: - с помощью необязательного модификатора класс можно явно задать область видимости функции, используя ключевые слова extern и static: a extern - глобальная видимость во всех модулях программы (по умолчанию); b static - видимость только в пределах модуля, в котором определена функция; - тип возвращаемого функцией значения может быть любым, кроме массива и функции (но может быть указателем на массив или функцию). Если функция не должна возвращать значение, указывается тип void; - список параметров определяет величины, которые требуется передать в функцию при ее вызове. Элементы списка параметров разделяются запятыми. Для каждого параметра, передаваемого в функцию, указывается его тип и имя (в объявлении имена можно опускать). В определении, в объявлении и при вызове одной и той же функции типы и порядок следования параметров должны совпадать. На имена параметров ограничений по соответствию не накладывается, поскольку функцию можно вызывать с различными аргументами, а в прототипах имена компилятором игнорируются (они служат только для улучшения читаемости программы). Тип возвращаемого значения и типы параметров совместно определяют тип функции. Для вызова функции в простейшем случае нужно указать ее имя, за которым в круглых скобках через запятую перечисляются имена передаваемых аргументов. Вызов функции может находиться в любом месте программы, где по синтаксису допустимо выражение того типа, который формирует функция. Если тип возвращаемого функцией значения не void, она может входить в состав выражений или, в частном случае, располагаться в правой части оператора присваивания. Пример функции, возвращающей сумму двух целых величин: #include <iostream.h> int sum(int a, int b); // объявление функции int main(){ int a = 2, b = 3, c, d; с = sum(a, b); // вызов функции cin» d; cout «sum(c, d); // вызов функции return 0; } int sum(int a, int b){ // определение функции return (а + b); } Пример функции, выводящей на экран поля переданной ей структуры: #include <iostream.h> struct Worker{ char fio[30]; int date, code; double salary; }; void print_worker(Worker); //объявление функции int main(){ Worker staff[100]; ... /* формирование массива staff */ for (int i = 0; i<100; i++) print_worker(staff[i]); // вызов функции return 0; } void print_worker(Worker w){ //определение функции cout «w.fio «' ' «w.date «' ' «w.code «' ' «w.salary «endl; } Все величины, описанные внутри функции, а также ее параметры, являются локальными. Областью их действия является функция. При вызове функции, как и при входе в любой блок, в стеке выделяется память под локальные автоматические переменные. Кроме того, в стеке сохраняется содержимое регистров процессора на момент, предшествующий вызову функции, и адрес возврата из функции для того, чтобы при выходе из нее можно было продолжить выполнение вызывающей функции. При выходе из функции соответствующий участок стека освобождается, поэтому значения локальных переменных между вызовами одной и той же функции не сохраняются. Если этого требуется избежать, при объявлении локальных переменных используется модификатор static: #include <iostream.h> void f(int a){ int m = 0; cout «"n m p\n"; while (a--){ static int n = 0; int p = 0; cout «n++ «' ' «m++ «' ' «p++ «'\n'; } } int main(){ f(3); f(2); return 0;} Статическая переменная n размещается в сегменте данных и инициализируется один раз при первом выполнении оператора, содержащего ее определение. Автоматическая переменная m инициализируется при каждом входе в функцию. Автоматическая переменная р инициализируется при каждом входе в блок цикла.
Программа выведет на экран: n m р 0 0 0 1 1 0 2 2 0 n m р 3 0 0 4 1 0 При совместной работе функции должны обмениваться информацией. Это можно осуществить с помощью глобальных переменных, через параметры и через возвращаемое функцией значение.
9.2 Глобальные переменные
Глобальные переменные видны во всех функциях, где не описаны локальные переменные с теми же именами, поэтому использовать их для передачи данных между функциями очень легко. Тем не менее, это не рекомендуется, поскольку затрудняет отладку программы и препятствует помещению функций в библиотеки общего пользования. Нужно стремиться к тому, чтобы функции были максимально независимы, а их интерфейс полностью определялся прототипом функции.
9.3 Возвращаемое значение
Механизм возврата из функции в вызвавшую ее функцию реализуется оператором return [ выражение ]; Функция может содержать несколько операторов return (это определяется потребностями алгоритма). Если функция описана как void, выражение не указывается. Оператор return можно опускать для функции типа void, если возврат из нее происходит перед закрывающей фигурной скобкой. Выражение, указанное после return, неявно преобразуется к типу возвращаемого функцией значения и передается в точку вызова функции. Примеры: int fl(){return 1;} //правильно void f2(){return 1;} // неправильно, f2 не должна возвращать значение double f3(){return 1;} // правильно, 1 преобразуется к типу double
9.4 Параметры функции
Механизм параметров является основным способом обмена информацией между вызываемой и вызывающей функциями. Параметры, перечисленные в заголовке описания функции, называются формальными параметрами, или просто параметрами, а записанные в операторе вызова функции - фактическими параметрами, или аргументами. При вызове функции в первую очередь вычисляются выражения, стоящие на месте аргументов; затем в стеке выделяется память под формальные параметры функции в соответствии с их типом, и каждому из них присваивается значение соответствующего аргумента. При этом проверяется соответствие типов и при необходимости выполняются их преобразования. При несоответствии типов выдается диагностическое сообщение. Существует два способа передачи параметров в функцию: по значению и по адресу. При передаче по значению в стек заносятся копии значений аргументов, и операторы функции работают с этими копиями. Доступа к исходным значениям параметров у функции нет, а, следовательно, нет и возможности их изменить. При передаче по адресу в стек заносятся копии адресов аргументов, а функция осуществляет доступ к ячейкам памяти по этим адресам и может изменить исходные значения аргументов: #include <iostream.h> void f(int i, int * j, int & k); int main(){ int i = 1, j = 2, k = 3; cout «"i j k\n"; cout «i «' '«j «' '«k «'\n'; f(i, &j, k); cout «i «' '«j «' '«k; return 0; } void f(int i, int* j, int& k){ i++; (*j)++; k++; } Результат работы программы: i j k 1 2 3 1 3 4 Первый параметр (i) передается по значению. Его изменение в функции не влияет на исходное значение. Второй параметр (j) передается по адресу с помощью указателя, при этом для передачи в функцию адреса фактического параметра используется операция взятия адреса, а для получения его значения в функции требуется операция разыменования. Третий параметр (k) передается по адресу с помощью ссылки. При передаче по ссылке в функцию передается адрес указанного при вызове параметра, а внутри функции все обращения к параметру неявно разыменовываются. Поэтому использование ссылок вместо указателей улучшает читаемость программы, избавляя от необходимости применять операции получения адреса и разыменования. Использование ссылок вместо передачи по значению более эффективно, поскольку не требует копирования параметров, что имеет значение при передаче структур данных большого объема. По умолчанию параметры любого типа, кроме массива и функции (например, вещественного, структурного, перечисление, объединение, указатель), передаются в функцию по значению.
9.5 Передача массивов в качестве параметров
При использовании в качестве параметра массива в функцию передается указатель на его первый элемент, иными словами, массив всегда передается по адресу. При этом информация о количестве элементов массива теряется, и следует передавать его размерность через отдельный параметр (в случае массива символов, то есть строки, ее фактическую длину можно определить по положению нуль-символа). #include <iostream.h> int sum(const int * mas, const int n); int const n = 10; int main(){ int marks[n] = {3, 4, 5, 4, 4}; cout «"Сумма элементов массива: " «sum(marks, n); return 0; } int sum(const int * mas, const int n){ // варианты: int sum(int mas[], int n) // или int sum(int mas[n], int n) // (величина n должна быть константой) int s = 0; for (int i = 0; i<n; i++) s += mas[i]; return s; } При передаче многомерных массивов все размерности, если они не известны на этапе компиляции, должны передаваться в качестве параметров. Внутри функции массив интерпретируется как одномерный, а его индекс пересчитывается в программе. В приведенном ниже примере с помощью функции подсчитывается сумма элементов двух двумерных массивов. Размерность массива b известна на этапе компиляции, под массив а память выделяется динамически: #include <stdio.h> #include <stdlib.h> int sum(const int *a, const int nstr, const int nstb); int main(){ int b[2][2] = {{2, 2}, {4, 3}}; printf("Cуммa элементов b: %d\n", sum(&b[0][0], 2, 2)); // имя массива передавать в sum нельзя из-за несоответствия типов int i, j, nstr, nstb, *a; printf("Введите количество строк и столбцов: \n"); scanf("%d%d", &nstr, &nstb); а = (int *)malloc(nstr * nstb * sizeof(int)); for (i = 0; i<nstr; i++) for (j = 0; j<nstb; j++) scanf("%d", &a[i * nstb + j]); printf("Cyммa элементов a: %d\n", sum(a, nstr, nstb)); return 0; } int sum(const int *a, const int nstr, const int nstb){ int i, j, s = 0; for (i = 0; i<nstr; i++) for (j = 0; j<nstb; j++) s += a[i * nstb + j]; return s; } Для того чтобы работать с двумерным массивом естественным образом, можно применить альтернативный способ выделения памяти: #include <iostream.h> int sum(int **a, const int nstr, const int nstb); int main(){ int nstr, nstb; cin» nstr» nstb; int **a, i, j; // Формирование матрицы a: a = new int* [nstr]; for (i =0; i<nstr; i++) a[i] = new int [nstb]; for (i = 0; i<nstr; i++) for (j = 0; j<nstb; j++) cin» a[i][j]; cout «sum(a, nstr, nstb); return 0; } int sum(int **a, const int nstr, const int nstb){ int i, j, s = 0; for (i = 0; i<nstr; i++) for (j = 0; j<nstb; j++)s += a[i][j]; return s; } В этом случае память выделяется в два этапа: сначала под столбец указателей на строки матрицы, а затем в цикле под каждую строку, как показано на рисунке 7.2. Освобождение памяти должно выполняться в обратном порядке.
9.6 Параметры со значениями по умолчанию
Чтобы упростить вызов функции, в ее заголовке можно указать значения параметров по умолчанию. Эти параметры должны быть последними в списке и могут опускаться при вызове функции. Если при вызове параметр опущен, должны быть опущены и все параметры, стоящие за ним. В качестве значений параметров по умолчанию могут использоваться константы, глобальные переменные и выражения: int f(int a, int b = 0); void f1(int, int = 100, char* = 0); /* обратите внимание на пробел между * и = (без него получилась бы операция сложного присваивания *=) */ void err(int errValue = errno); // errno - глобальная переменная … f(100); f(a, 1); // варианты вызова функции f f1(a); f1(a, 10); f1(a, 10, "Vasia"); // варианты вызова функции f1 f1(a,,"Vasia") // неверно!
9.7 Функции с переменным числом параметров
Если список формальных параметров функции заканчивается многоточием, это означает, что при ее вызове на этом месте можно указать еще несколько параметров. Проверка соответствия типов для этих параметров не выполняется, char и short передаются как int, a float - как double. В качестве примера можно привести функцию printf, прототип которой имеет вид: int printf (const char*,...); Это означает, что вызов функции должен содержать по крайней мере один параметр типа char* и может либо содержать, либо не содержать другие параметры: printf("Введите исходные данные"); // один параметр printf("Cyммa: %5.2f рублей", sum); // два параметра printf("%d %d %d %d ", a, b, c, d); // пять параметров Для доступа к необязательным параметрам внутри функции используются макросы библиотеки va_start, va_arg и va_end, находящиеся в заголовочном файле <stdarg.h>. Поскольку компилятор не имеет информации для контроля типов, вместо функций с переменным числом параметров предпочтительнее пользоваться параметрами по умолчанию или перегруженными функциями, хотя можно представить случаи, когда переменное число параметров является лучшим решением.
9.8 Рекурсивные функции
Рекурсивной называется функция, которая вызывает саму себя. Такая рекурсия называется прямой. Существует еще косвенная рекурсия, когда две или более функций вызывают друг друга. Если функция вызывает себя, в стеке создается копия значений ее параметров, как и при вызове обычной функции, после чего управление передается первому исполняемому оператору функции. При повторном вызове этот процесс повторяется. Ясно, что для завершения вычислений каждая рекурсивная функция должна содержать хотя бы одну нерекурсивную ветвь алгоритма, заканчивающуюся оператором возврата. При завершении функции соответствующая часть стека освобождается, и управление передается вызывающей функции, выполнение которой продолжается с точки, следующей за рекурсивным вызовом. Классическим примером рекурсивной функции является вычисление факториала (это не означает, что факториал следует вычислять именно так). Для того чтобы получить значение факториала числа n, требуется умножить на n факториал числа (n-1). Известно также, что 0!=1 и 1!=1. long fact(long n){ if (n==0 || n==1) return 1; return (n * fact(n - 1); } To же самое можно записать короче: long fact(long n){ return (n>1)? n * fact(n - 1): 1; } Рекурсивные функции чаще всего применяют для компактной реализации рекурсивных алгоритмов, а также для работы со структурами данных, описанными рекурсивно, например, с двоичными деревьями. Любую рекурсивную функцию можно реализовать без применения рекурсии, для этого программист должен обеспечить хранение всех необходимых данных самостоятельно. Достоинством рекурсии является компактная запись, а недостатками - расход времени и памяти на повторные вызовы функции и передачу ей копий параметров, и, главное, опасность переполнения стека.
9.9 Задачи для решения на тему «функции»
Вариант 1. Даны действительные числа s, t. Получить f(t, -2s, 1.17)+f(2.2, t, s-t), где . Вариант 2. Даны действительные числа s, t. Получить g(1.2, s)+g(t, s)-g(2s-1, st), где . Вариант 3. Дано действительное число y. Получить , где Вариант 4. Даны действительные числа a, b, c. Получить . Вариант 5. Даны действительные числа a, b. Получить u= min(a, b), v = min(ab,a+b), d = min(u+v2, 3.1416). Вариант 6. Даны натуральные числа n, m, целые числа a1,..., an, b1,..., bm, c1,..., c30. Получить
Вариант 7. Даны натуральные числа k, l, m, действительные числа x1,..., xk, y1,..., yl, z1,..., zm. Получить
Вариант 8. Даны действительные числа s, t. Получить h(s, t)+max(h2(s-t, st), h4(s-t, s+t))+h(1, 1), где Вариант 9. Даны действительные числа a0,..., a6. Получить для x=1, 3, 4 значения p(x+1)-p(x), где p(y)=a6 y6 +a5y5 +...+a0. Вариант 10. Даны действительные числа s, t, a0,...., a12. Получить p(1)-p(t)+p(s-t)-p(1), где p(x)=a12x12 +a11x11 +...+a0. Вариант 11. Даны действительные числа a1,..., an, b1,..., bm. В последовательности a1,..., an, и в последовательности b1,..., bm, все члены, следующие за членом с наибольшим значением (за первым по порядку, если их несколько), заменить на 0.5. Вариант 12. Даны целые числа a1,..., an, b1,..., bm, k. Если в последовательности a1,..., an, нет ни одного члена со значением k, то первый по порядку член этой последовательности, не меньший всех остальных членов, заменить на значение k. По такому же правилу преобразовать последовательность b1,..., bm применительно к значению 10. Вариант 13. Даны натуральные числа n, m найти НОД(n, m). Использовать программу, включающую РЕКУРСИВНУЮ процедуру вычисления НОД, основанную на соотношении НОД(n, m) = НОД(m, r), где r - остаток от деления n на m. Вариант 14. Написать процедуру сортировки строк матрицы в порядке неубывания суммы абсолютных величин элементов. Оформить как функцию вычисление суммы абсолютных величин элементов строки и как процедуру – перестановку строк. Вариант 15. Написать процедуру сортировки строк матрицы в порядке неубывания максимального по модулю элемента. Оформить как функцию вычисление максимального по модулю элемента строки и как процедуру – перестановку строк. Вариант 16. Написать процедуру сортировки строк матрицы из натуральных чисел в порядке неубывания суммы простых элементов строки. Оформить как функции вычисление суммы простых элементов строки и определение простоты числа и как процедуру – перестановку строк. Вариант 17. Написать процедуру поиска седловой точки матрицы. Элемент матрицы называется седловой точкой, если он одновременно максимален в своей строке и минимален в столбце. Оформить как функции поиск максимума в строке и проверку, будет ли заданный элемент минимальным в столбце. Вариант 18. Написать процедуру, вычеркивающую из матрицы строку и столбец, на пересечении которых располагается максимальный элемент матрицы. Поиск максимального элемента, а также вычеркивания строки и столбца оформить как процедуры.
10 Директивы препроцессора
Препроцессором называется первая фаза компилятора. Инструкции препроцессора называются директивами. Они должны начинаться с символа #, перед которым в строке могут находиться только пробельные символы.
10.1 Директива #include
Директива #include <имя_файла> вставляет содержимое указанного файла в ту точку исходного файла, где она записана. Включаемый файл также может содержать директивы #include. Поиск файла, если не указан полный путь, ведется в стандартных каталогах включаемых файлов. Вместо угловых скобок могут использоваться кавычки
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|