Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга_2013_2_часть+.doc
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
816.64 Кб
Скачать

Упражнения, тесты

        1. Пусть int t=10; int *p=&t; Какие из следующих операторов допустимы и что они означают?

1) cin>>p; 2) cin>>(*p); 3) cout<< p; 4) cout<<(*p); 5) p=1000; 6)*p=1000; 7) t*=*p; 8) t*=p; 9) t=*p; 10) t=p; 11) cout <<(*p*=2)?

2. Дан код:

void fun2 (int *x, int &y) { (*x)++; y--; } //1

void main() { int a=5, b=2; fun2 (&a, b); //2

cout<< a<< “ “<< b; getch() ; }

Что будет выведено?

Варианты ответов: 1) 6 1; 2) 5 1; 3) 6 2; 4) 5 2; 5) Ошибка в //1; 6) Ошибка в //2; 7) Ошибки в //1 и //2.

3. Дан код:

void fun3 (int x, int *y) { *y=x+10; } //1

void main() { int a=5, b=2; int* p=&b; /*2 */

fun3 (a,*p); /*3*/

cout<< a<< “ “<< b; getch() ; }

Что будет выведено?

Варианты ответов: 1) 5 15 ; 2) 15 2; 3) 5 2; 4) Ошибка в //1; 5) Ошибка в /*2*/; 6) Ошибки в /*3*/ .

4. Дан код:

void fun4 (int x, int *y) { * y=x*10; } //1

void main() { int a=5, *b=new int (2); //2

fun4 (????????); //3

cout<< a<< “ “<< (*b); getch() ; } //4

Как вызвать функцию в //3, чтобы вывести 5 50?

Варианты ответов:

1) fun3(a,b); 2) fun3(a, &b); 3) fun3(&a, b); 4) fun3(&a, &b); 5) fun3(a, *b); 6) fun3(*a, b); 7) fun3(*a, *b); 8) ошибки (указать номера строк).

5. Дан код:

void fun5(int &x, int *y) { ????????????? ; } //1

void main() { int a=5, *b=new int; //2

fun5 (a,b); //3

cout<<endl<<a<<" "<<(*b)<<endl; //4

getch(); }

Что записать в тексте функции в строке //1, чтобы вывести 5 15?

Варианты ответов:

1) y=x+10; 2) *y=*x+10; 3) *y=x+10; 4) y=*x+10;

5) ошибка в //2; 6) ошибка в //3; 7) ошибка в //4.

6. Дан код:

int *p1= new int = 11; int *p2; *p2= new int =11;

int *p3= new int[11]; int *p4= new int (11);

int *p5= new (int) 11; int *p6= new int :11;

Для каких указателей (p1p6) правильно резервируется память для одной целочисленной переменной и выполняется её инициализация?

Г л а в а 8 одномерные массивы, указатели и функции

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

§ 1. Связь указателей и одномерных массивов. Передача массива в качестве параметра функции

Рассмотренное понятие указателя усложняется ещё и тем, что для массивов указатели имеют некоторые особенности. Пусть мы объявили статический массив, то есть массив с фиксированной размерностью: const n=10; int A[n]; Этим самым на этапе компиляции выделяется непрерывный участок памяти из 4*n =40 байт для размещения n целых чисел. Кроме этого, без какого-либо дополнительного объявления, без использования символа “*” мы объявили и переменную-указатель с именем массива A, в которой находится адрес начала массива, т. е. номер первого байта элемента A[0]. Этот адрес можно в программе обозначить ещё и как &A[0].

Идентификатор массива определяется не просто как указатель, а как константный указатель на первый элемент массива. Это означает, что нельзя изменить адрес начала массива, то есть нельзя, например, переменной А присвоить значение другого адреса. Если int *q; то запись A=q;недопустима. Но обратное присваивание q=A; или q=&A[0]; разрешено. После такого присваивания элементы одного и того же массива можно обозначать не только A[i], но и q[i], где i = 0, 1, 2, …, n-1. Более того, переменной указателю q можно присвоить адрес любого, не обязательно первого с нулевым номером элемента массива. Например, после присваивания q=&A[5]; q[0] и A[5] — это одна и таже ячейка, один и тот же элемент массива с разными именами, q[1] — это A[6], и т. д., q[4] — это A[9].

