Структуры. Вложенные структуры

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

Тип вложенной структуры должен быть объявлен раньше. Кроме того, структура не может быть вложена в структуру того же типа.

46. Массивы структур. Битовые поляСтруктурный тип «structID_структуры», как правило, используют для декларации массивов, элементами которых являются структурные переменные. Это позволяет создавать программы, оперирующие с простейшими базами данных. Например, массив структур, объявленного ранее типа:

structPersonspisok[100];

причем ключевое слово struct можно не писать. Декларацию массива можно выполнить и в описании шаблона следующим образом:

structРerson {

charfio[40];

int day, month, year;

} spisok[100];

В данном случае обращение к полю, например, dayэлемента массива с индексом i может быть выполнено одним из следующих способов:

spisok[i].day=22; *(spisok+i).day=22; (spisok+i)–>day=22;

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

structSpisok {

charFio[20];

doubleS_Bal;

};

// Описание прототипов функций пользователя

voidOut(int, Spisok);

void In(int, Spisok *);

void main(void)

{

SpisokStud[50], *sved;

. . .

for(i=0;i<N;i++)

Vvod(i, &Stud[i]);

puts("\n Spisok Students");

for(i=0;i<N;i++)

Out(i+1, Stud[i]);

. . .

}

// Функция вывода на экран данных одного элемента структуры

void Out(int nom, Spisokdan) {

printf("\n %3d – %20s %4.2lf ",nom, dan.Fio, dan.S_Bal);

}

// Функция ввода данных одного элемента структуры

void In (int nom, Spisok *sved) {

printf("\n Введите сведения %d : ", nom+1);

fflush(stdin);

puts("\n ФИО – ");

gets(sved–>Fio);

puts("\n Среднийбалл – ");

scanf(“%lf”, &sved–>S_Bal);

}

Битовые поля – это особый вид полей структуры. Они используются для плотной упаковки данных, например, флажков типа «да/нет». Минимальная адресуемая ячейка памяти – 1 байт, а для хранения флажка достаточно одного бита. При описании битового поля после имени через двоеточие указывается длина поля в битах (целая положительная константа), не превышающая разрядности поля типа int.

47. Объединения. ПеречисленияОбъединение – поименованная совокупность данных разных типов, размещаемых с учетом выравнивания в одной и той же области памяти, размер которой достаточен для хранения наибольшего элемента.Объединенный тип данных декларируется подобно структурному типу:

unionID_объединения {

Описание полей

};

Пример описания объединенного типа:

unionword {

intnom;

charstr[20];

};

Пример объявления объектов объединенного типа:

union word *p_w, mas_w[100];

Объединения применяют для экономии памяти в случае, когда объединяемые элементы логически существуют в разные моменты времени либо требуется разнотипная интерпретация поля данных.Практически все вышесказанное для структур имеет место и для объединений. Декларация данных типа union, создание переменных этого типа и обращение к полям объединений производится аналогично структурам.Перечисления – средство создания типа данных посредством задания ограниченного множества значений.Определение перечисляемого типа данных имеет вид

enumID_перечисляемого_типа {

список_значений };

Значения данных перечисляемого типа указываются идентификаторами, например:

enum marks {

zero, one, two, three, four, five };

Компилятор последовательно присваивает идентификаторам списка значений целочисленные величины 0, 1, 2,... . При необходимости можно явно задать значение идентификатора, тогда очередные элементы списка будут получать последующие возрастающие значения. Основные операции с данными перечисляемого типа:– присваивание переменных и констант одного типа;– сравнение для выявления равенства либо неравенства.Практическое назначение перечисления – определение множества различающихся символических констант целого типа.

48. Указатели. Указатели и адреса объектов. Указатели и массивы.При обработке декларации любой переменной, например doublex=1.5; компилятор выделяет для переменной участок памяти, размер которого определяется ее типом (double – 8 байт), и инициализирует его указанным значением (если таковое имеется). Далее все обращения в программе к переменной по име­ни заменяются компилятором на адрес участка памяти, в котором будет храниться значение этой переменной. Разработчик программы на языке Си имеет возможность определить собственные переменные для хранения адресов участков оперативной памяти. Такие переменные называются указателями.Итак, указатель – это переменная, которая может содержать адрес некоторого объекта. Простейшая декларация указателя имеет формат

