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

29.04.2019 Сотовые операторы

Инициализация

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

= <инициализатор >

Можно инициализировать переменные любого типа. Функции не инициализируются. Объявления, которые используют спецификацию класса памяти extern , не могут содержать инициализатор.

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

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

Инициализация переменных класса памяти auto и register выполняется каждый раз при входе в блок (за исключением входа в блок по оператору goto ), в котором они объявлены. Если инициализатор опущен в объявлении переменной класса памяти auto или register , то ее начальное значение не определено. Инициализация переменных составных типов (массив, структура, объединение), имеющих класс памяти auto , запрещена в СП MSC, но допускается в СП ТС даже для переменных, объявленных с модификатором const . Переменные составного типа, имеющие класс памяти static , могут быть инициализированы на внутреннем уровне.

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

Из книги C++ автора Хилл Мюррей

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

Из книги Справочное руководство по C++ автора Страустрап Бьярн

6.6 Присваивание и Инициализация Рассмотрим очень простой класс строк string:struct string (* char* p; int size; // размер вектора, на который указывает pstring(int sz) (* p = new char; *) ~string() (* delete p; *) *);Строка – это структура данных, состоящая из вектора сиволов и длины этого вектора. Вектор

Из книги Идиомы и стили С++ автора Makhmutov Albert

Из книги Системное программирование в среде Windows автора Харт Джонсон М

R.12.6 Инициализация Объект класса без конструкторов, без частных или защищенных членов, без виртуальных функций и без базовых классов можно инициализировать с помощью списка инициализаторов (§R.8.4.1). Объект класса с конструктором должен инициализироваться или иметь

Из книги Инфраструктуры открытых ключей автора Полянская Ольга Юрьевна

Из книги Язык программирования Си для персонального компьютера автора Бочков C. О.

Шаг 12 - Двухэтапная инициализация. Когда мы создаем нестековый экземпляр, то пишем такой код:CClass* cc = new CClass();Попробуем поразбираться. new - это глобальный оператор с определением:void* operator new (size_t bytes);Он получает от компилятора количество байт, необходимое для хранения объекта,

Из книги QNX/UNIX [Анатомия параллелизма] автора Цилюрик Олег Иванович

Инициализация Winsock Winsock API поддерживается библиотекой DLL (WS2_32.DLL), для получения доступа к которой следует подключить к программе библиотеку WS_232.LIB. Эту DLL следует инициализировать с помощью нестандартной, специфической для Winsock функции WSAStartup, которая должна быть первой из

Из книги Язык Си - руководство для начинающих автора Прата Стивен

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

Из книги Программирование для Linux. Профессиональный подход автора Митчелл Марк

Инициализация Переменной в объявлении может быть присвоено начальное значение посредством инициализатора. Записи инициализатора в объявлении предшествует знак равенства=<инициализатор>Можно инициализировать переменные любого типа. Функции не инициализируются.

Из книги C++ для начинающих автора Липпман Стенли

Инициализация параметров int pthread_mutexattr_init(const pthread_mutexattr_t* attr);Функция инициализирует структуру атрибутов мьютекса, на которую указывает параметр attr. Тип данных pthread_mutexattr_t определен в файле (производный от типа sync_attr_t, который в свою очередь определен в файле

Из книги автора

Инициализация мьютекса int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr);Структура данных pthread_mutex_t определена в файле (производный тип от типа sync_t, который в свою очередь определен в файле ) и имеет следующий вид:struct _sync_t { /* Счетчик для рекурсивного

Из книги автора

Инициализация структуры Мы видели, как инициализируются переменные и массивы: int count = 0;static int fibo={0, 1, 1, 2, 3, 5, 8};Можно ли инициализировать и структурную переменную? Да, если структурная переменная будет внешней или статической. Здесь следует иметь в виду, что

Из книги автора

5.2.2. Инициализация семафоров Выделение и инициализация семафора - две разные операции. Чтобы проинициализировать семафор, вызовите функцию semctl(), задав второй аргумент равным нулю, а третий аргумент - равным константе SETALL. Четвертый аргумент должен иметь тип union semun, поле

Из книги автора

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

Из книги автора

14.1. Инициализация класса Рассмотрим следующее определение класса:class Data {public:int ival;char *ptr;};Чтобы безопасно пользоваться объектом класса, необходимо правильно инициализировать его члены. Однако смысл этого действия для разных классов различен. Например, может ли ival

Из книги автора

14.6. Почленная инициализация A Инициализация одного объекта класса другим объектом того же класса, как, например:Account oldAcct(" Anna Livia Plurabelle");Account newAcct(oldAcct);называется почленной инициализацией по умолчанию. По умолчанию - потому, что она производится автоматически, независимо

Страница 74 из 82

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

Список параметров в определении и прототипе функции, кроме согласования типов параметров, имеет ещё одно назначение.

Объявление параметра может содержать инициализатор, то есть выражение, которое должно обеспечить параметру присвоение начального значения. Инициализатор параметра не является константным выражением. Начальная инициализация параметров происходит не на стадии компиляции (как, например, выделение памяти под массивы), а непосредственно в ходе выполнения программы.

Следующие строки демонстрируют пример объявления функции с инициализацией параметров. Для инициализации параметра ww используется функция XX.

Int BigVal;
int XX(int);
int ZZ(int tt, int ww = XX(BigVal));

Второй параметр можно проинициализировать и таким способом, вовсе не указывая его имени. Синтаксис объявления позволяет сделать и такое!

Int ZZ(int tt, int = XX(BigVal));

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

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

Кроме того, в C++ действует ещё одно ограничение, связанное с порядком инициализации параметров в пределах области видимости. Инициализация проводится непременно с самого последнего (самого правого) параметра в списке объявлений параметров. Инициализация параметров не допускает пропусков: инициализированные параметры не могут чередоваться с параметрами неинициализированными.

Int MyF1 (int par1, int par2, int par3, int par4 = 10);
int MyF1 (int par1, int par2 = 20, int par3 = 20, int par4);
int MyF1 (int par1 = 100, int, int, int);

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

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

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

Объявление параметра может содержать инициализатор, то есть выражение, которое должно обеспечить параметру присвоение начального значения. Инициализатор параметра не является константным выражением. Начальная инициализация параметров происходит не на стадии компиляции (как, например, выделение памяти под массивы), а непосредственно в ходе выполнения программы.

Следующие строки демонстрируют пример объявления функции с инициализацией параметров. Для инициализации параметра ww используется функция XX.

int ZZ(int tt, int ww = XX(BigVal));

Второй параметр можно проинициализировать и таким способом, вовсе не указывая его имени. Синтаксис объявления позволяет сделать и такое!

int ZZ(int tt, int = XX(BigVal));

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

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

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

int MyF1 (int par1, int par2, int par3, int par4 = 10);

………………………………….

int MyF1 (int par1, int par2 = 20, int par3 = 20, int par4);

………………………………….

int MyF1 (int par1 = 100, int, int, int);

#include

int f(int, int=4);

int main(int argc, char* argv)

printf("%d\n", f(2)); //8

printf("%d\n", f(2,3)); //6

int f(int a, int b)

Функции с переменным числом параметров

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

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

Примерами функций с переменным числом параметров являются функции из библиотеки функций языка СИ, осуществляющие операции ввода-вывода информации (printf, scanf и т.п.). Функция printf() в библиотеке объявлена следующим образом:

int printf(const char* ...);

Это гарантирует, что при любом вызове printf() ей будет передан первый аргумент типа const char*. Содержание такой строки, называемой форматной, определяет, необходимы ли дополнительные аргументы при вызове. При наличии в строке формата метасимволов, начинающихся с символа %, функция ждет присутствия этих аргументов.

Программист может разрабатывать свои функции с переменным числом параметров. Для обеспечения удобного способа доступа к аргументам функции с переменным числом параметров имеются три макроопределения (макросы) va_start, va_arg, va_end, находящиеся в заголовочном файле stdarg.h. Эти макросы указывают на то, что функция, разработанная пользователем, имеет некоторое число обязательных аргументов, за которыми следует переменное число необязательных аргументов. Обязательные аргументы доступны через свои имена как при вызове обычной функции. Для извлечения необязательных аргументов используются макросы va_start, va_arg, va_end в следующем порядке.

Макрос va_start предназначен для установки аргумента arg_ptr на начало списка необязательных параметров и имеет вид функции с двумя параметрами:

void va_start(arg_ptr, prav_param);

Параметр prav_param должен быть последним обязательным параметром вызываемой функции, а указатель arg_prt должен быть объявлен с предопределением в списке переменных типа va_list в виде:

va_list arg_ptr;

Макрос va_start должен быть использован до первого использования макроса va_arg.

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

type_arg va_arg(arg_ptr,type);

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

Макрос va_end используется по окончании обработки всех параметров функции и устанавливает указатель списка необязательных параметров на ноль (NULL).

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

#include

#include

int sred_znach(int,...);

n=sred_znach(2,3,4,-1); /* вызов с четырьмя параметрами */

printf("n=%d\n",n);

n=sred_znach(5,6,7,8,9,-1); /* вызов с шестью параметрами */

printf("n=%d\n",n);

int sred_znach(int x,...)

int i=0, j=0, sum=0;

va_start(uk_arg,x); /* установка указателя uk_arg на */

/* первый необязятельный параметр */

if (x!=-1) sum=x; /* проверка на пустоту списка */

else return (0);

while ((i=va_arg(uk_arg,int))!=-1)

/* выборка очередного */

{ /* параметра и проверка */

sum+=i; /* на конец списка */

va_end(uk_arg); /* закрытие списка параметров */

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

#include

#include

int sred_znach2(int,...);

n=sred_znach2(3,2,3,4);

printf("n=%d\n",n);

int sred_znach2(int n,...)

int *pPointer = &n;

for (int i=n ; i; i--) Sum += *(++pPointer);


Список параметров в определении и прототипе функции, кроме согласования типов параметров, имеет ещё одно назначение.

Объявление параметра может содержать инициализатор, то есть выражение, которое должно обеспечить параметру присвоение начального значения. Инициализатор параметра не является константным выражением. Начальная инициализация параметров происходит не на стадии компиляции (как, например, выделение памяти под массивы), а непосредственно в ходе выполнения программы.

Следующие строки демонстрируют пример объявления функции с инициализацией параметров. Для инициализации параметра ww используется функция XX.

Int BigVal;
int XX(int);
int ZZ(int tt, int ww = XX(BigVal));

Второй параметр можно проинициализировать и таким способом, вовсе не указывая его имени. Синтаксис объявления позволяет сделать и такое!

Int ZZ(int tt, int = XX(BigVal));

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

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

Кроме того, в C++ действует ещё одно ограничение, связанное с порядком инициализации параметров в пределах области видимости. Инициализация проводится непременно с самого последнего (самого правого) параметра в списке объявлений параметров. Инициализация параметров не допускает пропусков: инициализированные параметры не могут чередоваться с параметрами неинициализированными.

Int MyF1 (int par1, int par2, int par3, int par4 = 10);
int MyF1 (int par1, int par2 = 20, int par3 = 20, int par4);
int MyF1 (int par1 = 100, int, int, int);

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

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

33передача параметров функции main c++

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

int main(int argc, char* argv) // параметры функции main()

Эта строка - заголовок главной функции main(), в скобочках объявлены параметры argс и argv. Так вот, если программу запускать через командную строку, то существует возможность передать какую-либо информацию этой программе, для этого и существуют параметры argcи argv. Параметр argc имеет тип данных int, и содержит количество параметров, передаваемых в функцию main. Причем argc всегда не меньше 1, даже когда мы не передаем никакой информации, так как первым параметром считается имя функции. Параметр argv это массив указателей на строки. Через командную строку можно передать только данные строкового типа. Указатели и строки - это две большие темы, под которые созданы отдельные разделы. Так вот именно через параметр argv и передается какая-либо информация. Разработаем программу, которую будем запускать через командную строку Windows, и передавать ей некоторую информацию.

Время жизни и область видимости программных объектов Классы памяти.

Время жизни переменной (глобальной или локальной) определяется по следующим правилам.

1. Переменная, объявленная глобально (т.е. вне всех блоков), существует на протяжении всего времени выполнения программы.

2. Локальные переменные (т.е. объявленные внутри блока) с классом памяти register или auto, имеют время жизни только на период выполнения того блока, в котором они объявлены. Если локальная переменная объявлена с классом памяти static или extern, то она имеет время жизни на период выполнения всей программы.

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

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

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

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

4. Функции с классом памяти static видимы только в исходном файле, в котором они определены. Всякие другие функции видимы во всей программе.

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

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

Классы памяти в с++.

2) register

3) static

4) extern

