- •Аннотация
- •Предисловие
- •Г л а в а 1. Введение в указатели
- •§1. Понятие указателя. Операции разыменования и разадресации
- •§2. Инициализация и присваивание указателей
- •§3. Передача параметров функций с помощью указателей.
- •§4. Распределение динамической памяти.
- •4.1. Операция new
- •4.2. Операция delete
- •Упражнения, тесты.
- •Указатели и массивы
- •§1. Связь указателей и массивов
- •1.1. Указатели и одномерные массивы
- •1.2. Указатели и матрицы
- •Int MyFun (int *X, int n)
- •§3. Операции над указателями при работе с массивами.
- •3.1. Арифметические операции
- •3.2. Операции сравнения.
- •§4. Использование операций над указателями при работе с одномерными массивами
- •4.1. Использование индексов
- •4.2. Указатель в качестве параметра цикла
- •4.3. Использование указателя и индекса
- •§5. Строки.
- •5.1. Общая характеристика строк.
- •5.2. Примеры алгоритмов работы со строками.
- •5.3. Анализ строковых функций.
- •§6. Использование операций над указателями при работе со статической матрицей.
- •Упражнения, тесты.
- •Массивы указателей
- •§1. Статический массив указателей
- •§2. Частично динамическая матрица.
- •Int arr_of_size[n];
- •§3. Статический массив строк
- •§4. Динамический массив указателей
- •4.1. Указатель на указатель
- •4.2. Динамические “матрицы”.
- •Int *arr_of_size;
- •4.3. Передача матрицы в функцию
- •Int a[10]; FunArr1(a, 10,…);
- •Упражнения, тесты.
- •Задачи второго среднего уровня.
- •Структуры и другие типы, определяемые пользователем
- •§1. Структуры
- •Объявление структуры
- •1.2. Работа со структурой.
- •1.3. Вложенные структуры и статические массивы в структурах
- •1.4. Статический массив структур
- •§2. Cтруктуры и указатели
- •2.1. Указатели в структуре.
- •2.2. Указатели на структуру
- •2.3. Динамический массив структур
- •2.4. Ссылка на структуру.
- •2.5. Указатели и вложенные структуры
- •§3. Cтруктуры и функции
- •3.1. Передача полей структуры в функцию.
- •Void MyFun1 (int X, float &y, int *u1, float *u2, char *s);
- •3.2. Передача всей структуры в функцию
- •Void Fun1 (tst s,…);
- •Void Fun2 (tst & s,…);
- •Void Fun3 (tst* s,…);
- •§4. Cтруктуры и классы.
- •§5. Объединения.
- •Представление вещественных чисел в памяти компьютера.
- •§6. Поля битов (битовые поля)
- •Ввод в ы в о д
- •Symbol Code16 Code10 Code2
- •§7. Перечисления
- •Какие из строк (//1 – //9) правильные?
4.2. Указатель в качестве параметра цикла
Параметром цикла может быть переменная-указатель.
void MySum2(int* ar, unsigned size, int &S)
{ int *p=ar; S=0;
for ( ; p<= ar+(size-1); p++) S+=*p;
}
Если бы дополнительно указатель p для цикла не использовали, а ограничились бы только переменной ar, то мы потеряли бы доступ к началу массива, то есть его адрес. Более того, для статического массива адрес его начала (имя массива) компилятор не разрешит менять. Поэтому и в одном и в другом случаях надо объявить и использовать другой указатель (в функции таким является p) для использования его в цикле.
Можно цикл записать и так: for ( ; p<=&ar[size-1]; p++) S+=*p; Вариант, записанный в функции, лучше, так как мы “помогли компьютеру” найти адрес последнего элемента массива и не требуем, чтобы система преобразовывала индексное выражение. Можно объявить и проинициализировать ещё один указатель int *q=ar +size-1; и использовать его в заголовке цикла: for (; p<=q; p++) S+=*p;
Правильно будет работать и следующий вариант функции
void MySum3 (int *ar, unsigned size, int *uS)
{ int *p, *q=&ar[0]+size-1;
for (*uS=0, p=ar; p<=q;p++) *uS+=*p;
}
Здесь начальное значение указателя p определили в заголовке цикла, который можно записать и так: for ( *uS=0, p=&ar[0]; p<=q; p++) … Но вариант в функции компьютеру больше понравится. Подумайте, почему. Кроме этого, результат функции получаем с помощью указателя uS. Поэтому в тексте функции для uS используется операция “*”.
Заметим, что во всех этих вариантах вместо условия p<= ar+size-1; в заголовке цикла можно записать p< ar+size; , и такой вариант будет правильно работать. Но так как ar+size — это адрес “не нашей”, то есть не принадлежащей нашему массиву ячейки, то первое условие в виде нестрогого неравенства предпочтительнее.
4.3. Использование указателя и индекса
Когда непросто определить условие выхода из цикла с помощью указателя, то в заголовке оператора цикла можно использовать как переменную-указатель, так и индекс.
void MySum4 (int *ar, unsigned size, int *uS)
{ int *p=ar; unsigned i;
for (*uS=0, i=0; i<size; p++, i++) *uS+=*p;
}
В этом варианте надо не забыть изменить значение как указателя, который будет использоваться для доступа к элементу массива, так и значение индекса, используемое для выхода из цикла.
Заголовок функции и их вызов не зависят от того, какой вариант использовали при организации цикла. Разные виды параметра функции для результата приведены только с целью повторения. Для проверки рассмотренных выше вариантов добавим функции для ввода и вывода массива.
void MyInp ( int *ar, unsigned size)
{ int *p=ar;
for (int i=0; i<size; i++) *(p+i)=random(20)-10;
}
void MyOut ( int *ar, unsigned size)
{ int *q=ar+size-1; cout<<endl;
for (; ar<=q;ar++) cout<<(*ar)<< " ";
cout<<endl;
}
int main()
{ // a) Работа со статическим массивом
const n=5; int a[n] ={1,2,3,4,5}, S2, S3;
cout<<MySum0(a,n)<<" "<<MySum1(a, n)<< endl;
MySum2(a,n, S2); cout<<S2<<" ";
MySum3(a,n, &S3); cout<<S3<<" ";
int *S4= new int;
MySum4(a,n, S4); cout<<*S4<<" "<< endl;
// b) Использование функций для динамического массива
randomize();
unsigned N; N=random(10)+2;
int *aD=new int[N];
MyInp(aD, N); MyOut(aD,N);
cout<<MySum0(aD,N)<<" "<<MySum1(aD, N)<< endl;
MySum2(aD,N, S2); cout<<S2<<" ";
MySum3(aD,N, &S3); cout<<S3<<" ";
MySum4(aD,N, S4); cout<<*S4<<" ";
getch(); return 0;
}
Обратите внимание на вызов функций MySum3 и MySum4. Особенности их вызова по сравнению с MySum2 связаны не с тем, как организован цикл в этих функциях, а с тем, что одно целочисленное значение возвращается из функции с помощью указателя, а не с помощью переменной ссылочного типа. Для лучшего понимания этих вариантов повторите § 3 главы 1.