тип * ID_указателя;

Например: int *a; double *f; char *w;

Здесь тип может быть любым, кроме ссылки или битового поля.Символ«звездочка»относится непосредственно к ID указателя, поэтому для того, чтобы декларировать несколько указателей, ее нужно записывать перед именем каждого из них. Например, в декларации:

int *a, *b, с;

определены два указателя на участки памяти для целочисленных данных, а также обычная целочисленная переменная с.Значение указателя равно первому байту участка памяти, на который он ссылается.Указатели предназначены для хранения адресов областей памяти. В языке Cи имеются тривида указателей – указатели на объект известного типа, указатель типа void и указатель на функцию. Эти три вида различаются как своими свойствами, так и набором допустимых операций. Указатель не является самостоятельным типом данных, так как всегда связан с каким-либо конкретным типом, т.е. указатель на объект содержит адрес области памяти, в которой хранятся данные определенного типа.Указатель типа voidприменяется в тех случаях, когда конкретный тип объекта, адрес которого требуется хранить, не определен (например, если в одной и той же переменной в разные моменты времени требуется хранить адреса объектов различных типов).Указателю типа void можно присвоить значение указателя любого типа, а также сравнивать его с любыми другими указателями, но перед выполнением каких-либо действий с участком памяти, на которую он ссылается, требуется явно преобразовать его к конкретному типу.Указатель может быть константой или переменной, а также указывать на константу или переменную.С указателями-переменными связаны две унарные операции& и *.Операция & означает «взять адрес» операнда. Операция * имеет смысл – «значение, расположенное по указанному адресу» (операция разадресации).Таким образом, обращение к объектам любого типа как операндам операций в языке Cи может производиться:– по имени (идентификатору);– по указателю (операция косвенной адресации)

50. Операции с указателями: сложение и вычитание, инкремент и декремент, сравнение указателейПомимо уже рассмотренных операций, с указателями можно выполнять арифметические операции сложения, инкремента (++), вычитания, декремента (--) и операции сравнения.Арифметические операции с указателями автоматически учитывают размер типа величин, адре­суемых указателями. Эти операции применимы только к указателям одного типа и имеют смысл в основном при работе со структурами данных, последовательно размещенными в памяти, например с массивами.Инкремент перемещает указатель к следующему элементу массива, декремент – к предыдущему. Указатель, таким образом, может использоваться в выражениях вида

p # iv, ## p, p##, p# = iv,

p – указатель, iv – целочисленное выражение, # – символ операции '+' или '–'.Результатом таких выражений является увеличенное или уменьшенное значение указателя на величину iv* sizeof(*p), т.е. если указатель на определенный тип увеличивается или уменьшается на константу, его значение изменяется на величину этой константы, умножен­ную на размер объекта данного типа.Текущее значение указателя всегда ссылается на позицию некоторого объекта в памяти с учетом правил выравнивания для соответствующего типа данных. Таким образом, значение p # iv указывает на объект того же типа, расположенный в памяти со смещением на iv позиций. При сравнении указателей могут использоваться отношения любого вида («>», «<» и т.д.), но наиболее важными видами проверок являются отношения равенства и неравенства («==», «!=»).Отношения порядка имеют смысл только для указателей на последовательно размещенные объекты (элементы одного массива).Разность двух указателей дает число объектов адресуемого ими типа в соответствующем диапазоне адресов, т.е. в применении к массивам разность указателей, например, на третий и шестой элементы равна 3. Очевидно, что уменьшаемый и вычитаемый указатели должны принадлежать одному массиву, иначе результат операции не имеет практической ценности и может привести к непредсказуемому результату. То же можно сказать и о суммировании указателей.Значение указателя можно вывести на экран с помощью функции printf, используя спецификацию %p (pointer), результат выводится в шестнадцатеричном виде.