auto

int i; -> auto int i;

Классы памяти в с++.

Существует 4 спецификатора класса памяти:

2) register

3) static

4) extern

Если класс памяти не указан, он определяется по умолчанию из контекста объявления.

Объекты классов auto и register имеют локальное время жизни.

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

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

int i; -> auto int i;

Переменная с классом памяти auto имеет локальное время жизни. Она видима только в том блоке, в котором объявлена. Память для этой переменной выделяется при входе в блок и освобождается при выходе. При повторном входе в блок память для этой переменной может быть распределена в другом месте.

register

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

Когда компилятор встречает спецификатор register, а свободного регистра не имеется, то для переменной распределяется память auto.

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

static

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

extern

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

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

35Инициализация глобальных и локальных переменных

Существуют локальные и глобальные переменные. Так вот, переменные, объявленные внутри функции, называются локальными. Локальные переменные имеют свои области видимости, этими областями являются функции, в которых объявлены переменные. Таким образом, в разных функциях можно использовать переменные с одинаковыми именами, что в свою очередь очень удобно. Разделение переменных на глобальные и локальные соответствует одному из главных правил программирования, а именно – принципу наименьших привилегий. То есть, переменные, объявленные внутри одной функции, должны быть доступны только для этой функции и ни чему другому, в конце концов, они создавались именно для этой функции. Глобальные переменные объявляются вне тела какой-либо функции, и поэтому область видимости таких переменных распространяется на всю программу. Обычно глобальные переменные объявляются перед главной функцией, но можно объявлять и после функции main(), но тогда данная переменная не будет доступна в функции main().

