МУ_ЛР5_ОП (1079930), страница 3
Текст из файла (страница 3)
int rez;
if ( n == 0 )
return rez = 1;
else
return rez = n * fact ( n - 1 ) ;
};
Вызов функции вычисления факториала (fact) из основной программы:
//
printf ("fact 5 = %d\n" , fact(5) );
..
Полученный результат:
fact 5 = 120
В литературе можно познакомится и с другими вариантами рекурсивных функция. Они широко используются в алгоритмах комбинаторных вычислениях.
14 Макросы и переменные этапа компиляции
В базовом языке СИ (и, конечно, в С++) предусмотрены возможности задания макросов (макрокоманд) или переменных этапа компиляции (иногда их называют препроцессорными переменными). Для этого используется директива #define. Переменная этапа компиляции задается так:
#define <имя этапа компиляции> <пробел “ ”> <выражение этапа компиляции>
Например, для размерности массива мы можем задать переменную NMAX:
#define NMAX 10 // Описание переменной этапа компиляции
…
int iMas[NMAX]; // Использование этой переменной для задания размерности массива
…
for ( int k = 0; k < NMAX ; k++ )… // Задание числа повторений цикла
С помощью специальных директив препроцессора (#ifndef и #ifdef) можно проверить определена ли переменная этапа компиляции к данному моменту обработки текста, и вставить в программу новый фрагмент текста:
#ifndef MyLibrary
#include <my_lib.h> // Подключение заголовочного файла
#endif
Примечание. Подстановка не производится в комментариях программы и текстовых константах или литералах..
При использовании макросов (макрокоманд) можно задавать параметры, на которые будет настраиваться текст макроопределения. При описании макросов задаются формальные макропараметры, которые должны быть текстовыми. Для обращения к макросам используется макровызовы, которых может быть много. Такие параметры называются фактическими макропараметрами. Определение макроса выполняется на основе следующего формального правила:
#define <имя макроса>(<параметр>, …, <параметр>) <текст на языке, содержащий параметры>
Имена формальных параметров должны быть уникальными в пределах описания. Между именем макроса и открывающей скобкой не должно быть пробелов. Если макрос продолжается на следующую строку текста, то используется обратная наклонная черта (“\”). Макровызов может быть размещен в тексте программы после определения макроса и содержит конкретные параметры. Примеры макрокоманд и макровызовов:
// Описанные макросы с параметрами
#define max(a,b) ((a>b)?a:b) // Макрос вычисления максимума их двух переменных
#define Swap(type,a,b) {type t;t=a;a=b;b=t;} // Макрос кода программы
…
// Макросы
int imax = max(3,5); // Макровызов max с константами
printf ("Максимум из двух = %d \n",imax);
// Аналогично imax = ((3>5)?3:5);
a=10 ; b = 20;
imax = max(a, b); // Макровызов max с с переменными
printf ("Максимум из двух = %d \n",imax);
// Аналогично imax = ((a>b)?a:b);
int x = 5 , y = 10 ;
printf ("До Swap x , y %d %d \n",x , y);
Swap(int,x,y);
printf ("После Swap x , y %d %d \n",x , y);
// Аналогично {int t;t=x;a=y;b=t;};
// Макрос с типом переменной
double d1 = 5.5 , d2 = 10.5 ;
printf ("До Swap d1 , d2 %f %f \n",d1 , d2);
Swap(double,d1,d2);
printf ("После Swap d1 , d2 %f %f \n",d1 , d2);
// Аналогично { double t;t=d1;a=d2;b=t;};
Результат будет таким:
Максимум из двух = 5
Максимум из двух = 20
До Swap x , y 5 10
После Swap x , y 10 5
До Swap d1 , d2 5.500000 10.500000
После Swap d1 , d2 10.500000 5.500000
Примечание. Использовать и разрабатывать макросы необходимо очень внимательно, так как при макроподстановке возможны различные ошибки: типов переменных, ошибки повторных описаний переменных и т.д. Такие ошибки трудно обнаружить, так как на этапе компиляции невозможно использовать отладчик. В нашем примере, если неверно указать тип переменных (вместо double задать int), выполнится неявное округление переменной и результат получиться неверным. Проверьте это на практике.
15 Параметры главной функции main
Главная функция программы может использоваться с параметрами, формат которых следующий:
void main ( int argc, [ char * [] argv, [ char * [] env ]]), где
argc – задает число параметров командной строки, если равно 1 то параметров нет.
Argv – массив указателей на строки представляющие параметры командной строки.
Env - массив указателей на строки для переменных окружения.
Небольшая программа позволяет вывести значения параметров командной строки и переменных окружения.
void main( int argc, char * argv[] , char * env[] )
{
…
// Число параметров командной строки
printf ("Число параметров командной строки = %d\n" , argc );
// Распечатка спика параметров
printf ("Параметры командной строки:\n" );
if ( argc >0)
{
for (int i = 0 ; i < argc ; i++)
printf ("Номер - %d Значение =%s \n" , i+1 , argv[i] );
};
// Распечатка переменных окружения (set – переменные для текущего процесса)
printf ("Переменные окружения:\n" );
i = 0;
while ( env[i] !=NULL )
{
printf ("Номер - %d Значение =%s \n" , i+1 , env[i] );
i++;
};
Результат распечатаем не полностью, так как большой объем переменных окружения:
Число параметров командной строки = 3
Параметры командной строки:
Номер - 1 Значение =i:\2014_2015\kaf\оп\лр\prog\lr5_op\debug\LR5_OP.exe
Номер - 2 Значение =aaa
Номер - 3 Значение =ddd
Переменные окружения:
Номер - 1 Значение =ALLUSERSPROFILE=C:\Documents and Settings\All Users
Номер - 2 Значение =APPDATA=C:\Documents and Settings\serge\Application Data
… (N.B. – параметров значительно больше!)
Первый параметр командной строки (argv[i]) всегда задает имя выполняемой программы, а два других мы ввели в параметры проекта: Project-> Debugging -> Comand Arguments -> aaa ddd.
16 Inline функции
В языке СИ предусмотрена возможность предписания компилятору не вызова функции (передачи управления к операторам функции), а непосредственной вставки операторов функции в текст основной программы. В ряде случаев это приводит к экономии памяти и времени выполнения программы. Такие функции называются встраиваемыми и имеют спецификатор inline. Пример встраиваемой функции и ее использования приведен ниже:
//описание inline функция
inline int even (int x)
{
return ! (x%2); // возврат по модулю 2 четное 1 (истина) нечетное 0 (ложь)
} ;
…
Пример вызова в основной программе:
//вызов inline функции
i =10;
if (even (i)) // встраивается операция взятия по модулю с отрицание
// это эквивалентно выражению - if (!(i%2))
printf ("Число %d является четным\n", i );
else
printf ("Число %d является нечетным\n", i );
i = 5 ;
if (even (i))
printf ("Число %d является четным\n", i );
else
printf ("Число %d является нечетным\n", i );
В результате получим:
Число 10 является четным
Число 5 является нечетным
17 Указатели на функции
Опишем простые функции для демонстрации использования указателей на функции.
// Функции для указателей
//
int fun1 (int i) { return i=5;};
//
int fun2 (int i) { return i=10;};
//…
В основной программе, указатель на функцию (pFun) вычисляется динамически:
int i , j , k =5;
int (* pFun) (int); // указатель на функцию с параметром int
pFun = &fun1;
i = (pFun)(k); // выражение одинаковое для вызова функции через указатель
printf ("pFun = &fun1 => %d\n" , i );
pFun = &fun2; //
j = (pFun)(k); // выражение одинаковое для вызова функции через указатель
printf ("pFun = &fun2 => %d\n" , j );
j = pFun(k); // можно и так
printf ("j = pFun(k) => %d\n" , j );
В результате получим:
pFun = &fun1 => 5
pFun = &fun2 => 10
j = pFun(k) => 10
18 Библиотеки стандартных функций
В системах программирования предусматривается много стандартных библиотек для функций различного назначения (например, для работы со строками, выполнения ввода и вывода, работы с массивами и т.д.). Эти библиотеки подключаются с помощью заголовочных файлов или пространств описаний (пространств имен в С++ - namespace). Кроме заголовочных файлов для использования библиотек подключаются специальные модули (иногда они подключаются автоматически), содержащие описания функций ( *.lib или *.dll). Пример подключения библиотек ввода/вывода и библиотек для работы с математическими, системными функциями и других:
#include <math.h> // Математическая библиотека функций
#include <process.h> // Системная библиотека функций
#include <string.h> // библиотека функций для работы со строками
#include <stdlib.h> // Стандартная библиотека разных функций
#include <locale.h> // библиотека локализации программ
Стандартных библиотек и классов для описания строк очень много. Нужно хорошо знать их назначение и их состав для использования в программах. Чем лучше знания о библиотеках, тем быстрее и безошибочно можно создать сложную программу. В современных системах программирования доступны (большом количестве) библиотеки классов, которые описывают новые дополнительные типы данных. Для детального знакомства с библиотеками нужно использовать: литературу, справочники и MSDN [3] в локальном варианте (нужно инсталлировать вместе с VS) или в Интернет.
19 Примеры программы с использованием функций
Вторая часть задания, помимо первой связанной с изучением теоретического раздела заключается в том, чтобы испытать в проекте СИ уже отлаженные программы и фрагменты программ. Возможно, что, осваивая теоретическую часть работы, вы уже на компьютере проверили выполнение фрагментов текста и применения различных операторов ветвления (из раздела 3), тогда вам будет проще продемонстрировать их работу преподавателю. В дополнение к примерам, расположенным выше нужно испытать и изучить примеры расположенные ниже. Эти действия нужно сделать в отладчике.
Для этого нужно создать пустой проект в MS VS (Test_LR2), как описано выше, скопировать через буфер обмена в него текст данных примеров, отладить его и выполнить.
20 Примеры, описанные в теоретической части ЛР
Нужно внимательно изучить и проверить работу всех примеров из теоретической части ЛР. Эти примеры расположены выше. Все примеры можно скопировать в свой проект. Все эти задания выполняются обязательно, они не требуют дополнительной отладки и легко (через буфер обмена -Clipboard) переносятся в программу. Все фрагменты должны демонстрироваться преподавателю. В частности, в первой части, там представлены следующие примеры:
-
Простая функция суммирования 2-х целых (Summa).
-
Функция максимума в целом массиве (MaxMas).
-
Функция с попыткой возврата значений (Summ2).
-
Функция с возвратом указателя (Summ3).
-
Функция с константным параметром (Summ0)
-
Функции с передачей массива в качестве параметра (Summ51 , Summ5)
-
Функция с массивом с нулевым элементом(Summ6).
-
Рекурсивная функция факториала ( fact ).
-
Макросы (max и Swap).
-
Пример с распечаткой параметров командной строки и окружения.
-
Пример с inline функцией.
-
Пример с вызовом функции через указатели.
Кроме этого ниже представлены примеры, которые могут быть полезными, в том числе и при выполнении контрольных заданий. Их тоже нужно изучить и проверить.
21 Пример с функцией SWAP для целых
Описание функции SWAP:
void SWAP(int * a, int * b)
{
int Temp;
Temp = *a;
*a = *b;
*b = Temp;
}