Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
shpora_prog.docx
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
163 Кб
Скачать
☆

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

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

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

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

struct Person spisok[100];

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

struct Рerson {

char fio[40];

Int day, month, year;

} spisok[100];

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

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

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

struct Spisok {

char Fio[20];

double S_Bal;

};

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

void Out(int, Spisok);

void In(int, Spisok *);

void main(void)

{

Spisok Stud[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, Spisok dan) {

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. Объединения. Перечисления Объединение – поименованная совокупность данных разных типов, размещаемых с учетом выравнивания в одной и той же области памяти, размер которой достаточен для хранения наибольшего элемента. Объединенный тип данных декларируется подобно структурному типу:

union ID_объединения {

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

};

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

union word {

int nom;

char str[20];

};

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

union word *p_w, mas_w[100];

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

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

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

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

enum marks {

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

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

48. Указатели. Указатели и адреса объектов. Указатели и массивы. При обработке декларации любой переменной, например double x=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;

int n;

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;

int i, 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; // Освобождение памяти

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]