Разработаем программу, в которой будут объявлены две переменные, локальная и глобальная, с одинаковым именем.

37Динамические массивы. Особенности выделения и освобождения памяти для многомерных массивов.

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

Динамические массивы ограниченного размера и их объём

Простейший динамический массив - это массив с фиксированным размером длинны, который делится на две части: в первой хранятся элементы динамического массива, а вторая часть является резервной или неиспользуемой. Вы можете добавлять или удалять элементы в конец динамического массива в определённое время используя резервное пространство массива, до тех пор, пока оно не исчерпается. Число элементов, используемых в динамических массивах это логический размер или просто размер, в то время как размер основного массива называется объёмом динамического массива или физическим размером, который является максимально возможным размером, без переопределения размера данных.

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

38Директивы препроцессора. Макроопределения.

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

#define, #elif, #else, #endif, #if, #ifdef, #ifndef, #include, #undef.

Директива #define используется для задания констант, ключевых слов, операторов и выражений, используемых в программе. Общий синтаксис данной директивы имеет следующий вид:

#define <идентификатор> <текст>

#define <идентификатор> (<список параметров>) <текст>

Директива #undef отменяет определение, введенное ранее директивой #define. Предположим, что на каком-либо участке программы нужно отменить определение константы FOUR. Это достигается следующей командой:

