Студопедия

КАТЕГОРИИ:


Архитектура-(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; Просмотров: 248; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.009 сек.