Программирование C++. Пензенский государственный университет введение в программирование на языке с лабораторный практикум пенза 2010 г 2
Скачать 1.47 Mb.
|
using std::cout; using std::endl; int main() { int i =10, j =20; int & rfc = i; cout< int * ptr = &i; cout<<*ptr< //10 ptr = &j; cout<<*ptr< //20, изменилось значение указателя cout<< i < //10, значение i не изменилось rfc = j; cout< //20, изменилось значение данных cout<< i < //20, значение i изменилось, так как rfc по-прежнему ссылается на i; system( "pause" ); return 0; } Указатели и массивы Имя массива – это указатель- константана начало массива. Таким образом доступ к элементу массива можно записать через смещение от начала массива. Например для массива b , операция разыменования *(b+3) – это тоже, что и операция индексирования b[3] взгляните еще раз например первой программы во введении. Для элемента двумерного массива data[row][col] операция индексирования переводится компилятором в операцию разыменования указателей *( *(data+row)+col). Описание int (*data1)[20] определяет указатель нам асс ив из целых значений. Описание int *data1[20] определяет массив из указателей на целые значения. Начальный адрес массива определяется компилятором в момент описания массива, и такой адрес немо же т быть переопределен. Это делает невозможным копирование массивов с помощью простого оператора присваивания (как в языке Pascal ). Наример: const int ArraySize=5; int a[ArraySize] = {12, 3, 42, 5, 34}; int b[ArraySize]; b=a; // ОШИБКА Попытка изменить константный указатель b, // сообщение компилятора C++ Microsoft Visual Studio: // «error C2106: '=' : left operand must be l-value», // те. b должно быть изменяемой величиной Динамическое размещение массива Указатели используются для размещения массива в динамическойобласти оперативной памяти – куче. Для выделения памяти применяется оператор – new тип[размер массива , для освобождения – delete []. Например int * ptrArray1 = new int [5]; // ptrArray1 – начальный адрес массива из 5 элементов . . . // использование массива ptrArray1 delete [] ptrArray1; освобождение памяти с начальным адресом ptrArray1 37 Пример Указатели и массивы using std::cout; using std::endl; int main() { int Array[5] = {10, 20, 30, 40, 50}; статический массив – память резервируется при компиляции int * ptrArray1 = new int [5]; // динамические массивы – память резервируется int * ptrArray2 = new int [5]; // в куче при выполнении программы Размер массива. В случае динамического массива получаем размер указателя cout<< "SizeofArray " << sizeof (Array) < cout<< "SizeofPtrArray " << sizeof (ptrArray1)< for ( int i=0; i<5; ++i){ копируем значения изв используя для доступа к значениям cout<<*(ptrArray1+i)< // массивов разыменование указателя } for ( int i=0; i<5; i++) { копируем значения изв используя для доступа к значениям cout< // массивов имя массива и номер элемента } delete [] ptrArray1; освобождаем память, delete [] ptrArray2; выделенную для ptrArray1 и ptrArray2 system( "pause" ); return 0; } Пример Указатели и массивы символов using std::cout; using std::endl; int main() { char string1[10], *string2 = "Hello" , string3[10], *string4 = "GoodBye" , *tmp; копирование string2 в string1, используя обозначение массива for ( int i=0; (string1[i]= string2[i]) != '\0' ; ++i) ; cout<< "string1:" < tmp=string3; используем tmp, т.к. нельзя string3++ for ( ; (*tmp = *string4) != '\0' ; tmp++, string4++) ; cout<< "string3:" < system( "pause" ); return 0; } 38 Пример Указатели и массивы символов #include #include using std::endl; using std::cout; int main() { // использование функции strtok для определения слов в предложении // разделитель слов - пробел char str[] = Это предложение из 5 слов char *tkPtr; указатель на слово tkPtr = strtok(str, " " ); while (tkPtr != NULL) { cout< tkPtr=strtok(NULL, " " ); } system( "pause" ); return 0; Указатели и ссылки как параметры Передача параметров по адресу Указатели и ссылки используются для передачи параметров по адресу. Схема обмена информацией вызывающей программы и функции с использованием передачи параметра по адресу приведена на рисунке 3.1. В этом случае формальный параметр обрабатывается в функции как переменная, адрес которой есть адрес соответствующего фактического параметра. Рисунок 3.1 – Передача параметра по адресу В операторе вызова фактическим параметром может быть только переменная. Любое изменение формального параметра есть изменение соответствующего ему фактического параметра. После завершения работы функции f значение равно, так как в функции выполнен оператора Пример 3_5_1. Перестановка значений Вариант 1 #include using std::cout; using std::endl; void swap_values( int * a, int * b) // формальный параметр - указатель int temp=*a; // требуется разыменование указателей *b=temp; } int main() { int x=10, y=20; swap_values(&x, &y); // требуется операция & , так как // параметры передаются по адресу cout< " " < // результат 20 10 system( "pause" ); return 0; } Вариант 2 #include using std::cout; using std::endl; void swap_values( int & a, int & b) // формальный параметр - ссылка int temp=a; // НЕ требуется разыменование указателей b=temp; } int main() { int x=10, y=20; swap_values(x, y); // НЕ требуется операция & cout< " " < // результат 20 10 system( "pause" ); return 0; } Передача массивов в качестве параметров Во многих случаях передавать значения в функции выгоднее по адресу, а не по значению. Особенно эффективна передача по адресу, если передаваемые данные имеют большой размер, например массивы данных. Однако, при этом досаточно частот ре буе т с яз а щит и т ь передаваемые данные от изменения. Для этого в прототипе функции используется не константный указатель на константные данные Пример 3_6_1. Передача массива в качестве параметра using std::endl; using std::cout; int summa( const int * x, const int n); // массив всегда передается по адресу необходимо передать и размер массива main() { const int ArraySize=5; // размер массива int a[ArraySize] = {1, 3, 4, 5, 2}; // инициализация массива for ( int i=0; i // вывод значений элементов cout< // массива cout << Сумма элементов массива " < system( "pause" ); return 0; } int summa( const int * x, const int n) // можно итак сумма значений элементов массива for ( int i=0; i // цикл вычисления суммы total+=x[i]; return total; } Структуры Структура – это тип данных, состоящий из фиксированного числа компонентов разного типа. Компоненты записи называют полями, и соответственно имена компонентов являются именами полей. Основной операцией над структурами является выбор компонентов. Эта операция подобна выбору элементов из массива, нос одним существенным отличием индекс здесь всегда является именем компонента и никогда не может быть вычисляемым значением. Синтаксически операция выбора компонента структуры представляет собой имя структуры, за которым следует имя компонента. Пример Тип Комплексное число #include using std::endl; using std::cout; struct Complex{ // struct – структура, Complex – имя структуры r, поле r – действительная часть i; поле i – мнимая часть }; int main() { Complex c2, *pc, c1={1,2}; инициализация с c1.r=1, c1.i=2 // операция выбора поля структуры c2.r = c1.r; // имя_структуры . имя_поля c2.i = c1.i; // например, c2.i : c2 – имя структуры, i – имя поля cout<< "r=" < " i=" < pc = new Complex; // определение указателя на структуру pc->r = -10; // операция выбора поля структуры 41 pc->i = 5; // имя_структуры -> имя_поля cout<< "r=" < r<< " i=" < i< delete pc; system( "pause" ); return 0; } Как видите, выбор полей структуры с помощью операции . или операции -> зависит от переменной типа struct и не имеет отношения к полям самой структуры. Раз уж мы заговорили об этом, правильные названия этих операторов ( . и -> ) – селекторы полей структуры. Как сказано водной умной книге по С (автор Дж. Элджер), если вы назовете их точкой или стрелкой на семинаре с коктейлями, наступит гробовая тишина, все повернутся и презрительно посмотрят на вас, а в дальнем углу кто-нибудь выронит свой бокал. Заметим также, что синтаксис описания структуры позволяет объявить переменные сразу в самом описании. Для рассмотренного примера переменные с, си указатель pc можно объявить такс, с Примеры программ 3.8.1 Стек как одномерный статический массив Стек – структура для хранения данных с дисциплиной обслуживания последним пришел, первым ушел (Last In First Out). Стек широко используется как на аппаратном, таки на программном уровне копьютера. О снов н ы е функции работы со стеком следующие push – помещает значение в стек pop – извлекает значение и удаляет его из стека full – возвращает значение истина, если стек заполнен empty – возвращает значение истина, если стек пуст. Реализуем стек как структуру ( struct ), которая для хранения данных использует символьный (в данном примере) массив длиной max_len рисунок 3.2). Рисунок 3.2 – Стек 42 Пример Стек символов Файл Stack_1.h определение стека и его операций _STACK1_H предотвращение многократного #define _STACK1_H включения заголовочного файла //Данные const int Max_len = 100; enum {EMPTY = -1, FULL = Max_len -1}; struct ch_stack { char s[Max_len]; массив символов top; значение номера верхушки стека Операции со стеком void reset(ch_stack* stk) { очистка стека = EMPTY; } void push(ch_stack* stk, char c) { stk->s[++stk->top] = c; приоритет stk->top, ++, [ ], = } char pop(ch_stack* stk) { return (stk->s[stk->top--]); приоритет stk->top, [ ], stk->top -- } bool empty( const ch_stack* stk) { return (stk->top == EMPTY); } bool full( const ch_stack* stk) { return (stk->top == FULL); } #endif _STACK1_H Файл Stack_1.cpp // использование стека включение описания стека #include using std::endl; using std::cout; int main() { ch_stack st; // st - стек char str[]= "12345" ; // str - строка, символы которой помещаются в стек int i=0; cout< // вывод str reset(&st); // очистка стека while (str[i] && !full(&st)) // пока символ не '\0' истек не заполнен push(&st, str[i++]); // поместить символ в стек while (!empty(&st)) // пока стек не пуст cout< // выбрать символ из стека и вывести на экран cout< system( "pause" ); return 0; } Результат работы 43 3.8.2 Динамический двумерный массив В основе реализации динамического двумерного массива лежит следующая структура данных (рисунок 3.3) Рисунок 3.3 – Динамический двумерный массив Указатель base содержит начальный адрес массива указателей, каждый элемент которого является начальным адресом массива данных. При этом указатель base является указателем на указатель на тип данных. N – число строк в двумерном массиве. M – число столбцов в двумерном массиве. Пример Двумерный динамический массив Файл определение динамического массива и его операций Н предотвращение многократного #define Н включения заголовочного файла #include using std::endl; using std::cout; using std::cin; Данные struct array2d { int ** base; базовый адрес массива – двойной указатель int N, M; количество строки столбцов }; Операции с массивом создание массива размерность r c void allocate( const int r, const int c, array2d& x){ x.base = new int *[r]; выделить память для массива указателей на int for ( int i=0; i x.base[i]= new int [c]; выделить память для массива целых чисел x.N = r; x.M = c; } удаление массива void deallocate(array2d& x){ for ( int i=0; i delete [] x.base[i]; delete [] x.base; x.base=0; x.N = 0; x.M = 0; } 44 вывод значений элементов массива по строкам void print( const array2d& x){ for ( int i=0; i for ( int j=0; j cout<< " " < cout< } } ввод значений элементов массива по строкам void input( const array2d& x){ for ( int i=0; i for ( int j=0; j cin>>x.base[i][j]; cout< } поиск максимального значения массива int find_max( const array2d& x){ int i, j, max=x.base[0][0]; |