Отличие директивы #if от директив #ifdef и #ifndef заключается в возможности проверки более разнообразных условий, а не только существует или нет какие-либо константы. Например, с помощью директивы #if можно проводить такую проверку:

Используемая в приведенных примерах директива #include позволяет добавлять в программу ранее написанные программы и сохраненные в виде файлов. Например, строка

#include < stdio.h >

Макроопределение - директива #define

Макроопределение, или макроподстановка - пожалуй, самая мощная директива препроцессора. И вместе с тем самая коварная. Давайте посмотрим, как ей пользоваться. Взгляните на такой кусочек программы:

int x;
int y;
int i;

for (i=0; i<100; i++) {
x[i]=fx(i);
y[i]=fy(i);
}

Этот код считает значения функций fx и fy в 100 точках - например, для того, чтобы потом построить их графики. Если нам понадобится увеличить количество точек, придется в трех местах заменять 100 на другое число - и писанины много, и ошибиться легко.

45Шаблоны функций.

Шабло́ны (англ. template ) - средство языка C++, предназначенное для кодирования обобщённых алгоритмов, без привязки к некоторым параметрам (например, типам данных, размерам буферов, значениям по умолчанию).

В C++ возможно создание шаблонов функций и классов.

Шаблоны позволяют создавать параметризованные классы и функции. Параметром может быть любой тип или значение одного из допустимых типов (целое число, enum, указатель на любой объект с глобально доступным именем, ссылка). Например, нам нужен какой-то класс:

