Опубликован: 10.10.2006 | Доступ: свободный | Студентов: 6059 / 460 | Оценка: 4.26 / 3.88 | Длительность: 31:30:00
Лекция 4:

Функции

4.6.4 Возвращаемое значение

Если функция не описана как void, она должна возвращать значение. Например:

int f() { }    // ошибка
void g() { }   // нормально

Возвращаемое значение указывается в операторе return в теле функции. Например:

int fac(int n) { return (n>1) ? n*fac(n-1) : 1; }

В теле функции может быть несколько операторов return:

int fac(int n)
    {
      if (n > 1)
 return n*fac(n-1);
      else
 return 1;
    }

Подобно передаче параметров, операция возвращения значения функции эквивалентна инициализации. Считается, что оператор return инициализирует переменную, имеющую тип возвращаемого значения. Тип выражения в операторе return сверяется с типом функции, и производятся все стандартные и пользовательские преобразования типа. Например:

double f()
       {
 // ...
 return 1;   // неявно преобразуется в double(1)
       }

При каждом вызове функции создается новая копия ее формальных параметров и автоматических переменных. Занятая ими память после выхода из функции будет снова использоваться, поэтому неразумно возвращать указатель на локальную переменную. Содержимое памяти, на которую настроен такой указатель, может измениться непредсказуемым образом:

int* f()
{
int local = 1;
// ...
return &local;    // ошибка
}

Эта ошибка не столь типична, как сходная ошибка, когда тип функции - ссылка:

int& f()
{
int local = 1;
// ...
return local;   // ошибка
       }

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

int& f() { return 1; }  // ошибка

4.6.5 Параметр-массив

Если в качестве параметра функции указан массив, то передается указатель на его первый элемент. Например:

int strlen(const char*);

 void f()
 {
char v[] = "массив";
strlen(v);
strlen("Николай");
 }

Это означает, что фактический параметр типа T[] преобразуется к типу T*, и затем передается. Поэтому присваивание элементу формального параметра-массива изменяет этот элемент. Иными словами, массивы отличаются от других типов тем, что они не передаются и не могут передаваться по значению.

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

void compute1(int* vec_ptr, int vec_size);  // 1-ый способ

 struct vec {   // 2-ой способ
int* ptr;
int size;
 };

 void compute2(vec v);

Сложнее с многомерными массивами, но часто вместо них можно использовать массив указателей, сведя эти случаи к одномерным массивам. Например:

char* day[] = {
"mon", "tue", "wed", "thu", "fri", "sat", "sun"
};

Теперь рассмотрим функцию, работающую с двумерным массивом - матрицей. Если размеры обоих индексов известны на этапе трансляции, то проблем нет:

void print_m34(int m[3][4])
{
for (int i = 0; i<3; i++) {
   for (int j = 0; j<4; J++)
       cout << ' ' << m[i][j];
   cout << '\n';
}
}

Конечно, матрица по-прежнему передается как указатель, а размерности приведены просто для полноты описания. Первая размерность для вычисления адреса элемента неважна, поэтому ее можно передавать как параметр:

void print_mi4(int m[][4], int dim1)
       {
for ( int i = 0; i<dim1; i++) {
  for ( int j = 0; j<4; j++)
      cout << ' ' << m[i][j];
  cout << '\n';
}
       }

Самый сложный случай - когда надо передавать обе размерности. Здесь "очевидное" решение просто непригодно:

void print_mij(int m[][], int dim1, int dim2)   // ошибка
      {
for ( int i = 0; i<dim1; i++) {
for ( int j = 0; j<dim2; j++)
    cout << ' ' << m[i][j];
cout << '\n';
}
     }

Во-первых, описание параметра m[][] недопустимо, поскольку для вычисления адреса элемента многомерного массива нужно знать вторую размерность. Во-вторых, выражение m[i][j] вычисляется как *(*(m+i)+j), а это, по всей видимости, не то, что имел в виду программист. Приведем правильное решение:

void print_mij(int** m, int dim1, int dim2)
{
for (int i = 0; i< dim1; i++) {
   for (int j = 0; j<dim2; j++)
     cout << ' ' << ((int*)m)[i*dim2+j];  // запутано
   cout << '\n';
}
}

Выражение, используемое для выбора элемента матрицы, эквивалентно тому, которое создает для этой же цели транслятор, когда известна последняя размерность. Можно ввести дополнительную переменную, чтобы это выражение стало понятнее:

int* v = (int*)m;
// ...
v[i*dim2+j]

Лучше такие достаточно запутанные места в программе упрятывать. Можно определить тип многомерного массива с соответствующей операцией индексирования. Тогда пользователь может и не знать, как размещаются данные в массиве.

Дарья Федотова
Дарья Федотова
Сергей Березовский
Сергей Березовский

В рамках проф. переподготовки по программе "Программирование"

Есть курсы, которые я уже прошел. Но войдя в курс я вижу, что они не зачтены (Язык Ассемблера и архитектура ЭВМ, Программирование на С++ для профессионалов). Это как?

Роман Островский
Роман Островский
Украина
Оксана Пагина
Оксана Пагина
Россия, Москва