C++: Л/р№3 Функции - часть II
реклама
Функции - часть II (4 часа)
Цель работы: изучение правил создания и использования функций.
Порядок выполнения работы
1.Провести анализ примеров. Программы из примеров копируются в файлы С++ и выполняются.
2.Выполнить задания.
3.Защитить выполненную работу.
Контрольные вопросы
1.Как работают программы примеров?
2.Перечислить преимущества применения указателей на функцию.
3.Придумать новый способ передачи функции неопределенного количества аргументов.
Примеры
Пример 1. Пример использования указателей на функцию.
Имя функции в Си – это указатель-константа на функцию, равный адресу точки входа (адресу первой машинной команды) функции. Помимо констант, возможно описание и указателей-переменных на функции:
return_type (* name) (arg_list);
где return_type – тип возвращаемого функцией значения; name – имя переменной-указателя на функцию; arg_list – необязательный список типов аргументов, передаваемых функции при ее вызове по значению указателя.
Указатели на функцию используются в случаях, перечисленных ниже.
1. Многие библиотечные функции в качестве аргумента получают указатель на функцию. Например, функция сортировки qsort( ) получает четвертым аргументом указатель на составленную пользователем функцию, выполняющую сравнение сортируемых элементов. При этом библиотечные функции задают типы для возвращаемого значения и аргументов.
2. Использование указателей на функцию в качестве аргументов позволяет разрабатывать универсальные функции, например функции численного решения уравнений, численного интегрирования и дифференцирования.
3. Указатели на функцию могут использоваться для косвенного вызова резидентных программ, точка входа в которые записывается в известное место оперативной памяти, например на место неиспользуемого вектора таблицы векторов прерывания.
Как и обычные переменные, указатели на функции могут объединяться в массивы. Например, описать и инициализировать массив указателей на функцию можно следующим образом:
/* Описание прототипов функций, точки
входа в которые будут элементами
массива указателей на функции. */
int cmp_year( const void *, const void*);
int cmp_price( const void *, const void*);
int cmp_title( const void *, const void*);
int cmp_name( const void *, const void*);
int cmp_totally( const void *, const void *);
int(*fcmp[5])( )=
{cmp_name, cmpJtttle, cmp_year,
cmp_price, cmp_totally };
Доступ к элементам массива fcmp выполняется как к обычным элементам массива. Например:
int index=0;
fcmp[index](ptr1, ptr2);
вызывает на выполнение функцию cmp_name(ptrl, ptr2). Заменив только значение индекса, можно обратиться к другой функции.
В качестве примера приведена программа, которая для доступа к функциям difference( ) и sum( ) использует указатель на функцию.
#include<stdio.h>
int difference(int, int); /* прототип функции difference */
int sum(int, int); /* прототип функции sum */
void main(void)
{
int (*func_ptr)(int, int); /* описание указателя на функцию,
возвращающую значение типа int и которой
передаются два аргумента типа int */
int var1 = 20, var2 = 5, ret;
func_ptr = difference; /* присваивание указателю-переменной
значения указателя-константы */
ret = func_ptr(var1, var2); /* косвенный вызов функции */
printf("Полученный результат %d\n", ret);
func_ptr = sum; /* присваивание указателю-переменной
значения указателя-константы */
ret = func_ptr(var1, var2); /* косвенный вызов функции sum*/
printf("Полученный результат %d\n", ret);
}
int difference(int a, int b)
{
return (a - b);
}
int sum(int a, int b)
{
return (a + b);
}
Пример 2. Примеры использования функции с переменным числом аргументов.
Язык программирования Си допускает использование переменного числа аргументов. Признаком функции с переменным числом аргументов является многоточие ... в списке параметров прототипа функции. Встретив (...), компилятор прекращает контроль соответствия типов параметров для такой функции. Естественно, что функция с переменным числом аргументов должна иметь способ определения точного числа входных аргументов при каждом вызове.
Далее приводятся два варианта функции example( ), которая принимает переменное число аргументов и выводит на печать их количество и принятые значения. В первом варианте функции число действительно переданных через стек значений задает первый аргумент.
Неудобство использования первого аргумента в качестве счетчика фактически переданных в функцию значений заключается в необходимости каждый раз подсчитывать число аргументов. Если, например, известно, что среди переданных значений не будет нулевого, значение параметра, равное нулю, можно использовать как индикатор завершения списка переданных аргументов (см. второй вариант).
Для доступа к фактическим аргументам в функции example( ) используется продвижение указателя вперед. Именно в таком порядке расположатся в стеке копии аргументов при так называемом Си-порядке передачи аргументов в функцию: последний аргумент записывается ("пушируется" машинной командой PUSH) в стек самым первым, затем в стек помещается предпоследний аргумент и т. д. Запись в стек выполняется по адресу, задаваемому парой регистров SS:SP. Но так как при выполнении команды PUSH значение регистра SP физически уменьшается, то в оперативной памяти копия первого аргумента в списке параметров расположена по меньшему адресу памяти, следующий аргумент – по большему адресу и т.д.
На Си-порядок передачи параметров указывает модификатор типа функции cdecl. Он устанавливается специальным параметром IDE (Main Menu-Options-Compiler-Code Generation-Cdlling Convention для Turbo С версии 2.01 или Main Menu-Options-Coimpile-Entry/-Exit Code в Borland C++) или задается опцией командной строки (-р-). По умолчанию выбирается cdecl. Альтернативой Си-порядку передачи параметров является последовательность передачи параметров, принятая в языке Pascal: копии аргументов "пушируются" в стек, начиная с первого. Как результат, копия первого аргумента имеет в оперативной памяти больший адрес, чем копия последнего. На такой порядок передачи параметров указывает модификатор типа функции pascal. Явное задание порядка передачи отменяет порядок передачи, принимаемый компилятором по умолчанию. Модификатора типа функции должны совпадать как в прототипе, так и в определении функции. В языке Pascal не поддерживается переменное число аргументов и функция с модификатором Pascal должна иметь фиксированное число аргументов. Модификатор типа функции pascal требуется при разработке Си-функций, которые будут вызываться из пределов написанных на языке Pascal программ.
Функции с переменным числом аргументов должны иметь как минимум один фиксированный аргумент для того, чтобы была возможность "привязаться" к адресу копии этого аргумента в стеке.
#include <stdio.h>
void example(int, ...); /* прототип функции с переменным числом
аргументов. Первый аргумент задает
фактически переданное количество
аргументов */
void main(void)
{
int var1 = 5, var2 = 6, var3 = 7, var4 = 8, var5 = 9;
example(2, var1); /* вызов функции с двумя аргументами */
example(3, var1, var2); /* та же функция имеет три аргумента */
example(6, var1, var2, var3, var4, var5); /* а теперь - пять */
}
void example(int arg1, ...)
{
int * ptr = &arg1; /* установка указателя на первый
переданный в функцию фактический аргумент */
printf("\nФункции EXAMPLE передано всего %d аргумента(ов):\n", arg1);
for(; arg1; arg1--)
{
printf(" %d", * ptr); /* печатаем все переданные в функцию
фактические аргументы */
ptr++; /* продвигаем указатель на следующее слово стека */
}
}
///////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
void example(int, ...); /* прототип функции с переменным числом
аргументов. Индикатором конца списка
фактических параметров выбрано нулевое
значение в списке аргументов */
void main(void)
{
int var1 = 5, var2 = 6, var3 = 7, var4 = 8, var5 = 9;
example(var1, 0); /* вызов функции с двумя аргументами */
example(var1, var2, 0); /* та же функция имеет три аргумента */
example(var1, var2, var3, var4, var5, 0); /* а теперь - пять */
}
void example(int arg1, ...)
{
int number = 1; /* учитываем и переданный индикатор конца
списка фактических аргументов */
int * ptr = &arg1;/* установка указателя на первый
переданный в функцию фактический аргумент */
while(* ptr) /* пока не встретится нулевой элемент списка */
{
printf(" %d", * ptr); /* печатаем все переданные в функцию
фактические аргументы */
ptr++; /* продвигаем указатель на следующее слово стека */
number++; /* наращиваем счетчик параметров */
}
printf("\nФункции EXAMPLE передано всего %d аргумента(ов):\n", number);
}
Пример 3. Выдача командной строки и среды, переданной программе.
реклама
#include <stdio.h>
int main(int argc, char ** argv, char **envp)
{
char * ptr = argv[0];
printf("Командная строка, переданная программе %s\n", ptr);
while(argc--) /* пока не выведем argc слов */
puts(*argv++); /* выводим слова командной строки */
printf("Cpеда программы %s\n",ptr);
while(*envp) /* пока не встретится нулевой указатель */
puts(*envp++); /* выводим строки среды */
return 0; /* завершение с передачей кода возврата в MS-DOS */
}
Примечание.
Функция main( ) обязательно присутствует в каждой программе. Она получает управление первой после запуска программы на выполнение (является точкой входа в программу). В остальном это обычная функция, которой могут передаваться аргументы и которая может возвращать значение в точку вызова.
Для того чтобы был возможен доступ к аргументам, функцию main( ) следует описать в одной из двух форм:
[тип] main (int argc, char ** argv)
{
тело функции
}
или
[тип] main (int argc, char ** argv, char ** env)
{
тело функции
}
В первом случае функция main( ) будет иметь доступ ко всем словам командной строки, во втором – еще и к текстовым строкам среды программы.
Слова командной строки – это преобразованные к формату ASCIIZ-строк последовательности символов, разделенных пробелами в командной строке, заданной при запуске программы или каким-либо другим способом переданные запускаемой программе.
Среда программы – это совокупность ASCIIZ-строк, которая становится доступной программе при ее запуске на выполнение. Среда программы определяет некоторые особенности поведения оболочки и ядра операционной системы. Например, строка среды
СОМSPEC = C:\COMMAND.СОМ
определяет местоположение в файловой системе командного процессора. Эта информация необходима при восстановлении его нерезидентной части, которая была частично переопределена завершаемой программой. Строка среды
PATH=C:\;C:\TOOLS;
перечисляет те накопители и директории, в которых будет выполняться поиск запускаемого на выполнение .СОМ, .ЕХЕ или .ВАТ файла, если он отсутствует в специфицированных или текущих накопителе и директории.
Смысл аргументов, передаваемых в точку входа main(): int argc – число слов в командной строке при запуске программы из оболочки операционной системы или переданное в программу каким-либо другим способом; char ** argv – указатель на массив указателей из argc элементов. Каждый элемент массива является указателем на слово командной строки в формате ASCIIZ-строки. Нулевое слово, на начало которого указывает argv[0], – это спецификация запускаемого на выполнение файла. Следующий элемент массива указателей argv[l] указывает на начало первого слова командной строки, argv[2] – на начало второго слова и т.д.; char ** envp – указатель на массив указателей из переменного числа элементов. Признаком завершения массива является нулевой указатель. Каждый элемент массива является указателем на отдельную ASCIIZ-строку среды программы.
Функция main( ) может возвращать значение. Особенностью функции main( ) является то, что возвращаемое значение передается через операционную систему. Она может передавать код возврата только в одном байте. Поэтому для функции main( ) возвращаемое значение имеет тип int, в котором действительным будет только младший байт. Как и для обычной функции, возврат значения из main( ) выполняет оператор return. Дополнительно передача возвращаемого значения из функции main( ) и любых других функций может выполняться библиотечной функцией exit( ). Функция exit( ) завершает исполнение программы и передает код возврата в операционную систему.
Программа примера может быть вызвана в командной строке операционной системы следующим образом (программа записана под именем prog)
D:\>prog Пример вызова программы из командной строки
Заданиe
Программу задания из лабораторных работ 1-2 реализовать с использованием указателя на функцию и передачей реальной размерности массива через параметры функции main( ). При этом можно в программе описать массив с максимальной размерностью, значения количества строк и столбцов, переданные через командную строку в строковом виде, преобразовать в числа и передать функции поиска седловой точки совместно с массивом (указателем на данные массива).
Выполнение задания: Writen by Alexei Borisenko
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define size 51
int perevod(char*);
int col(int a[][size],int,int);
int main(int argc, char* argv[])
{ int a[size][size],i,b,j,c,h,i1;
clrscr();
int (*kosv)(int a[][size],int,int);
int m,n,false;
if(argc==1)
{printf("Snachala parametri zadai idiot!!!");
return(0);
}
m=perevod(argv[1]);
n=perevod(argv[2]);
printf("m=%d n=%d",m,n);
for(i=0;i<m;i++)
{printf("\n");
for(j=0;j<n;j++)
{ printf("a[%d][%d]= ",i,j);
scanf("%d",&a[i][j]);}
}
for(i=0;i<m;i++)
{printf("\n");
for(j=0;j<n;j++)
{ printf(" %4d",a[i][j]);}
}
kosv = col;
false=kosv(a,m,n);
printf("%d",false);
return(0);
}
/////////////
int perevod(char* x)
{ int t=0,d=1;
int s=0;
// for(t=0;x[t];t++);
while(x[t]) {t++;}
for(;t>0;t--)
{
s+=(*(x+t-1)-'0')*d;
d*=10;
}
return(s);
}
//////////
int col(int a[][size],int m, int n)
{
int c,b,h,j,i,i1;
for(i=0;i<m;i++)
{ c=*(*(a+i)+0);h=0;
for(j=0;j<n;j++)
{ if(c>*(*(a+i)+j))
{c=*(*(a+i)+j);
h=j;}
}
b=*(*(a+0)+j);
for(i1=0;i1<m;i1++)
{
if(b<*(*(a+i1)+h))
{b=*(*(a+i1)+h);}
}
if(c==b){
printf("\n\nURA mi nashli osedluju tochechku a[%d][%d] = ",i,h);
return(c);
}
}
}
+ Ранее я выкладывал С++: Перевод строки символов char* в int. . Это как раз к этому заданию.
Конечно эту функцию можно реализовать через стрндартную библиатеку atoe();
Но здесь было задание написать свою. Она немного корявая, но работает.
По материалам ОмГУПС, все права защищены...
реклама
Лента материалов
Соблюдение Правил конференции строго обязательно!
Флуд, флейм и оффтоп преследуются по всей строгости закона!
Комментарии, содержащие оскорбления, нецензурные выражения (в т.ч. замаскированный мат), экстремистские высказывания, рекламу и спам, удаляются независимо от содержимого, а к их авторам могут применяться меры вплоть до запрета написания комментариев и, в случае написания комментария через социальные сети, жалобы в администрацию данной сети.
Сейчас обсуждают