Class SomeClass{ int SomeValue; int SomeArray; ...};

Для одной конкретной цели мы можем использовать этот класс. Но, вдруг, цель немного изменилась, и нужен еще один класс. Теперь нужно 30 элементов массива SomeArray и вещественный тип SomeValue элементов SomeArray. Тогда мы можем абстрагироваться от конкретных типов и использовать шаблоны с параметрами. Синтаксис: в начале перед объявлением класса напишем слово template и укажем параметры в угловых скобках. В нашем примере:

Template < int ArrayLength, typename SomeValueType > class SomeClass{ SomeValueType SomeValue; SomeValueType SomeArray[ ArrayLength ]; ...};

Тогда для первой модели пишем:

SomeClass < 20, int > SomeVariable;

для второй:

SomeClass < 30, double > SomeVariable2;

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

46 Шаблоны классов.

Любой шаблон начинается со слова template, будь то шаблон функции или шаблон класса. После ключевого словаtemplate идут угловые скобки - < >, в которых перечисляется список параметров шаблона. Каждому параметру должно предшествовать зарезервированное слово class или typename. Отсутствие этих ключевых слов будет расцениваться компилятором как синтаксическая ошибка. Некоторые примеры объявления шаблонов.

Ключевое слово typename говорит о том, что в шаблоне будет использоваться встроенный тип данных, такой как: int, double,float, char и т. д. А ключевое слово class сообщает компилятору, что в шаблоне функции в качестве параметра будут использоваться пользовательские типы данных, то есть классы. Но не в коем случае не путайте параметр шаблона и шаблон класса. Если нам надо создать шаблон класса, с одним параметром типа int и char, шаблон класса будет выглядеть так.

Как видите шаблон класса Stack объявлен и определен в файле с main-функцией. Конечно же такой способ утилизации шаблонов никуда не годится, но для примера сойдет. В строках 7 - 20 объявлен интерфейс шаблона класса. Объявление класса выполняется привычным для нас образом, а перед классом находится объявление шаблона, в строке 7. При объявлении шаблона класса, всегда используйте такой синтаксис.

Строки 47 - 100 содержат элемент-функции шаблона класса Stack, причем перед каждой функцией необходимо объявлять шаблон, точно такой же, как и перед классом - template . То есть получается, элемент-функции шаблона класса, объявляются точно также, как и обычные шаблоны функций. Если бы мы описали реализацию методов внутри класса, то заголовок шаблона - template для каждой функции прописывать не надо.

