Программирование на C и C++

Онлайн справочник программиста на C и C++

Передача массивов в функции

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

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

#include <stdio.h>
void display(int num[10]);
int main (void) /* вывод чисел */
{
int  t [10], i;
for (i=0; i<10; ++i) t[i]=i;
display(t);
return 0;
}

void display(int num[10])
{
int i;
for (i=0; i<10; i++) printf ("%d", num[i]);
}

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

Следующий способ состоит в объявлении параметра для указания на безразмерный массив, как показано ниже:

void display(int num[])
{
int i;
for (i=0; i<10; i++) printf("%d ", num[i]);
}

где num объявлен как целочисленный массив неизвестного размера. Поскольку С не предоставляет проверку границ массива, настоящий размер массива не имеет никакого отношения к параметру (но, естественно, не к программе). Данный метод объявления также определяет num как целочисленный указатель.

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

void display(int *num)
{
int i;
for (i=0; i<10; i++) printf ("%d ", num[i]);
}

Он допустим, поскольку любой указатель может быть индексирован с использованием [], если он является массивом. (На самом деле массивы и указатели очень тесно связаны друг с другом.) Все три метода объявления параметра приводят к одинаковому результату - указателю. С другой стороны, элемент массива используется как аргумент, трактуемый как и другие простые переменные. Например, программа может быть написана без передачи всего массива:

#include <stdio.h>
void display(int num);
int main(void) /* вывод чисел */
{
int t[10], i;
for (i=0; i<10; ++i) t[i] = i;
for (i=0; i<10; i++) display(t[i]);
return 0;
}

void display(int num)
{
printf ("%d ", num);
}

Как можно видеть, в display() передается параметр типа int. Не имеет значения, что display() вызывается с элементом массива в качестве параметра, поскольку передается только одно значение.

Важно понять, что при использовании массива в качестве аргумента функции происходит передача в функцию его адреса. Это означает, что код внутри функции действует и может изменять настоящее значение массива, используемого при вызове. Например, рассмотрим функцию print_upper(), выводящую строку прописными буквами:

#include <stdio.h>
#include <ctype.h>
void print_upper(char *string);
int main(void) /* вывод строки в верхнем регистре */
{
char s[80];
gets (s);
print_upper(s) ;
return 0;
}

void print_upper(char *string)
{
register int t;
for(t=0; string[t]; ++t)
{
string[t] = toupper(string[t]);
printf("%c", string[t]);
}
}

После вызова print upper() происходит изменение содержимого массива s в main(). Если это не нужно, следует переписать программу следующим образом:

# include <stdio.h>
#include <ctype.h>
void print upper(char *string);
int main(void) /* вывод строки в верхнем регистре */
{
char s[80];
gets(s);
print_upper(s);
return 0;
}

void print_upper(char *string)
{
register int t;
for(t=0; string[t]; ++t)
printf ("%c",  toupper (string[t]));
}

В данной версии содержимое массива s остается неизменным, поскольку значения не меняются.

Классический пример передачи массивов в функции находится в стандартной библиотечной функции gets(). Хотя gets() из библиотеки Borland С++ гораздо сложнее, функция, показанная в данном примере, содержит основную идею работы. Для того, чтобы избежать путаницы и не вызвать стандартную функцию, данная функция называется xgets().

/* простейшая версия стандартной библиотечной функции gets() */

void xgets (char *s)
{
register char ch;
register int t;
for(t=0; t<79; )
{
ch = getche();
switch(ch)
{
case ' \r':
s[t] = '\0'; /* null завершает строку */
return;
case '\b':
if(t>0) t-;
break;
default:
s[t] = ch;
t++;
}
}
s[79] = ' \0';
}

Функция xgets() должна вызываться с указателем на символ. Это может быть имя символьного массива, который по определению является указателем на символ. xgets() организует цикл for от 0 до 79. Таким образом предотвращается ввод больших строк с клавиатуры. Если набирается более 80 символов, функция завершает работу. Поскольку C не имеет проверки границ массива, следует убедиться, что массив, передаваемый в xgets(), может принять, по крайней мере, 80 символов. По мере набора символов на клавиатуре они вводятся в строку. Если набирается забой, счетчик t уменьшится на 1. При нажатии на ввод помещается нулевой символ в конец строки, то есть строка оканчивается. Поскольку массив, используемый при вызове xgets(), модифицируется, после возврата он будет содержать набранные символы.