51. Работа с динамической памятью.Динамические массивы.В языке Си размерность массива при объявлении должна задаваться константным выражением. При необходимости работы с массивами переменной размерности вместо массива достаточно объявить указатель требуемого типа и присвоить ему адрес свободной области памяти (захватить память). После обработки массива занятую память надо освободить. Библиотечные функции работы с памятью описаны в файле alloc.h. Пример создания динамического массива:

float*x;

intn;

printf("\nРазмерность - "); scanf(" %d",&n);

if((x = calloc(n, sizeof(*x)))==NULL) { // Захватпамяти

printf("\n Предел размерности “);

exit(1); }

else{

printf("\n Массив создан !");

...

for(i=0; i<n; i++)

printf("\n%f",x[i]);

...

free(x); // Освобождение памяти

}

ВС++ введены две операции: захват памяти - newи освобождение захваченной ранее памяти - delete.Общий формат записи: указатель = newтип (значение);Пример создания одномерного динамического массиваМассив объявляем указателем.

...

double*x;

inti, n;

...

puts(" Введите размер массива: ");

scanf(“%d”, &n);

x = new double [n] ;

if(x == NULL) {

puts(" Предел размерности ! ");

return; }

for(i=0; i<n; i++) // Ввод элементов массива

scanf(“%lf”, &x[i]);

...

delete[ ]x; // Освобождение памяти

Операции typedef и sizeof.

Операция typedef. Любому типу данных, как стандартному, так и определенному пользователем, можно задать новое имя с помощью операции typedef<тип><новое_имя>;Введенный таким образом новый тип используется аналогично стандартным типам, например, введя пользовательские типы:typedefunsignedintUINT;typedefcharM_s[100];декларации идентификаторов введенных типов имеют видUINT i, j;→ две переменные типа unsignedint;M_sstr[10]; → массив из 10 строк по 100 символов. Операция sizeofДанная операция позволяет определить размер объекта по его идентификатору или типу, результатом является размер памяти в байтах (типрезультата int). Формат записи:sizeof(параметр);где параметр – тип или ID объекта (не ID функции).Если указан идентификатор сложного объекта (массив, структура, объединение), то получаем размер всего сложного объекта. Например:sizeof(int)→ размер памяти 2 байта,

intb[5];

sizeof(b)→ размер памяти 10 байт.

Наиболее часто операция sizeofприменяется при динамическом распределении памяти.

53.Функции.Определение функции. объявление функции и вызов функции. Возвращаемое значение функции.В языке Си любая подпрограмма – функция, представляющая собой отдельный программный модуль, к которому можно обратиться в любой момент и (в случае необходимости) передавать через параметры некоторые исходные данные и который (в случае необходимости) способен возвращать один или несколько результатов своей работы. Объявление функции пользователя, т.е. ее декларация, выполняется в двух формах – в форме описания и форме определения. Полное определение функции имеет следующий вид:

тип_результатаID_функции(список параметров) {

код функции returnвыражение;

}. Вызов функции имеет следующий формат:

ID_функции (список аргументов);где в качестве аргументов можно использовать константы, переменные, выражения (их значения перед вызовом функции будут определены компилятором).

54.Функции. Типы передачи параметров функции (по значению, по адресу).В языке Си аргументы при стандартном вызове функции передаются по значению. Это означает, что в стеке, как и в случае локальных данных, выделяется место для формальных параметров функции. В выделенное место при вызове функции заносятся значения фактических аргументов, при этом проверяется соответствие типов и при необходимости выполняются их преобразования. При несоответствии типов выдается диагностическое сообщение. Затем функция использует и может изменять эти значения в стеке. При выходе из функции измененные значения теряются, т.к. время жизни и зона видимости локальных параметров определяется кодом функции. Вызванная функция не может изменить значения переменных, указанных как фактические аргументы при обращении к данной функции. В случае необходимости функцию можно использовать для изменения передаваемых ей аргументов. В этом случае в качестве аргумента необходимо в вызываемую функцию передавать не значение переменной, а ее адрес.При передаче по адресу в стек заносятся копии адресов аргументов, а функция осуществляет доступ к ячейкам памяти по этим адресам и может изменить исходные значения аргументов. Для обращения к значению аргумента-оригинала используется операция «*».

56. Функции. Массивы в качестве параметров функцииФункция – это именованная последовательность инструкций, выполняющая какое-либо законченное действие. Передача в функцию двухмерного массива:Если размеры известны на этапе компиляции, то

void f1(int m[3][4]) {

inti, j;

for ( i = 0; i<3; i++)

for ( j = 0; j<4; j++)

Обработка массива

}

Двухмерный массив передается как указатель, а указанные размеры используются просто для удобства записи.

57 Указатели на функцииВ языке Си идентификатор функции является константным указателем на начало функции в оперативной памяти и не может быть значением переменной. 1. указатель на функции необходимо декларировать. Формат объявления указателя на функции следующий:тип (*переменная-указатель)(список параметров);double (*p_f )(char, double); говорит о том, что декларируется указатель p_f, который можно устанавливать на функции, возвращающие результат типа double и имеющие два параметра: первый – символьного типа, а второй – вещественного типа.2. Идентификатор функции является константным указателем, поэтому для того чтобы установить переменную-указатель на конкретную функцию, достаточно ей присвоить ее идентификатор:переменная-указатель = ID_функции;3. Вызов функции после установки на нее указателя выглядит так:(*переменная-указатель)(список аргументов);После таких действий кроме стандартного обращения к функции:ID_функции(список аргументов);появляется еще два способа вызова функции: (*переменная-указатель)(список аргументов);Для нашего примера к функции f1 можно обратиться следующими способами:

f1(‘z’, 1.5); – обращение к функции по имени (ID);

(* p_f)(‘z’, 1.5); – обращение к функции по указателю;

p_f(‘z’, 1.5); – обращение к функции по ID указателя.

58. Перегрузка функцийПерегрузка функций улучшает удобочитаемость программПерегрузка функций C++ позволяет вашим программам определять несколько функций с одним и тем же именем. Перегруженные функции должны возвращать значения одинакового типа*, но могут отличаться количеством и типом параметров. До появления перегрузки функций в C++ программисты языка С должны были создавать несколько функций с почти одинаковыми именами. К сожалению программисты, желающие использовать такие функции, должны были помнить, какая комбинация параметров соответствует какой функции. С другой стороны, перегрузка функций упрощает задачу программистов, требуя, чтобы они помнили только одно имя функции.Использование шаблонов функцийПо мере того как ваши программы становятся более сложными, возможны ситуации, когда вам потребуются подобные функции, выполняющие одни и те же операции, но с разными типами данных. Шаблон функции позволяет вашим программам определять общую, или типонезависимую, функцию. Когда программе требуется использовать функцию для определенного типа, например int или float, она указывает прототип функции, который использует имя шаблона функции и типы возвращаемого значения и параметров. В процессе компиляции C++ создаст соответствующую функцию. Создавая шаблоны, вы уменьшаете количество функций, которые должны кодировать самостоятельно, а ваши программы могут использовать одно и то же имя для функций, выполняющих определенную операцию, независимо от возвращаемого функцией значения и типов параметров.

59 Классы памяти. Области действия объектовКласс памяти программного объекта определяет время ее существования (время жизни) и область видимости (действия) и может принимать одно из значений: auto, extern, static и register. Область действия объекта – это часть кода программы, в которой его можно использовать для доступа к связанному с ним участку памяти. В зависимости от области действия переменная может быть локальной (внутренней) или глобальной (внешней). Эти переменные соответственно называются локальными (внутренними) переменными, формальными параметрами и глобальными (внешними) переменными. Существуют следующие области действия: Блок. Идентификаторы, описанные внутри блока, являются локальными. Область действия идентификатора начинается в точке определения и заканчивается в конце блока. После выхода из блока память освобождается.Файл. Идентификаторы, описанные вне любого блока, функции, класса или пространства имен, имеют глобальную видимость и постоянное время жизни и могут использоваться с момента их определения.Функция. Единственными идентификаторами, имеющими такую область действия, являются метки операторов. В одной функции все метки должны различаться, но могут совпадать с метками других функций.Прототип функции. Идентификаторы, указанные в списке параметров прототипа (декларации) функции, имеют областью действия только прототип функции.Структурированный тип данных. Элементы структур и объединений являются видимыми лишь в их пределах. Они образуются при создании переменной указанного типа и разрушаются при ее уничтожении.

60.Работа с файлами.Типы файлов (текстовый и бинарный) Файл – это набор данных, размещенный на внешнем носителе и рассматриваемый в процессе обработки как единое целое. В файлах размещаются данные, предназначенные для длительного хранения.Различают два вида файлов: текстовые и бинарные. Текстовые файлы представляют собой последовательность ASCII символов и могут быть просмотрены и отредактированы с помощью любого текстового редактора.Бинарные (двоичные) файлы представляют собой последовательность данных, структура которых определяется программно.В языке Си имеется большой набор функций для работы с файлами,большинство которых находятся в библиотеках stdio.hи io.h.Открытие файла FILE*указатель на файл;FILE - идентификатор структурного типа, описанный в стандартной библиотеке stdio.h. Для открытия файла для доступа используется функцияFILE* fopen(char* имя_файла, char*режим); При успешном открытии функция fopenвозвращает указатель на файл (в дальнейшем - указатель файла). При ошибке возвращается NULL.Посимвольный ввод-вывод происходит прием одного символа из файла или передача одного символа в файл:intfgetc(FILE *f) - считывает и возвращает символ из файла f;intfputc(intch, FILE *f) - записывает в файл f код ch символа.Закрытие файлаПосле работы с файлом доступ к нему необходимо закрыть. Это выполняет функция intfclose(указатель файла). Например: fclose (f);Текстовые файлыДля работы с текстовыми файлами удобнее всего пользоваться функциями fprintf, fscanf, fgetsи fputs.Создание текстовых результирующих файлов обычно необходимо для оформления отчетов по лабораторным и курсовым работам.Бинарные файлы (двоичные) файлы обычно используются для организации баз данных, состоящих, как правило, из объектов структурного типа. При чтении-записи бинарных файлов удобнее всего пользоваться функциями, выполняемыми блоковый ввод-вывод freadи fwrite.Наиболее распространенные функции, с помощью которых можно организовать работу с файлами:1) intfileno(FILE *f) – возвращает значение дескриптора файла f – fd(число, определяющее номер файла);2) longfilelength(intfd) – возвращает длину файла, имеющего номер (дескриптор) fdв байтах;3) intchsize(intfd, longpos) – выполняет изменение размера файла, имеющего номер fd, признак конца файла устанавливается после байта с номером pos;4) intfseek(FILE *f, longsize, intkod) – выполняет смещение указателя файла f на sizeбайт в направлении признака kod: 0 - от начала файла;1 - от текущей позиции указателя;2 - от конца файла;5) longftell(FILE *f) – возвращает значение указателя на текущую позицию файла (-1 – ошибка);6) intfeof(FILE *f) – возвращает ненулевое значение при правильной записи признака конца файла;7) intfgetpos(FILE *f, long*pos) – определяет значение текущей позиции posфайла f, возвращает 0 при успешном завершении.

