Visual_Studio_2010
.pdfРезультат выполнения программы представлен на рис.10.6.
Рис. 10.6. Пример выделения слов из строки
Задание5
1.В программу включите обработку символов: запятая ',', точка с запятой ';', точка '.' в качестве разделителей слов.
2.Используйте символ пробела для разделения слов «фамилия, имя, год поступления в вуз».
3.В программу включите прототип функции wordstr().
4.Изучите возможность применения в программе функций strcpy_s() и strtok_s().
Пример 6. Написать программу расчета суммы и среднего арифметического числовых произвольного количества данных. В качестве ключевых слов для выбора варианта расчета принять mean (среднее) и sum (сумма).
В данном примере необходимо использовать функцию с переменным числом агрументов. Среднее арифметическое (m) рассчитывается по формуле
1 n
m n xi ,
i 1
где n – количество числовых данных, xi – текущее число. Программный код решения примера
#include <stdio.h>
#include <conio.h> #include <string.h> #include <stdarg.h>
#include <float.h>
/* Прототип функции с переменным числом аргументов*/ double varfun(char str[], double v1, ...);
171
int main(void)
{
double v1 = 10.0, v2 = 2.5, control;
char str[121];
printf("\n Enter one of the key words 'mean' or 'sum': "); gets_s(str, 120);
control = varfun(str, v1, v2, 7.5, 0.0); if (control < DBL_MAX)
printf("\n Result: %lf\n", control);
else
printf("\n Invalid input keyword.\n");
printf("\n\n ... Press any key: "); _getch();
return 0;
}
/* Определение функции с переменным числом аргументов */ double varfun( char str[], double v1, ...) {
/* Указатель на переменные списка аргументов */ va_list parg;
double sum = v1;
double value = 0.0; /* значение аргумента */
int count = 1; /* начальное количество аргументов */ int k, p;
char *ch = "mean"; char *ch2 = "sum";
// Лексиграфическое сравнение строк k = strcmp(str, ch);
if ( !k ) // k == 0 p = 1;
else if ( k ) // k != 0
{
k = strcmp(str, ch2); if ( !k )
p = 2;
else
p = 0;
}
if ( p == 1 ) {
va_start(parg,v1); /* инициализация указателя parg */
172
/* Просмотр списка аргументов*/
while( (value = va_arg(parg, double)) != 0.0)
{
// Суммирование числовых аргументов функции varfun() sum += value;
count++;
}
/* Завершение процесса счтывания аргументов */ va_end(parg);
return sum/count;
}
if ( p == 2 )
{
va_start(parg,v1);
while( (value = va_arg(parg, double)) != 0.0)
{
sum += value; count++;
}
va_end(parg);
return sum;
}
return DBL_MAX;
}
Возможный вариант выполнения программы показан на рис. 10.7.
Рис. 10.7. Пример выполнения программы
Задание 6
1.Напишите функцию с явным указанием количества аргументов.
2.Включите в программу расчет исправленной выборочной дисперсии (D), которая рассчитывается по формуле
|
1 |
n |
2 , |
|
D |
(xi m) |
|||
|
||||
|
n 1i 1 |
|
где m – среднее арифметическое заданных чисел.
173
Контрольные вопросы
1.Что лежит в основе всех программ, созданных на языке С?
2.Какие типы данных функция возвращать может и что не может?
3.Что такое прототип функции? Какие элементы объявления функции входят в ее прототип?
4.В чем разница между фактическими и формальными параметрами функции?
5.Какой способ передачи параметров в функциях предусматривает синтаксис языка С?
6.Как можно изменить значение аргумента функции в теле самой функции?
7.Какова область видимости переменных, определенных в теле функции?
8.Можно ли использовать функцию без параметров и без служебного слова void? К каким последствиям это может привести?
9.Какие обязательные атрибуты должна содержать функция с переменным числом аргументов?
БИБЛИОГРАФИЧЕСКИЙ СПИСОК
1.Шилдт Г. Полный справочник по С : пер. с англ./Г. Шилдт. – 4-е изд. –
М. : Вильямс, 2007. – 704 с.
2.Кочан С. Программирование на языке С : пер. с англ./С. Кочан. –
3-е изд. – М. : Вильямс, 2007. – 496 с.
3.Харбисон С. П. Язык программирования С : пер. с англ./С. П. Харбисон, Г. Л. Стил. – М. : Бином-Пресс, 2004. – 528 с.
4.Березин Б. И. Начальный курс С и С++/Б. И. Березин, С. Б. Березин. –
М. : ДИАЛОГ-МИФИ, 1998. – 288 с.
5.Подбельский В. В. Программирование на языке Си : учеб. пособие/ В. В. Подбельский, С. С. Фомин. – 2-е изд., доп. – М. : Финансы и стати-
стика, 2007. – 600 с.
6.Хусаинов Б. С. Структуры и алгоритмы обработки данных. Примеры на языке Си (+CD) : учеб. пособие / Б. С. Хусаинов. – М. : Финансы и стати-
стика, 2004. – 464 с.
7.Дейтел Х. М. Как программировать на С : пер. с англ. / Х. М. Дейтел, П. Дж. Дейтел. – 4-е изд. – М. : Бином-Пресс, 2006. – 912 с.
174
Тема 11
УКАЗАТЕЛИ И ФУНКЦИИ В ЯЗЫКЕ ПРОГРАММИРОВАНИЯ С
Изучаются вопросы программирования функций, аргументами которых могут быть указатели, а также функции, возвращающие значения через указатели.
ТЕОРЕТИЧЕСКАЯ ЧАСТЬ
В предыдущей теме анализировались примеры функций, аргументами которых выступали указатели. Здесь подробнее будут рассмотрены вопросы, касающиеся указателей и функций.
Ранее было отмечено, что в языке С аргументы передаются в функции по значению и не существует прямого способа изменить переменную вызывающей функции, действуя внутри вызываемой. Благодаря аргументам-указателям функция может обращаться к объектам в вызвавшей ее функции, в том числе модифицировать их [1]. В качестве примера рассмотрим функцию swap(), в задачу которой входит обмен элементов местами. Для решения такой задачи необходимо передать из вызывающей программы (например, из главной функции main()) в функцию указатели на переменные, которые нужно изменить.
Программный код решения примера
#include <stdio.h>
#include <conio.h>
// Прототип функции void swap(int*, int*);
int main (void)
{
int a = 10, b = -20;
// Вывод на консоль исходных значений переменных printf("\n Initial values:\n a = %d, b = %d\n", a, b);
//Вызов функции swap() с фактическими параметрами swap(&a, &b);
//Результат после обращения функции swap() printf("\n New values:\n a = %d, b = %d\n", a, b);
printf("\n ... Press any key: ");
_getch(); return 0;
}
175
// Определение функции
void swap(int *pa, int *pb)
{
int temp; temp = *pa;
*pa = *pb; *pb = temp;
}
В программе в качестве фактических параметров функции swap() выступают адреса заданных переменных. Можно было в главной функции определить указатели и инициализировать их адресами заданных переменных, а потом передать эти указатели в функцию swap.
Результат выполнения программы показан на рис. 11.1.
Рис. 11.1. Результат обмена данными, выполненного функцией swap()
Указатели, передаваемые в функцию, могут ссылаться на указатели, обозначать начало какого-либо массива и т. д., применяться для защиты массивов, над которыми необходимо произвести некоторые вычисления или преобразования. Особым свойством указателей можно считать возможность использования их в качестве возвращаемых значений функций. Поскольку функции возвращают только одно значение, то несколько значений одного типа можно поместить в массив, а затем указатель на этот массив рассматривать в качестве возвращаемого значения.
Общая форма определения функции, которая возвращает указатель,
следующая:
тип *имя_функции ( аргументы функции )
{
// тело функции
тип *имя_указателя;
return имя_указателя;
}
Рассмотрим пример сложения двух одномерных массивов и результата возвращения через указатель.
176
Программный код решения примера
#include <stdio.h> #include <conio.h> #include <stdlib.h>
int *out2(int A[], int B[], int);
int main (void) { int i, n;
int A[] = {1,2,3,4,5}; int B[] = {2,2,2,2,2}; int *ptrAB = NULL;
n = (sizeof(A)/sizeof(A[0]));
puts("\n The initial arrays: "); for (i = 0; i < n; i++)
printf(" %d", A[i]);
puts("");
for (i = 0; i < n; i++) printf(" %d", B[i]);
ptrAB = out2(A, B, n);
puts("\n\n Result from function: "); for (i = 0; i < n; i++)
printf(" %d", ptrAB[i]);
puts("\n\n Control of the arrays: "); for (i = 0; i < n; i++)
printf(" %d", A[i]);
puts("");
for (i = 0; i < n; i++) printf(" %d", B[i]);
free(ptrAB); // освобождение выделенной памяти printf("\n\n ... Press any key: ");
_getch(); return 0;
}
int *out2(int A[], int B[], int n) { int i;
int *ptr = (int *)calloc(n, sizeof(int)); //выделение памяти
for (i = 0; i < n; i++) ptr[i] = A[i] + B[i];
return ptr;
}
177
Программа не требует особых пояснений. Однако отметим, что никогда не следует возвращать адрес переменной, определенной в теле функции, так как переменные функции являются локальными и существуют только во время работы функции.
Указатели возвращаются подобно значениям любых других типов данных. Чтобы вернуть указатель, функция должна объявить его тип в качестве типа возвращаемого значения. Таким образом, если функция возвращает указатель, то значение, используемое в ее инструкции return, также должно быть указателем. В частности, многие библиотечные функции, предназначенные для обработки строк, возвращают указатели на символы.
В языке С существует такой механизм, как указатель на функцию. Допустим, существует несколько функций для различных операций с данными. В этом случае оказывается удобным определить указатель на функцию и использовать его там, где требуется производить расчет для различных функций.
Указатель на функцию – это переменная, содержащая в памяти адрес, по которому расположена функция [2]. Имя функции – это адрес начала ее программного кода. Указатели на функции могут передаваться функциям в качестве аргументов, возвращаться функциями, сохраняться в массивах и присваиваться другим указателям на функции [2].
Типичное определение указателя на функцию следующее:
тип_возвращаемый_функцией(*имя_указателя_на_функцию)(аргументы);
В круглых скобках определяется указатель на функцию, которая возвращает тот или иной тип – тип_возвращаемый_функцией. Хотя знак * обозначает префиксную операцию, он имеет более низкий приоритет, чем функции, заключенные в круглые скобки, поэтому для правильного комбинирования частей объявления необходимы дополнительные скобки [1]. При этом аргументы – это аргументы той или иной функции с заданным типом возвращаемого значения, для которой определяется указатель *имя_указателя_на_функцию. Очевидно, что возможны сложные объявления функций.
Указатели на функции часто используются в системах, управляемых меню [2]. Пользователь выбирает команду меню (одну из нескольких), обслуживающуюся своей функцией. Указатели на каждую функцию находятся в массиве указателей. Выбор пользователя служит индексом, по которому из массива выбирается указатель на нужную функцию.
Часто указатели на функции применяются при реализации обобщенных алгоритмов, например алгоритмов сортировки и поиска. В этом случае критерии сортировки и поиска приобретают вид отдельных функций и передаются при помощи указателей на функции в качестве параметра реализации основного алгоритма.
178
ПРАКТИЧЕСКАЯ ЧАСТЬ
Пример 1. Написать функцию с пузырьковой сортировкой числового массива, предусмотреть вызов этой функции по ссылке.
Вызов по ссылке означает, что в качестве фактических параметров функций будут использоваться адреса переменных, и в этом случае прототип таких функций будет содержать указатели на соответствующие типы.
Программный код решения примера
#include <stdio.h> #include <conio.h>
// Прототип функции
void bsort (int* const, const int);
int main (void) {
int A[] = {56, 34, 2, 0, 1, -21, 6, 8, 7}; int i, n;
//Размерность массива
n = sizeof(A)/sizeof(A[0]);
puts("\n Data items in original order:");
for (i = 0; i < n; i++) printf(" %3d", A[i]);
// Вызов функции сортировки - bsort() bsort (A, n);
puts("\n\n Data items in ascending order:"); for (i = 0; i < n; i++)
printf(" %3d", A[i]);
printf("\n\n ... Press any key: "); _getch();
return 0;
}
// Определение функции
void swap(int *pa, int *pb) { int temp;
temp = *pa; *pa = *pb;
*pb = temp;
}
void bsort (int *const arr, const int size) {
int pass,j; //счетчик проходов и счетчик сравнений
// Прототип функции обмена - swap() void swap (int*, int*);
179
// Цикл для контроля проходов
for (pass = 0; pass < size - 1; pass++ )
{
// цикл для контроля сравнений на данном проходе for (j = 0; j < size - 1; j++)
{
// обмен значений при нарушении порядка возрастания if (arr[j] > arr[j + 1])
{
swap(&arr[j], &arr[j+1]);
}
}
}
}
В функции сортировки bsort() в качестве формального параметра используется константный указатель, который указывает на первый элемент заданного массива. Второй формальный параметр также константный, так подчеркивается его неизменность в теле функции bsort(). Передача функции размера массива в качестве параметра имеет два преимущества: это хороший стиль программирования и, кроме того, такую функцию можно использовать многократно.
Прототип функции swap() включен в тело функции bsort(), потому что это единственная функция, которая вызывает функцию обмена swap().
Пример выполнения программы представлен на рис. 11.2.
Рис. 11.2. Пример сортировки массива методом пузырька
Задание 1
1.Напишите программу сортировки семи вещественных чисел, которые должны быть случайными по равномерному закону из интервала [–Х;+Х], где Х – номер компьютера, на котором выполняется лабораторная работа.
2.Видоизмените программу так, чтобы функция bsort() возвращала указатель на отсортированный массив, а сам исходный массив был при этом неизменным. Предусмотрите вывод на консоль исходного массива, потом отсортированного массива после вызова функции сортировки, и снова – для контроля – исходного массива. При этом аргументы функции bsort() оставьте без изменения.
180