Чтение онлайн

на главную - закладки

Жанры

Шрифт:

стандартные и определенные пользователем преобразования тпов. Есть особые правила для передачи векторов (#4.6.5), средство передавать параметр без проверки типа параметра (#4.6.8) и средство для задания параметров по умолчанию (#4.6.6). Рассмотрим

void f(int val, int amp; ref) (* val++; ref++; *)

Когда вызывается f, val++ увеличивает локальную копию первого фактического параметра, тогда как ref++ увеличивает второй фактический параметр. Например:

int i = 1; int j = 1; f(i,j);

увеличивает j, но не i. Первый параметр – i,

передается по значению, второй параметр – j, передается по ссылке. Как уже отмечалось в #2.3.10, использование функций, которые именяют переданные по ссылке параметры, могут сделать програму трудно читаемой, и их следует избегать (но см. #6.5 и #8.4). Однако передача большого объекта по ссылке может быть гораздо эффективнее, чем передача его по значению. В этом случае параметр можно описать как const, чтобы указать, что ссылка применяется по соображениям эффективности, а также чтобы не позволить вызываемой функции изменять значение обекта:

void f(const large amp; arg) (* // значение «arg» не может быть изменено *)

Аналогично, описание параметра указателя как const соощает читателю, что значение объекта, указываемого указателем, функцией не изменяется. Например:

extern int strlen(const char*); // из «string.h» extern char* strcpy(char* to, const char* from); extern int strcmp(const char*, const char*);

Важность такой практики возрастает с размером программы.

Заметьте, что семантика передачи параметров отлична от семантики присваивания. Это важно для const параметров, сслочных параметров и параметров некоторых типов, определяемых пользователем (#6.6).

4.6.4 Возврат значения

Из функции, которая не описана как void, можно (и долно) возвращать значение. Возвращаемое значение задается опратором 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; *)

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

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

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

int* f (* int local = 1; // ... return amp;local; // так не делайте *)

Эта ошибка менее обычна, чем эквивалентная ошибка при использовании ссылок:

int amp; f (* int local = 1; // ... return local; // так не делайте *)

К счастью, о таких возвращаемых значениях

предупреждает компилятор. Вот другой пример:

int amp; f (* return 1;*) // так не делайте

4.6.5 Векторные параметры

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

int strlen(const char*);

void f (* char v[] = «a vector» strlen(v); strlen(«Nicholas»); *);

Иначе говоря, при передаче как параметр параметр типа T[] преобразуется к T*. Следовательно, присваивание элементу векторного параметра изменяет значение элемента вектора, кторый является параметром. Другими словами, вектор отличается от всех остальных типов тем, что вектор не передается (и не может передаваться) по значению.

Размер вектора недоступен вызываемой функции. Это может быть неудобно, но эту сложность можно обойти несколькими спсобами. Строки оканчиваются нулем, поэтому их размер можно легко вычислить. Для других векторов можно передавать второй

параметр, который задает размер, или определить тип, содержщий указатель и индикатор длины, и передавать его вместо просто вектора (см. также #1.11). Например:

void compute1(int* vec_ptr, int vec_size); // один способ

struct vec (* // другой способ 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“; *) *)

Матрица, конечно, все равно передается как указатель, а размерности используются просто для удобства записи.

Первая размерность массива не имеет отношения к задаче поиска положения элемента (#2.3.6). Поэтому ее можно передвать как параметр:

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), но нпохоже, чтобы это имел в виду программист. Вот правильное решение:

Поделиться:
Популярные книги

Наследник, скрывающий свой Род

Тарс Элиан
2. Десять Принцев Российской Империи
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Наследник, скрывающий свой Род

Как я строил магическую империю 6

Зубов Константин
6. Как я строил магическую империю
Фантастика:
попаданцы
аниме
фантастика: прочее
фэнтези
5.00
рейтинг книги
Как я строил магическую империю 6

Вечный. Книга I

Рокотов Алексей
1. Вечный
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Вечный. Книга I

Охотник на демонов

Шелег Дмитрий Витальевич
2. Живой лёд
Фантастика:
боевая фантастика
5.83
рейтинг книги
Охотник на демонов

Лекарь Империи 7

Карелин Сергей Витальевич
7. Лекарь Империи
Фантастика:
городское фэнтези
аниме
боевая фантастика
попаданцы
5.00
рейтинг книги
Лекарь Империи 7

Тринадцатый IV

NikL
4. Видящий смерть
Фантастика:
боевая фантастика
попаданцы
5.00
рейтинг книги
Тринадцатый IV

Газлайтер. Том 4

Володин Григорий
4. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 4

Газлайтер. Том 29

Володин Григорий Григорьевич
29. История Телепата
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Газлайтер. Том 29

Камень. Книга пятая

Минин Станислав
5. Камень
Фантастика:
боевая фантастика
6.43
рейтинг книги
Камень. Книга пятая

Имя нам Легион. Том 19

Дорничев Дмитрий
19. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Имя нам Легион. Том 19

На границе империй. Том 10. Часть 9

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 9

Студент из прошлого тысячелетия

Еслер Андрей
2. Соприкосновение миров
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Студент из прошлого тысячелетия

Страж Кодекса. Книга VII

Романов Илья Николаевич
7. КО: Страж Кодекса
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Страж Кодекса. Книга VII

Имя нам Легион. Том 14

Дорничев Дмитрий
14. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
фантастика: прочее
5.00
рейтинг книги
Имя нам Легион. Том 14