65. Работа с файлами. Указатель текущей позиции в файле, его перемещение к нужной позиции.Каждый открытый файл, как уже отмечалось, имеет скрытый указатель на текущую позицию в нем. При открытии файла этот указатель устанавливается на начало данных, и все операции в файле будут производиться с данными, начинающимися в этой позиции. При каждом выполнении функции чтения или записи указатель смещается на количество прочитанных или записанных байт, т.е. устанавливается после прочитанного или записанного блока данных в файле – это последовательный доступ к данным. В языке Си/С++ можно установить указатель на некоторую заданную позицию в файле. Для этого используют стандартную функцию fseek, которая позволяет выполнить чтение или запись данных в произвольном порядке.Декларация функции позиционирования следующая:

intfseek(FILE *f, longsize, intcode);

Значение параметра size задает количество байт, на которое необходимо сместить указатель в файле f, в направлении параметра code, который может принимать следующие значения:

– смещение от начала файла – (SEEK_SET);
– смещение от текущей позиции – (SEEK_CUR);
– смещение от конца файла – (SEEK_END).

Таким образом, смещение может быть как положительным, так и отрицательным, но нельзя выходить за пределы файла.В случае успеха функция возвращает нулевое значение, а в случае ошибки (например, попытка выхода за пределы файла) – единицу. Доступ к файлу с использованием функции позиционирования (fseek) называют произвольным доступом.Иногда нужно определить текущее положение в файле. Для этого используют функцию со следующей декларацией:

longftell(FILE *f);

которая возвращает значение указателя на текущую позицию в файле или –1 в случае ошибки.

66. Графический режим. Функции черчения и ЗаполненияС++ Builder позволяет программисту разрабатывать программы, которые работают с графикой. Программа может вывести графику на поверхность формы (или компонента image), которой соответствует свойство canvas (Canvas — холст для рисова­ния). Для того чтобы на поверхности формы или компонента image появи­лась линия, окружность, прямоугольник или другой графический элемент (примитив), необходимо к свойству canvas применить соответствующий ме­тод.Например, оператор Forml->Canvas->Rectangle(10,10,50,50); рисует на поверхности формы прямоугольник.LineTo(x,y) - Рисует линию из текущей точки в точку с указанными координатами.Rectangle(x1, y1, x2, y2) - Рисует прямоугольник, xl, y1 и x2, y2 — коор­динаты левого верхнего и правого нижнего уг­лов прямоугольника. Цвет границы и внутрен­ней области прямоугольника могут быть раз­нымиFillRect(xl,yl,x2,y2) - Рисует закрашенный прямоугольник, xl, yl, x2, y2 определяют координаты диагональных углов.FrameRect(xl,yl,x2,y2) - Рисует контур прямоугольника, xl, yl, х2, у2 - определяют координаты диагональных углов.RounRect(xl,yl,x2,y2,x3,y3) - Рисует прямоугольник со скругленными углами.Ellipse(xl,yl,x2,y2) - Рисует эллипс или окружность (круг), xl. y1. х2. у2 - координаты прямоугольника, внутри которого вычерчивается эллипс или, если пря­моугольник является квадратом, окружность.Polyline(points,n) - Рисует ломаную линию, points — массив типа TPoint. Каждый элемент массива представля­ет собой запись, поля х и у которой содержат координаты точки перегиба ломаной; n — коли­чество звеньев ломаной. Метод Polyline вы­черчивает ломаную линию, последовательно соединяя прямыми отрезками точки, координа­ты которых находятся в массиве: первую со второй, вторую с третьей, третью с четвертой и т. д.Для рисования дуги эллипса (рис. П 6.2) используется функция

