КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Лекция 6
Do Begin Withv.BirthDate do Begin Withv do Begin Begin Withv do Begin Var Type Пример ... Record Begin Два кода, второй из которых также должен быть извлечен Код «0» означает, что клавиша отправила в буфер клавиатуры Begin Begin Else End Begin Begin IfmG[j] andmDP[i + j] andmDM[i - j] then Begin Begin Var Var c: char; n: integer; mG: array [1 .. 8] of boolean; // Вертикали mDP: array [2 .. 16] of boolean; // Диагонали, параллельные побочной mDM: array [-7 .. 7] of boolean; // Диагонали, параллельные главной x: array [1 .. 8] of integer;
procedure PutNextQueen (i: integer); j, k: integer; for j:= 1 to 8 do x[i]:= j;
if i < 8 then mG[j]:= false; mDP[i + j]:= false; mDM[i - j]:= false; PutNextQueen (i + 1); mG[j]:= true; mDP[i + j]:= true; mDM[i - j]:= true; for k:=1 to 8 do write(x[k]:5); writeln; inc(n); if n mod 20 = 0 then // Останавливать вывод после показа очередной порции из 20 вариантов c:= ReadKey; if c = #27 then halt; if c = #0 then c:= ReadKey; end; end; end; end; end;
for n:= 1 to 8 do x[n]:=0; for n:= 1 to 8 do mG[n]:= true; for n:= 2 to 16 do mDP[n]:= true; for n:= -7 to 7 do mDM[n]:= true; n:= 0; PutNextQueen (1); writeln(‘n=’, n); end.
Тип «Запись»
<Тип «Запись»> :: = <Список имён полей 1>: <Тип 1>; <Список имён полей 2>: <Тип 2>; end; <Обращение к полю переменой типа «Запись»> :: = <Имя переменной>. <Имя поля> или with <Имя переменной> do <Оператор, содержащий Имя поля>
{ TP7 } program P0701;
DateTime = record // Delphi: TDateTime Year, Month, Day, Hour, Min, Sec: word; end; MyRec = record N: longint; FIO: string [40]; BirthDate: DateTime; end; N: longint;
v1: MyRec; v2: record N: longint; FIO: string [40]; BirthDate: DateTime; end;
procedure Proc1(v: MyRec); Writeln(v. FIO); Writeln(N); Writeln(BirthDate.Day:2,’.’, BirthDate.Month:2,’.’, BirthDate.Year:4); end; end;
procedure Proc2(v: MyRec); Writeln(v. FIO); Writeln(N); Writeln(Day,’.’, Month,’.’, Year); end; end; N:= 2; v1. N:= 1; v1.FIO:= ‘Ivanov I.I.’; v1.BirthDate. Year:= 1950; v1.BirthDate. Month:= 10; v1.BirthDate. Day:= 2; Proc1(v1); // Нормально // Proc1(v2); // Ошибка Proc2(v1); // Нормально readln; end.
{ cout << “\nКупи слоника! “; cin >> answer; } while (answer!= ‘y’); return 0; } Цикл с параметром имеет следующий формат: for (инициализация; выражение; модификации) оператор В части инициализации можно записать операторы, разделенные запятой (операцией «последовательное выполнение»). Например: for (int i = 0, j = 2;... …………….. int k, m; for (k = 1, m = 0;... Областью действия объявленных в инициализации переменных является весь цикл. Инициализация выполняется один раз в начале выполнения цикла. Выражение определяет условие выполнения цикла: если его результат соответствует true, цикл выполняется. В этом смысле цикл с параметром аналогичен циклу с предусловием. Модификации выполняются после каждой итерации цикла и служат обычно для изменения параметров цикла. В части модификаций также можно записать несколько операторов через запятую. Простой или составной оператор представляет собой тело цикла. Любая из частей оператора for может быть опущена (точки с запятой остаются). Пример (нахождение всех делителей целого положительного числа): #include <iostream.h> int main () { int num, half, div; cout << “\nВведите число: “; cin >> num; for (half = num / 2, div = 2; div <= half; div++) if (!(num % div)) cout << div << “\n”; return 0; }
4.5.4. Операторы передачи управления
В C++ есть четыре оператора, изменяющих естественный порядок выполнения вычислений: · безусловный переход goto; · выход из цикла break; · переход к следующей итерации continue; · возврат из функции return. Применение этих операторов (особенно goto) противоречит структурному программированию, поэтому их рекомендуется использовать лишь при необходимости. В связи с этим в настоящем курсе вообще не рассматривается оператор goto. Оператор break используется внутри операторов цикла или switch для обеспечения перехода в точку программы, находящуюся непосредственно за оператором, внутри которого находится break. Оператор continue пропускает все операторы, оставшиеся до конца тела цикла, и передает управление на начало следующей итерации. Оператор возврата из функции return завершает выполнение функции и передает управление в точку, следующую за ее вызовом. Его вид таков: return [выражение]; Выражение должно иметь тип возвращаемого функцией значения. Если последний описан как void, выражение в return должно отсутствовать.
4.6. Указатели и массивы
4.6.1. Указатели
Указатель – это элемент данных (константа или переменная), предназначенный для хранения адресов областей памяти (констант, переменных, функций). Это не самостоятельный тип. Указатель всегда связан с некоторым конкретным типом, адреса которого он может содержать. Указатель на данные содержит адрес памяти, в которой хранятся данные определенного типа. Простейшее объявление такого указателя имеет вид: тип *имя; где тип может быть любым, кроме ссылки и битового поля. Звездочка относится к имени, поэтому для объявления нескольких указателей требуется ставить ее перед именем каждого из них. Например, в операторе int *a, b, *с; описаны два указателя на целое с именами а и c, а также целая переменная b. Указатель на функцию также содержит адрес. Он используется для косвенного вызова (не по имени функции, а посредством переменной, содержащей ее адрес), а также для передачи одной функции в другую функцию в качестве параметра. Его объявление имеет вид тип (*имя) (список_типов_аргументов); Например, объявление: int (*fun) (double, double); определяет переменную fun как указатель на функцию, возвращающую значение типа int и имеющую два аргумента типа double. Можно также определить указатель на указатель и т. д. Возможно определение указателя на void. Оно применяется в случае, когда конкретный тип адресуемых элементов не определен (например, если в одной и той же переменной возможно хранение адресов данных различных типов). Указателю на void можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями. Однако перед выполнением каких-либо действий с областью памяти, на которую он ссылается, требуется преобразовать его к конкретному типу явным образом. Рассмотрим примеры: int i; // целая переменная const int ci = 1; // целая константа int *pi; // указатель на целую переменную const int *pci; // указатель на целую константу int * const cp = &i; // указатель-константа на целую переменную const int * const cpc = &ci; // указатель-константа на целую константу void f (int a) { /* */ } // определение функции void (*pf) (int), // указатель на функцию pf = f; // присваивание адреса функции Указатели часто используются при работе с динамической памятью – кучей (от английского heap). Это свободная память, которую во время выполнения программы можно использовать в соответствии с ее потребностями. Доступ к выделенным участкам динамической памяти, называемым динамическими переменными, производится через указатели. Время жизни динамических переменных – от точки создания до конца программы или до явного освобождения памяти. В C++ используется два способа работы с динамической памятью. Первый использует функции mallос и free (достался в наследство от С), второй – операции new и delete. Указателю можно присваивать следующие значения: · адрес существующего элемента; · явный адрес области памяти; · пустое значение (0); · адрес выделенного участка динамической памяти; С указателями можно выполнять ряд операций: разадресация, или косвенное обращение к объекту (*), присваивание, сложение с константой, вычитание, инкремент/декремент (++/--), сравнение, приведение типов. При работе с указателями часто используется операция получения адреса (&). Операция разадресации предназначена для доступа к величине, адрес которой содержится в указателе. Например: char a; // переменная типа char char *p = new char, /* объявление указателя и его инициализация адресом динамической переменной типа char */ *p = ‘Ю’; a = *p; // присваивание значений обеим переменным На одну и ту же область памяти может ссылаться несколько указателей различного типа. Применяемая к ним операция разадресации может давать различные результаты. Например: # include <stdio.h> int main() { unsigned long int A = 0xCC77FFAA; unsigned short int *pInt = (unsigned short int *) &A; unsigned char *pChar = (unsigned char *) &A; printf (“ | %x | % x | %x |", A, *pInt, *pChar); return 0; } Результаты: | CC77FFAA | FFAA | AA | В этом примере при инициализации указателей были использованы операции приведения типов. Синтаксис операции явного приведения типа таков: перед именем переменной в скобках указывается тип, к которому ее требуется преобразовать. Поскольку при этом не гарантируется сохранение информации, то без необходимости не рекомендуется использовать явные преобразования типов. При смешивании в выражении указателей разных типов явное преобразование типов требуется для всех указателей, кроме void *. Указатель может неявно преобразовываться в значение типа bool (например, в выражении условного оператора), при этом ненулевой указатель преобразуется в true, а нулевой в false. Значение 0 (пустой указатель) неявно преобразуется к указателю любого типа. Арифметические операциинад указателями автоматически учитывают размер адресуемых величин. Так инкремент перемещает указатель к следующему элементу в памяти, декремент – к предыдущему. Если указатель на определенный тип увеличивается или уменьшается на константу, его значение изменяется на величину этой константы, умноженную на размер данного типа, например: short *р = new short [5]; р++; // значение р увеличивается на 2 p += 5; // значение р увеличивается на 10 long *q = new long [5]; q++; // значение q увеличивается на 4 Разность двух указателей – это разность их значений, деленная на размер типа в байтах. Суммирование двух указателей не допускается. Унарная операция получения адреса & применима лишь к величинам, имеющим имя и размещенным в оперативной памяти. Невозможно получить адрес выражения, неименованной константы или регистровой переменной. Наряду с указателем, в C++ имеется элемент данных ссылка (в C его не было). Ссылку можно рассматривать как указатель, который всегда автоматически разадресуется, и поэтому при работе с ней операция * не нужна. Формат объявления ссылки таков: тип & имя; где тип – это тип величины, на которую указывает ссылка, & – признак ссылки. Например: int kol, &pal = kol; // ссылка pal – альтернативное имя для kol const char & CR = '\n'; // ссылка на константу Имеют место следующие правила: · переменная-ссылка должна явно инициализироваться при описании, кроме случаев, когда она является параметром функции, описана как extern или ссылается на поле данных класса; · после инициализации ссылке не может быть присвоена другая переменная; · тип ссылки должен совпадать с типом величины, на которую она ссылается; · не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки. Ссылки чаще всего применяются в качестве параметров функций и типов возвращаемых функциями значений. Ссылки позволяют использовать в функциях переменные, передаваемые по адресу, без операции разадресации. Это улучшает читаемость программы. Ссылка, в отличие от указателя, не занимает дополнительного пространства в памяти и является просто другим именем (синонимом) величины. Операция над ссылкой приводит к изменению величины, на которую она ссылается.
4.6.2. Массивы
Конечная именованная последовательность однотипных величин называется массивом. Описание массива в программе отличается от описания простой переменной наличием после имени квадратных скобок, в которых задается количество элементов массива (размер). Например, float a[10]; // Вещественный массив из 10 элементов Элементы массива всегда нумеруются с нуля. При описании массива используются те же модификаторы (класс памяти, const и инициализатор), что и для простых переменных. Инициализирующие значения для массивов записываются в фигурных скобках. Значения элементам присваиваются по порядку. Если элементов в массиве больше, чем инициализаторов, элементы, для которых значения не указаны, обнуляются: int b[5] = {3, 2, 1}; // b[0]=3. b[l]=2, b[2]=l. b[3]=0, b[4]=0 Размер массива вместе с типом его элементов определяют объем памяти, необходимый для размещения массива. Оно выполняется на этапе компиляции, поэтому размер может быть задан только целой положительной константой или константным выражением. При описании массива размер можно не указывать, при этом должен присутствовать инициализатор, и компилятор выделит память по количеству инициализирующих значений. Для доступа к элементу массива после его имени указывается номер элемента (индекс) в квадратных скобках. В следующем примере подсчитывается сумма элементов массива. #include <iostream h> int main () { const int n = 10; int i, sum; int marks[n] = {3, 4, 5, 4, 4}; for (i = 0, sum = 0; i < n; i++) sum += marks[i]; cout << "Сумма элементов- " << sum; return 0; } При обращении к элементам автоматический контроль выхода индекса за границу массива не производится, что может приводить к ошибкам. Идентификатор массива является константным указателем на его нулевой элемент. Например, для массива из предыдущего имя marks – это то же самое, что &marks[0], а к i-му элементу массива можно обратиться, используя выражение *(marks+i). Можно описать указатель, присвоить ему адрес начала массива и работать с массивом через указатель. Например: int a[100], b[100]; ……………………………………………… int *pa = а; // или int *рa = &а[0]; int *pb = b; for (int i = 0; i < 100; i++) *pb++ = *pa++; // или pb[i] = pa[i]; Динамические массивы создаются с помощью операции new, при этом необходимо указать тип и размер, например: int n = 100; float *p = new float [n]; Преимущество динамических массивов в том, что их размер может быть переменным, то есть нужный объем памяти определяется на этапе выполнения программы. Доступ к элементам динамического массива осуществляется так же, как и статического. Например, к 5-му элементу приведенного выше массива можно обратиться как р[5] или *(р+5). Альтернативный способ создания динамического массива – использование функции malloc библиотеки С: int n = 100; float *q = (float *) malloc (n * sizeof (float)); Поскольку функция malloc возвращает значение указателя void*, то требуется явное преобразование типа. Память, зарезервированная с помощью new [], должна освобождаться операцией delete [], а память, выделенная функцией malloc – посредством функции free, например: delete [] p; free (q); Многомерные массивы задаются указанием каждого измерения в квадратных скобках, например, оператор int matr [6][8]; объявляет двумерный массив из 6 строк и 8 столбцов. В памяти такой массив располагается в последовательных ячейках построчно. Многомерные массивы размещаются так, что при переходе к следующему элементу раньше изменяется последний индекс. Для доступа к элементу массива указываются все его индексы, например, matr [i][j], *(matr[i] + j) или *(*(matr + i) + j). Количество индексов массива называется его размерностью. При инициализации многомерного массива он представляется либо как массив из массивов, при этом каждый массив заключается в свои фигурные скобки (в этом случае размеры при описании можно не указывать), либо задается общий список элементов в том порядке, в котором элементы располагаются в памяти: int mass1 [][] = { {1, 1}, {0, 2}, {1, 0} }; int mass2 [3][2] = {1, 1, 0, 2, 1, 0}; Пример. Программа определяет в целочисленной матрице номер строки, которая содержит наибольшее количество нулевых элементов. #include <stdio.h> int main () { const int nstr = 4, nstb = 5; // размеры массива int b[nstr][nstb]; // описание массива int i, j:
for (i = 0; i < nstr; i++) for (j = 0; j < nstb; j++) scanf (“%d”, &b[i][j]); // ввод массива
int istr = -1, MaxKol = 0; for (i = 0; i < nstr; i++) // просмотр массива по строкам { int Kol = 0; for (j = 0; j < nstb; j++) if (b[i][j] == 0) Kol++; // Можно if (!b[i][j]) … if (Kol > MaxKol) {istr = i; MaxKol = Kol;} }
printf (“Исходный массив\n”); for (i = 0; i < nstr; i++) { for (j = 0; j < nstb; j++) printf (“%d “, b[i][j]); printf ("\n"); } if (istr == -1) printf ("Нулевых элементов нет"); else printf (“Номер строки: %d”, istr); return 0; } 4.6.3. Строки
Строка представляет собой массив символов, заканчивающийся нулем (символом с кодом 0). Нуль-символ при необходимости записывается в виде ‘\0’. По расположению нуль-символа определяется фактическая длина строки. Строку можно инициализировать строковым литералом: char str[10] = “Vasia”; // выделено 10 элементов с номерами от 0 до 9 // первые элементы – ‘V’, ‘a’, ‘s’, ‘i’, 'а', '\0' ... char str[] = “Vasia”; // выделено и заполнено б байтов Оператор char *str = “Vasia”; создает не строковую переменную, а указатель на строковую константу, изменить которую невозможно. Знак равенства перед строковым литералом означает инициализацию, а не присваивание. Операция присваивания одной строки другой не определена (поскольку строка является массивом) и может выполняться с помощью цикла или функций стандартной библиотеки. Пример. Программа запрашивает пароль не более трех раз. #include <stdio.h> #include <string.h> int main () { char s[10], passw[] = “kuku”; // passw – пароль, можно также написать *passw = “kuku” int i, k = 0; for (i = 0;!k && i < 3; i++) { printf (“\nвведите пароль:\n”); gets (s); // Стандартная функция ввода строки с клавиатуры if (strstr (s, passw)) k = 1; // “сравнение” строк (указатель вхождения passw в s) } if (k) printf (“\nпароль принят”); else printf (“\nпароль не принят”); return 0; }
Дата добавления: 2014-01-06; Просмотров: 261; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |