
- •Максимов м.Н.
- •3. Скалярные типы и выражения 51
- •5. Адреса, указатели, массивы, память 95
- •6. Функции, указатели, ссылки 132
- •7 Структуры, объединения и классы 170
- •Введение
- •Модуль 1
- •1.2. Этапы подготовки исполняемой программы
- •1.3. Системы счисления
- •Представление чисел от 0 до 16 в разных системах счисления
- •2.1. Общие сведения о программах, лексемах и алфавите
- •2.2. Идентификаторы и служебные слова
- •2.3. Типы данных
- •2.4. Константы
- •Типы, выбираемые компилятором по умолчанию для целых констант
- •Данные вещественного типа
- •2.5. Операции
- •2.6. Разделители
- •3. Скалярные типы и выражения
- •3.1. Определение и описание переменных
- •3.2. Явное и неявное преобразование типа
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Список литературы
- •Модуль 2
- •4.1. Последовательно выполняемые операторы
- •4.2. Операторы выбора
- •If( выражение) оператор_1 else оператор_2
- •If( выражение) оператор_1
- •4.3. Операторы цикла
- •4.4. Операторы передачи управления
- •If (условие) break;
- •4.5. Примеры численного моделирования цепей первого порядка
- •5. Адреса, указатели, массивы, память
- •5.1. Указатели и адреса объектов
- •5.2. Адресная арифметика, типы указателей и операции над ними
- •5.3. Свойства указателя типа void*
- •5.4. Свойства объекта cout
- •5.5. Массивы и указатели
- •5.6. Многомерные массивы, массивы указателей, динамические массивы
- •Проектные задания к модулю
- •Тесты рубежного контроля
- •6.2. Функции с переменным количеством параметров
- •6.3. Рекурсивные функции
- •6.4. Подставляемые (инлайн-) функции
- •6.5. Функции и массивы
- •6.6. Указатели на функции
- •Void f3(float) (...) // Определение функции
- •Int* f4(char *){...} // Определение функции
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Модуль 4
- •7 Структуры, объединения и классы
- •7.1 Структура как тип и совокупность данных
- •7.3 Объединения разнотипных данных
- •7.4 Деревья
- •7.5 Битовые поля структур и объединений
- •7.6 Компонентные функции структурированных объектов
- •7.7 Расширение действия (перегрузка) стандартных операций
- •7.8 Доступ к компонентам структурированного объекта
- •7.9 Классы и шаблоны
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Список литературы
- •Приложение 1
- •Приложение 2 Стандартная библиотека функций языка Си
2.6. Разделители
Разделители, или знаки пунктуации, входят в число лексем языка:
[] () {} , ; : … * = # &
Как видно одни и те же лексемы языка в зависимости от места их использования в программе могут выступать в качестве знака операции или в качестве разделителя.
Квадратные скобки [] являются разделителями (а не операцией) при определении размерности определяемого массива
int A[] = { 1,2,3,4,5}; //квадратные скобки выступают в качестве разделителя
int s[2][5]; //квадратные скобки выступают в качестве разделителя
Круглые скобки () :
входят как обязательный элемент в операторы цикла и условия;
входят как обязательный элемент в определение, описание (в прототип) и вызов любой функции, где выделяют соответственно список формальных параметров, список спецификаций параметров и список фактических параметров;
обязательны в определении указателя на функцию;
группируют выражение, изменяя естественную последовательность выполнения операций;
являются необходимым элементом при выполнении операции преобразования типа;
применение круглых скобок настоятельно рекомендуется в макроопределениях.
Фигурные скобки {} обозначают начало и конец составного оператора или блока. Они используются также для выделения списка компонентов в определениях типов структур, объединений, классов и инициализации массивов и структур при их определении.
Запятая , разделяет элементы списков, используется в описаниях и определениях объектов одного типа, участвует в качестве разделителя в заголовке оператора цикла for.
В качестве ещё одной области применения запятой как разделителя нужно отметить описание производного класса, в котором используется список базовых классов и список вызываемых конструкторов. Кроме того, в списке однотипных компонентов класса они отделяются друг от друга запятыми.
Точка запятой ; завершает каждый оператор, каждое определение (кроме определения функции) и каждое описание. Любое допустимое выражение, за которым следует ‘;’, воспринимается как оператор. Это справедливо и для пустого выражения, т.е. отдельный символ «точка с запятой» считается пустым оператором.
Двоеточие : служит для отделения метки от помечаемого ею оператора:
метка: оператор;
Метка – это идентификатор. Таким образом, допустимы, например, такие помеченные операторы:
XYZ: a = b+c –d; vv: x*=10; // XYZ и vv метки
Второе применение двоеточия – описание производного класса, где имя класса отделяется от списка базовых классов двоеточием.
Многоточие ‘…’ это три точки без пробелов между ними. Оно используется для обозначения переменного числа параметров у функции при её описании и определении.
Звездочка * не является операцией при описании и определении указателей, например:
int * point; // звездочка не выполняет роль операции при описании указателя
char ** refer; // аналогично
Знак ‘=’ является разделителем при определении объекта, отделяя определение объекта от списка его инициализации:
int D = 62; //
В списке формальных параметров функции знак ‘=’ указывает на выбираемое по умолчанию значение аргумента (фактического параметра).
Символ # используется для обозначения директив препроцессора. Если этот символ является первым отличным от пробела символом в строке программы, то строка воспринимается как директива препроцессора.
Символ & играет роль разделителя при определении переменных типа ссылки:
int Bl;
int &a = B1;
Отметив использование символа & в качестве разделителя при описании ссылок, отложим подробное рассмотрение ссылок.
2.7. Первоначальные сведения о функциях языка Си++
До этого момента все приведённые выше программы содержали только одну функцию – main(). Как уже говорилось любая программа, написанная на языке Си++, должна обязательно содержать функцию main(), и эта функция должна быть в программе единственной, так как именно с выполнения операторов этой функции и начинается выполнение программы. Синтаксис определения любой функции, в том числе и функции main(), задаётся следующими правилами:
тип_функции имя_функции (список формальных параметров)
{ тело функции}
где тип_функции – тип возвращаемого в точку вызова функцией значения; имя_функции – в общем случае определяется пользователем; список формальных параметров список типов данных, передаваемых функции; тело функции – последовательность, заключённых в фигурные скобки, операторов, реализующих алгоритм работы программы на языке Си++. Совокупность
тип_функции имя_функции (список формальных параметров)
называют ещё сигнатурой функции.
Приведем пример определения функции, которая суммирует два числа:
int sum(int z, int g) {
int s = z+g;
return s;
}
где int – тип функции; sum – имя функции; (int z, int g) – список формальных параметров (в данном случае z первый формальный параметр типа int, g – второй формальный параметр типа int); в фигурные скобки {} заключено тело функции (состоящее в данном случае из двух операторов); int sum(int z, int g) – сигнатура функции sum().
Само по себе наличие определения функции в программе ничего не даёт, для того чтобы функция начала работать (т.е. начали выполняться операторы тела функции), должен обязательно присутствовать вызов функции в программе. Под вызовом функции понимают выражение
имя_функции( список_фактических_параметров)
где имя_функции – имя вызываемой функции; список_фактических_параметров – список констант, переменных или выражений записанных через запятую. Если фактический параметр представлен выражением, то сначала вычисляется значение выражения, и только потом осуществляется вызов функции. Для примера несколько вызовов функции sum() можно записать следующим образом:
sum(2, 1); sum(3+5,3-7); sum(f+2, d);
в первом вызове первый фактический параметр равен 2, второй – 1; во втором вызове два фактических параметра представлены выражениями, только после нахождения значений которых осуществляется вызов функции; в третьем вызове первый фактический параметр является выражением, в котором используется переменная f, значение которой должно было быть определено выше в теле программы до вызова функции, вторым фактическим параметром является значение переменной d.
Что же происходит при вызове функции? При вызове функции формальным параметрам присваивается значение фактических параметров в строгом соответствии с их положением в списке. Другими словами первому формальному параметру присваивается значение первого фактического параметра, второму формальному параметру – значение второго фактического параметра и т.д. После этого начинают выполняться операторы тела вызванной функции. Таким образом, через аппарат формальных и фактических параметров данные передаются в вызванную функцию. В приведенных выше примерах в первом вызове формальные параметры получают следующие значения z= 2, g = 1; во втором вызове z= 8, g = -5; в третьем вызове (если предположить, например, что f = 1, d = 6) z= 3, g = 6.
Функция имеет не только механизм получения данных из вызывающей программы, но может и вернуть данные в точку вызова. Для этого используется оператор return в теле функции. В общем виде его можно записать так:
return выражение;
С помощью этого оператора в точку вызова возвращается значение выражения, тип которого соответствует типу функции. Другими словами результатом выполнения операции вызова функции является появление на месте вызова неименованного объекта с типом, совпадающим с типом функции, и значением равным значению выражения в операторе return.
В приведенных выше примерах вызова функции sum() в первом вызове на месте sum(2, 1) после выполнения операторов тела функции появится неименованный объект типа int с значением 3; во втором вызове на месте sum(3+5, 3-7) появится неименованный объект типа int с значением 4; в третьем вызове на месте sum(f+2, d) появится неименованный объект типа int с значением 9 (если предположить, например, что f = 1, d = 6). Именно потому, что функция sum() имеет тип int, т.е. возвращает один из арифметических типов данных, можно выполнять вложенные вызовы или использовать эту функцию в составе выражения. Например, выражение:
4+sum(2,1)/3+ sum(sum(1,2), 4);
является правильным и равно 12. Причем приоритет операции вызова функции в соответствии с таблицей рангов операций самый высокий, поэтому в выражении сначала выполнятся вызовы функций (в соответствии с направлением ассоциативности первым выполнится вызов sum(2,1), вторым sum(1,2) так как вызов может быть осуществлен только после вычисления значения фактических параметров и, наконец, sum(3, 4)) и только потом будут выполняться все остальные операции.
Приведем для примера полный текст программы на языке Си++, которая вызывает функцию sum() , и в которой определим ещё одну функцию, возвращающую максимум из двух чисел.
// Программа 2.1
#include "stdafx.h"
#include <iostream>
double max(double c, double k); /*1. Описание функции max( )*/
int sum(int z, int g) { /*2. Определение функции sum()*/
int s = z+g; //3.
return s; //4.
} //5.
//-----------------------------------------
void main(){ /*6. Определение функции main( )*/
int f = 1, d = 6; //7.
int z = -10, s; //8.
s = sum(3, 4); //9.
std::cout<<"\n s = "<<s; //10.
s = sum(f - d, s) - max(f+s,d); //11.
std::cout<<"\n s = "<<s; //12.
getchar();
} //13.
//-------------------------------------------
double max(double c, double k){ /*14. Определение функции max( )*/
return c>k? c: k; //15.
}
Итак, рассмотрим последовательность, в которой будут выполняться строки программы. Первыми будут выполнены строки 6-8, так как выполнение программы всегда начинается с функции main(). В 9-й строке программы присутствует вызов функции sum(), следовательно, будут выполнены строки 2-5, т.е. операторы тела функции sum(), и затем будет закончено выполнение 9-й строки присвоение переменной s значения 7. Далее в 10-й строке значение переменной s будет выведено на экран монитора. При выполнении 11-ой строки вызываются функции sum() и max(), т.е. выполняются строки 2-5 и 14-16, затем переменной s присваивается новое значение. В 12-й строке программы новое значение s выводится на экран монитора. При выполнении 13-ой строки программа завершает своё выполнение.
Таким образом, строки 2-5 выполнялись два раза (так как два раза вызывалась функция sum() ), но каждый раз формальные параметры получали разные значения и функция sum() естественно возвращала разные значения. Поэтому можно сказать, что в виде функции удобно оформлять фрагменты повторяющегося кода программы, а с помощью аппарата фактических и формальных параметров обеспечивать разные начальные условия.
Единственная строка программы, которая не выполнялась ни разу, это 1-я строка. Это строка является описанием (прототипом) функции max(). Описание необходимо в программе в том случае, если вызов функции по тексту программы предшествует её определению. В нашем случае вызов функции max() находится в 11-й строке, а определение начинается с 14-й. Синтаксис описания функции приведен ниже:
тип_функции имя_функции(список формальных параметров);
Как видно описание – это сигнатура функции с точкой запятой на конце. С помощью описания функции программист сообщает компилятору о том, что где-то далее в тексте программы будет вызвана функция с таким-то именем, с таким-то количеством и типом формальных параметров и с таким-то типом возвращаемого значения. Если же определение предшествует вызову, то компилятор получает эту информацию из него, что и продемонстрировано на примере функции sum(). Поэтому описание для функции sum() можно опустить.
Кроме функций определенных программистом есть огромное количество библиотечных функций, которыми постоянно приходится пользоваться. Например, необходимо в программе вычислить sin(x), для этого конечно можно написать свою собственную функцию, но проще воспользоваться готовой библиотечной. Для этого необходимо в справочнике или в помощи среды программирования найти описание функции и имя заголовочного файла, в котором это описание находится. Для функции sin() описание выглядит так double sin(double x) и находится оно в заголовочном файле math.h. В файле помощи к этой функции также сказано, что аргумент задаётся в радианах. Этой информации достаточно, чтобы можно было написать программу, в которой используется вызов библиотечной функции:
//Программа 2.2
#include "stdafx.h"
#include <iostream>
#include <math.h>
void main(){
double z;//Определение переменной z.
std::cout<<"\n Input x for sin(x), x = ";
std::cin>>z; //Ввод с клавиатуры значения в переменную z.
std::cout<<"\n Output sin(x) = "<<3*sin(z); /*Вызов библиотечной функции sin()*/
getchar();
}
Из описания функции sin() видно, что она имеет один формальный параметр типа double и возвращает в точку вызова значение функции в формате типа double. Описание функции находится в файле math.h, поэтому его необходимо с помощью директивы include включить в текст программы. Определение функции находится в виде двоичного кода в одном из файлов с расширением lib, оно будет включено компоновщиком в текст программы во время компоновки программы в среде программирования.
Таким образом, в этом параграфе были изложены основные понятия: определение, описание и вызов, связанные с функциями языка Си++. Безусловно, это не всё. Несколько позднее по мере освоения материала будут рассмотрены функции с переменным количеством параметров, рекурсивные функции, встроенные функции и т.д.