Чтобы привязать каждую элемент-функцию к шаблону класса, как обычно используем бинарную операцию разрешения области действия - :: с именем шаблона класса - Stack. Что мы и сделали в строках 49, 58, 68, 83, 96.

Обратите внимание на объявление объекта myStack шаблона класса Stack в функции main, строка 24. В угловых скобочка необходимо явно указывать используемый тип данных, в шаблонах функций этого делать не нужно было. Далее в mainзапускаются некоторые функции, которые демонстрируют работу шаблона класса Stack. Результат работы программы смотрим ниже.

47Библиотека STL. Другие библиотеки контейнерных классов.

Классификация указателей на функцию

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

Указатели на функцию

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

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

В языке Си существуют две разновидности указателей на функцию:

· указатель – выражение,

· указатель переменная.

38.9.3. Указатель – выражение

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

Пусть имеется прототип функции для вычисления квадрата числа типа double. Тогда выражение &sqr - это выражение – указатель. Здесь & - унарный оператор взятия адреса, который и предписывает компилятору вычислить адрес йункции.

38.9.4. Указатель – переменная

Указатель - переменная это переменная, предназначенная для хранения адреса функции. Рассмотрим формат определения такой переменной.

(* <α>) (<γ>)

· * - оператор разыменования,

α – идентификатор, объявленного указателя,

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

· γ – список типов параметров функции, на которую может быть установлен указатель.

Приведем пример объявления указателя – переменной.

double (*pf)(double);

Это объявление переменной. Имя переменной pf. значением этой переменной может служить адрес любой функции, которая принимает один параметр типа double и которая возращает значения типа double.

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

double sqr(double x)
{

double cube(double x)
{

return x * x * x;

Вернемся к объявлению указателя – переменной pf. Рассмотрим вопрос о назначении используемых в нем скобок. Здесь их две пары.



double (*pf)(double);

Вторая пара скобок – это оператор функция. Возникает вопрос: «Какую роль играет первая пара скобок?». С целью разобраться в этом вопросе на время удалим эту пару скобок. Тогда рассматриваемое выражение примет следующий вид.

double *pf(double);

Полученное выражение является объявлением функции, которая принимает один параметр типа double и возращает указатель типа double*. Для того чтобы убедиться в этом вспомним, что в синтаксис объявлений в языке Си заложен синтаксис выражений. Для определения назначения имени pf в рассматриваемом выражении следует рассмотреть подвыражение *pf(double). Здесь к имени pf применяются два оператора: * и () . Трактовка имени pf зависит от приоритетов этих операторов. Если обратиться к таблице приоритетов операторов, то можно убедиться в том, что оператор функция () имеет более высокий приоритет по сравнению с оператором разыменования *. Отсюда следует, что оператор () сильнее связывает имя pf по сравнению с оператором *. Итак, в новой редакции pf является именем функции, а не именем указателя.

Вернемся теперь к исходному объявленияю

double (*pf)(double);

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

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

Пусть имеются две функции, имеющие следующие прототипы.

double sqr(double x);
double cube(double x);

Объявим три указателя на функцию:

double (*pf1)(double) = &sqr;
double (*pf2)(double)= cube;

double (*pf3)(double)= 0;

Синтаксис, с которым объявлены первые две переменные (pf1 и pf2), несколько отличаются. Синтаксис, с которым объявлена первая переменная (pf1) не вызывает сомнений в корректности. Справа от знака присваивания находится выражение, значением которого является адрес функции. Могут возникнуть сомнения в корректности второго объявления, где объявлена переменная pf2. В этом объявлении в качестве инициализатора используется имя функции, а не ее адрес. Однако этот код нормально компилируется. Дело в том, что языке Си всегда в том коде, где ожидается указатель на функцию, а встречается имя функции, выполняется автоматическое преобразование имени функции в указатель.

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