Arc (hdc, x1, y1, x2, y2, xStart, yStart, xEnd, yEnd);

вкоторой значения (x1, y1) задают левый верхний угол, (x2, y2) – правый нижний; (xStart, yStart) – начало дуги; (xEnd, yEnd) – конец дуги.

Структуры. Вложенные структуры - student2.ru

Rectangle – прямоугольник; Ellipse – эллипс; RoundRect – прямоугольник со скругленными углами; Chord – дуга кривой эллипса, концы которой соединены хордой; Pie – кусок, вырезанный из эллипса; Polygon – многоугольник; PolyPolygon – множество многоугольников.Функция FillRect(hdc, &rect, hBrush); закрашивает прямоугольник (не включая правую и нижнюю координаты) заданной кистью.Функция FrameRect (hdc, &rect, hBrush); использует кисть для рисования прямоугольной рамки, но не закрашивает внутреннюю область. Функция InvertRect (hdc, &rect); инвертирует все пиксели в прямоугольнике, устанавливая единичные биты в ноль, а нулевые – в единицу, т.е. переводит белую область в черную, черную – в белую (зеленую – в фиолетовую).Функция PaintRgn закрашивает внутреннюю область региона текущей выбранной в контекст устройства кистью. Во всех функциях предполагается, что регион определен в логических координатах.

Наши рекомендации