книги / Практикум по программированию на языке Си
..pdfЗАДАЧА 08-11. Определите функцию с параметром-массивом. В теле функции напечатайте значение параметра-массива и попытайтесь вычислить его размер. В основной программе определите и инициализируйте массив с элементами long double. Напечатайте его размеры и число элементов. Обратитесь к функции, используя массив в качестве аргумента.
/* 08_11.c - значение параметра-массива в теле функции */
#include <stdio.h>
#define PRINTD(EXPRESSION) \ printf(#EXPRESSION"=%d\n",EXPRESSION)
void arrayTest(long double ar[], int n)
{
printf("\nParameter: address = %p\n", ar); PRINTD(sizeof(ar));
PRINTD(sizeof(ar[0]));
}
int main()
{
long double array[]={5.6,2.4,9.2,1.3,8.6,3.5}; int sizeArray = sizeof(array)/sizeof(array[0]); PRINTD(sizeof(array)); PRINTD(sizeof(array[0]));
printf("Array address = %p"
"\nNumber of elements: %d\n", array, sizeArray); arrayTest(array, sizeArray);
return 0;
}
Результаты выполнения программы:
sizeof(array)=72
sizeof(array[0])=12 Array address = 4c64c Number of elements: 6
Parameter: address = 4c64c sizeof(ar)=4 sizeof(ar[0])=12
291
Вфункции arrayTest() печатаются "известные внутри функции" сведения о параметре-массиве: адрес начала его размещения в памяти, размер этого адреса (размер указателя типа long double *) и размер нулевого элемента. Как видно из результатов, печатать значение выражения sizeof(ar)/sizeof(ar[0]) не имеет смысла – бесполезно делить размер адреса на размер элемента массива.
Восновной программе печатаются размеры и адрес массивааргумента array[], размер его нулевого элемента и число элементов в массиве. Важно отметить, что адрес массива-аргумента совпадает с адресом-значением массива-параметра. Но "ведут они себя" по-разному.
ЭКСПЕРИМЕНТ. Попытайтесь в программе 08_11.с напечатать значения следующих выражений:
*(++ar) – в функции arrayTest(); *(++array) – в основной программе;
Важное напоминание: для вывода значений типа long double используется любая из спецификаций преобразования %Le, %Lf, %Lg,
%LF, %LG.
Добавив в функцию arrayTest() оператор
printf("*(++ar)=%Le", *(++ar));
получим (см. текст программы 08_11_1.с):
sizeof(array)=72
sizeof(array[0])=12 Array address = 4c64c Number of elements: 6
Parameter: address = 4c64c sizeof(ar)=4 sizeof(ar[0])=12 *(++ar)=2.400000e+00
Все соответствует нашим ожиданиям – выражение ++ar – адресует второй элемент массива-аргумента с индексом 1.
Попытка напечатать значение (++array) в основной программе с помощью оператора
292
printf("(++array)=%le", *(++array));
воспринимается компилятором как ошибка (см. программу
08_11_2.с):
08_11_2.c: In function `main':
08_11_2.c:21: wrong type argument to increment
Это соответствует константности имени массива (в нашей программе – имя массива array[]).
ЗАДАЧА 08-12. Напишите функцию, нормирующую значения элементов вещественного массива-параметра. Нормировка состоит в делении каждого элемента на среднее арифметическое значение всех элементов. (Параметр – имя массива, в теле функции использовать индексирование.) В основной программе определите и инициализируйте массив, напечатайте его элементы до обращения к функции и после обращения.
Следующая программа решает задачу:
/* 08_12.c - Функция, нормирующая значения элементов массива-параметра */
#include <stdio.h> #include <float.h> #include <math.h>
void normalization(double array[], int n)
{
int i;
double mean=0.0; for (i=0; i<n; i++)
mean += array[i]; mean /= n;
printf("\nmean=%f",mean); if (fabs(mean) > DBL_MIN) for (i=0; i<n; i++)
array[i] /= mean;
}
int main()
{
double row[] = {5.6, 2.4, 9.2, 1.3, 8.6, 3.5};
293
int sizeRow = sizeof(row)/sizeof(row[0]); int i;
printf("\nBegin array:"); for (i=0; i<sizeRow; i++)
printf("%c[%d]=%5.2f",i%4?'\t':'\n',i,row[i]); normalization(row, sizeRow);
printf("\nResult array:"); for (i=0; i<sizeRow; i++)
printf("%c[%d]=%5.2f",i%4?'\t':'\n',i,row[i]); return 0;
}
Результаты выполнения программы:
Begin array: |
[1]= 2.40 |
[2]= 9.20 |
[3]= 1.30 |
[0]= 5.60 |
|||
[4]= 8.60 |
[5]= 3.50 |
|
|
mean=5.100000 |
|
|
|
Result array: |
[1]= 0.47 |
[2]= 1.80 |
[3]= 0.25 |
[0]= 1.10 |
|||
[4]= 1.69 |
[5]= 0.69 |
|
|
В функции normalization() вычисляется среднее арифметическое элементов массива-параметра (переменная mean). Его абсолютное значение сравнивается с препроцессорной константой DBL_MIN, определенной в заголовочном файле <float.h>. Если значение mean превышает DBL_MIN, то выполняется нормирование элементов массива. Остальное очевидно из текста программы и результатов.
Параметры-массивы специфицируются выражениями:
тип имя_параметра[] тип * имя_параметра
В приведенных программах использовалась первая из приведенных форм спецификации. Именно эту форму рекомендуется применять, чтобы подчеркнуть передачу в функцию массива, а не только указателя. Однако в практике программирования зачастую используется вторая форма, где имя массива представлено как обычный указатель.
294
ЗАДАНИЕ. Измените спецификации массивов-параметров в функциях из программ 08_11.с и 08_12.с. Например, озаглавьте функцию нормализации массива таким образом:
void normalization(double * array, int n).
Откомпилируйте и выполните программы с указанными изменениями.
ЗАДАЧА 08-13. Напишите функцию для реверсирования, т.е. перестановки в обратном порядке значений элементов целочисленного массива-параметра. В качестве вспомогательной определите функцию для обмена значений двух элементов массива. В основной программе определите и инициализируйте массив, напечатайте значения его элементов до обращения к функции перестановки и после обращения.
Для обмена значений двух переменных нужна функция с пара- метрами-указателями. Для перестановки значений двух элементов массива можно передать функции в качестве параметров имя массива (это указатель) и индексы переставляемых элементов. Прототип функции для целочисленного массива может быть таким:
void swap(int array[], int j, int k);
Функция реверсирования массива получает в качестве параметров его имя (указатель) и его размер (количество элементов):
void reverse(int array[], int size);
В теле функции reverse() нужно выполнить size/2 обращений к функции swap(), поочередно обменивая значения элементов – первого (с нулевым индексом) с последним, второго с предпоследним и т.д.
Следующая программа решает задачу:
/* 08_13.c - функция "реверсирования" массива-параметра */
#include <stdio.h>
void swap(int ar[], int j, int k)
{
295
int temp;
temp = ar[j]; ar[j] = ar[k]; ar[k] = temp;
}
void reverse(int array[], int size)
{
int i;
double mean=0.0;
for (i=0; i<size/2; i++) swap(array, i, (size-i-1));
}
int main()
{
int line[] = {5, 6, 2, 4, 9, 2, 1};
int sizeLine = sizeof(line)/sizeof(line[0]); int i;
printf("Begin array:\n"); for (i=0; i<sizeLine; i++)
printf("[%d]=%d\t",i,line[i]); reverse(line, sizeLine); printf("\nResult array:\n");
for (i=0; i<sizeLine; i++) printf("[%d]=%d\t",i,line[i]);
return 0;
}
Результаты выполнения программы:
Begin array: |
|
|
[0]=5 |
[1]=6 [2]=2 [3]=4 [4]=9 [5]=2 [6]=1 |
|
Result |
array: |
|
[0]=1 |
[1]=2 |
[2]=9 [3]=4 [4]=2 [5]=6 [6]=5 |
ЗАДАНИЕ. Замените в предыдущей программе функцию обмена значений элементов массива функцией перестановки значений обычных (неиндексированных) переменных типа int.
Функция для перестановки значений переменных, адресованных аргументами-указателями, может быть такой:
296
void swapInt(int * a, int * b)
{
int temp;
temp |
= *a; |
|
*a |
= |
*b; |
*b |
= |
temp; |
}
В теле функции для доступа к переменным-аргументам явно используется разыменование указателей-параметров. При обращении к этой функции потребуется использовать адреса переменных. В нашем случае обращение в цикле из функции reverse() будет таким:
swapInt(&array[i], &array[size-i-1]);
Программа с полным решением такого варианта задачи приведена в файле 08_13_1.с.
Функция с параметром-массивом может возвращать адрес како- го-либо из его элементов.
ЗАДАЧА 08-14. Определите функцию с параметром-массивом, которая возвращает адрес максимального элемента массива, заданного в качестве аргумента. В основной программе определите одномерный массив и, используя функцию, замените его положительные элементы нулевыми.
Функцию, возвращающую адрес максимального элемента масси- ва-параметра, можно написать по-разному, но выбор ее прототипа практически однозначен:
double * maxElement(double dar[], int size);
Втеле функции нужно определить вспомогательный указатель типа double * и, перебрав все элементы массива-параметра dar[], последовательно присваивать указателю адрес очередного кандидата
внаибольшие элементы.
Восновной программе разыменование возвращаемого функцией значения обеспечивает доступ к максимальному элементу массива. Решение задачи:
297
/* 08_14.c - функция возвращает адрес максимального элемента массива-параметра.*/
#include <stdio.h>
double * maxElement(double dar[], int size)
{
int j; |
pElement; |
double * |
|
pElement |
= dar; |
for(j=1; |
j<size; j++) |
if (* pElement < dar[j]) pElement = &dar[j]; return pElement;
}
int main()
{
double real[] = {5.6, -2.4, 9.2, -1.3, 8.6, 3.5}; int sizeArray = sizeof(real)/sizeof(real[0]);
int i;
printf("\nSource array:"); for (i=0; i<sizeArray; i++)
printf("%c[%d]=%5.2f",i%4?'\t':'\n',i,real[i]); for (i=0; i < sizeArray; i++)
if (*maxElement(real, sizeArray) <= 0.0) break; else *maxElement(real, sizeArray) = 0.0;
printf("\nResulting array:"); for (i=0; i<sizeArray; i++)
printf("%c[%d]=%5.2f",i%4?'\t':'\n',i,real[i]); return 0;
}
Результаты выполнения программы:
Source array: |
[1]=-2.40 |
[2]= 9.20 |
[3]=-1.30 |
[0]= 5.60 |
|||
[4]= 8.60 |
[5]= 3.50 |
|
|
Resulting array: |
[2]= 0.00 |
[3]=-1.30 |
|
[0]= 0.00 |
[1]=-2.40 |
||
[4]= 0.00 |
[5]= 0.00 |
|
|
В программе отметим использование выражения
*maxElement(real, sizeArray) в качестве левого операнда выражения с операцией присваивания. Остальное очевидно из текста программы и результатов ее выполнения.
298
ЗАДАНИЕ. Измените тело функции maxElement(). Используйте вместо вспомогательного указателя индекс искомого максимального элемента массива.
Функция примет следующий вид:
double * maxElement(double dar[], int size)
{
int j;
int index 0; for(j=1; j<size; j++)
if (dar[index] < dar[j]) index = j; return &dar[index];
}
Остальная часть программы и, конечно, результаты останутся без изменений. Полный текст программы приведен в файле 08_14_1.c.
ЗАДАНИЕ. Дополните основную функцию main() программы 08_14.с печатью найденных максимальных элементов изменяемого массива-аргумента с их индексами.
Для получения индекса найденного максимального элемента необходимо вычесть из его адреса адрес начала массива. В этом случае цикл "обнуления" положительных элементов может быть таким:
for (i=0; i < sizeArray; i++)
{pMaxElement = maxElement(real, sizeArray); if (*pMaxElement <= 0.0) break; printf("\nMaxElement: [%d]=%5.2f",
pMaxElement - real,real[pMaxElement - real]); *pMaxElement = 0.0;
}
В приведенном фрагменте используется дополнительный указатель double * pMaxElement, позволяющий уменьшить количество обращений к функции maxElement(). Полный текст программы приведен в файле 08_14_2.c.
299
Мы уже убедились (в предыдущем параграфе), что с помощью параметра-указателя можно возвращать в вызывающую программу результаты исполнения функции. В этом случае функция изменяет значение объекта, адресованного аргументом-указателем.
Предположим, что нам нужно с помощью функции найти в мас- сиве-параметре несколько элементов с заданными свойствами. Из функции можно передать в вызывающую программу либо индексы, либо адреса найденных элементов. Рассмотрим задачу с функцией, определяющей индексы искомых элементов массива-параметра.
ЗАДАЧА 08-15. Определите функцию, которая возвращает через параметры индексы максимального и минимального элементов массива, заданного в качестве параметра. В основной программе определите одномерный массив и, используя функцию, замените найденные (максимальный и минимальный) элементы нулевыми.
Задачу решает следующая программа:
/* 08_15.c - индексы максимального и минимального элементов массива-параметра. */
#include <stdio.h>
void minMaxIndex(int mir[], int n, int * iMin, int * iMax)
{
int j;
* iMin = * iMax = 0; for(j=1; j<n; j++)
{ if (mir[* iMin] > mir[j]) * iMin = j; if (mir[* iMax] < mir[j]) * iMax = j;
}
}
int main()
{
int integer[]={5,6,2,4,9,2,1,3,8,6};
int sizeArray=sizeof(integer)/sizeof(integer[0]); int i, kMin, kMax;
printf("Source array:\n"); for (i=0; i<sizeArray; i++)
printf("%c[%d]=%d",i%5?'\t':'\n',i,integer[i]); minMaxIndex(integer, sizeArray, &kMin, &kMax); integer[kMin] = integer[kMax] = 0;
300