Сказанное выше используется при передаче массива в качестве параметра функции. Независимо от того, является массив входным, выходным, или и тем и другим, то есть преобразуется, он передаётся одинаково в качестве параметра функции с помощью указателей по следующим правилам.

Заголовок функции можно записать по-разному. Например, функцию сортировки одномерного массива можно объявить двумя способами:

a) void MySort(int x[], int size); или b) void MySort(int *x, int size);

В заголовке функции указываем тип элементов массива, его имя и пустые квадратные скобки или указатель на массив, что одно и то же. Более того, в прототипе можем записать int x[], а в заголовке при определении этой же функции — int * x, или наоборот. Чаще всего в качестве параметра указывается и его размерность (size в примере). Этим самым мы подчёркиваем, что работаем с массивом произвольной размерности. Отметим, что из заголовка ещё не видно, что x — адрес массива, а не простой переменной.

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

Вызов такой функции выполняется одинаково и не зависит от вариантов прототипа (a) или b)). В вызывающей функции, например, в main, объявляем статический массив обычным образом const n=10; int A[n]; При вызове функции в качестве фактического параметра надо записать адрес начала массива, если обрабатываем его с самого начала. Поэтому в скобках в таком случае записываем только имя массива A или адрес его первого элемента &A[0], что одно и то же: MySort(A,n); или MySort(&A[0],n); Будет ошибка, если при вызове функции в качестве фактического параметра запишем, например, A[i], или A[n] (так записываем при объявлении массива), или A[] (так записываем при определении формального, а не фактического параметра). То есть следующие вызовы компилятор не пропустит:

MySort(A[i],n); ошибка, так как A[i] при i=0, 1, ... , n-1— это число или элемент массива, а не адрес;

MySort(A[n],n); — ошибка, так как A[n] это не адрес и, более того, n превосходит наибольшее значение индекса, так как нумерация элементов массива с нуля;

MySort(A[],n); — неправильно;

MySort(*A, n); — тоже неправильно;

При использовании массива в качестве параметра функции фактически передаётся не сам массив, а указатель на него, или адрес массива, то есть номер первого байта первой ячейки массива (с индексом 0). Другими словами, int x[] (или int *x) в заголовке функции означает, что ячейка x предназначена для хранения адреса массива. При вызове функции из объявления int A[n] следует, что A, или &A[0], — это адрес начала массива из n элементов. Как и для простых переменных, этот адрес (но не сам массив) копируется в ячейку x. В результате получается, что в A и в x хранятся адреса одной и той же области оперативной памяти. Другими словами, массив в памяти не дублируется, а его элементы по-разному называются: A[0] в main — это та же ячейка, что и x[0] в MySort, A[1] — это та же ячейка, что и x[1], и так далее. Поэтому если в функции элементы массива с именем x изменим, то этим самым мы изменим и элементы массива A. Поэтому с помощью указателя можно не только передать массив в функцию, но и вернуть из неё вновь полученный или изменённый массив.

Никакого ссылочного типа для возврата массива из функции не требуется.

Благодаря указателям составленную функцию для работы с одномерным массивом можно без изменений использовать для обработки части массива. Например, для сортировки n/2 первых элементов массива ту же функцию вызываем так: MySort (A, n/2); или MySort (&A[0], n/2); Заметим, что если n нечётное (например, n=15), то рассортируем первые n/2=7 элементов массива, то есть меньше половины.

Пусть надо рассортировать массив не с самого начала, а, например, с его середины и до конца. Тогда в качестве фактического параметра при вызове такой функции указываем адрес элемента a[n/2], а количество обрабатываемых элементов уменьшаем: MySort (&a[n/2], n%2 ? n/2+1 : n/2); Если n — нечётное (n=15), то, начав с a[n/2] (a[7]), мы рассортируем n/2+1=8 элементов, то есть больше половины. При чётном n n%2=0, что соответствует false, и рассортируем n/2 элементов.