Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Теория ответы.doc
Скачиваний:
39
Добавлен:
24.12.2018
Размер:
1.61 Mб
Скачать

17.Структуры. Создание и заполнение структур информацией. Доступ к полям структуры.

Основные операции с массивом структур. Структуры и указатели.

Структуры

Структура — это группа связанных переменных.

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

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

Член структуры — это переменная, которая является частью структуры.

В общем случае все члены структуры должны быть логически связаны друг с другом. Например, структуры обычно используются для хранения такой информации, как почтовые адреса, таблицы имен компилятора, элементы карточного каталога и т.п. Безусловно, отношения между членами структуры совершенно субъективны и определяются программистом. Компилятор "ничего о них не знает" (или "не хочет знать").

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

struct inv_type {

char item[40]; // наименование товара

double cost; // стоимость

double retail; // розничная цена

int on_hand; // имеющееся в наличии количество

int lead time; // число дней до пополнения запасов

};

Имя структуры — это ее спецификатор типа.

Обратите внимание на то, что объявление завершается точкой с запятой. Дело в том, что объявление структуры представляет собой инструкцию. Именем типа структуры здесь является inv_type. Другими словами, имя inv_type идентифицирует конкретную структуру данных и является ее спецификатором типа.

В предыдущем объявлении в действительности не было создано ни одной переменной. Был определен лишь формат данных. Чтобы с помощью этой структуры объявить реальную переменную (т.е. физический объект), нужно записать инструкцию, подобную следующей. inv_type inv_var;

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

При объявлении структурной переменной C++ автоматически выделит объем памяти, достаточный для хранения всех членов структуры. На рис. 10.1 показано, как переменная inv_var будет размещена в памяти компьютера (в предположении, что double-значение занимает 8 байт, а int-значение — 4).

Одновременно с определением структуры можно объявить одну или несколько переменных, как показано в этом примере.

struct inv_type {

char item[40]; // наименование товара

double cost; // стоимость

double retail; // розничная цена

int on_hand; // имеющееся в наличии количество

int lead-time; //число дней до пополнения запасов

}inv_varA, inv_varB, inv_varC;

Этот фрагмент кода определяет структурный тип inv_type и объявляет переменные inv_varA, inv_varB и inv_varC этого типа. Важно понимать, что каждая структурная переменная содержит собственные копии членов структуры. Например, поле cost структуры inv_varA изолировано от поля cost структуры inv_varB. Следовательно, изменения, вносимые в одно поле, никак не влияют на содержимое другого поля.

Если для программы достаточно только одной структурной переменной, в ее определение необязательно включать имя структурного типа. Рассмотрим следующий пример.

struct {

char ltem[40]; // наименование товара

double cost; // стоимость

double retail; // розничная цена

int on_hand; // имеющееся в наличии количество

int lead_time; // число дней до пополнения запасов

} temp;

Этот фрагмент кода объявляет одну переменную temp в соответствии с предваряющим ее определением структуры.

Ключевое слово struct означает начало объявления структуры.

Общий формат объявления структуры выглядит так.

struct имя_типа_структуры { тип имя_элемента1; тип имя_элемента2; тип имя элемента 3;

тип имя_элементаЫ; } структурные переменные;

Доступ к членам структуры

К отдельным членам структуры доступ осуществляется с помощью оператора "точка". Например, при выполнении следующего кода значение 10.39 будет присвоено полю cost структурной переменной inv_var, которая была объявлена выше.

inv_var.cost = 10.39;

Чтобы обратиться к члену структуры, нужно перед его именем поставить имя структурной переменной и оператор "точка". Так осуществляется доступ ко всем элементам структуры. Общий формат доступа записывается так.

имя_структурной_переменной.имя_члена

Оператор "точка" (.) позволяет получить доступ к любому члену любой структуры.

Следовательно, чтобы вывести значение поля cost на экран, необходимо выполни следующую инструкцию.

cout << inv var.cost;

Аналогичным способом можно использовать символьный массив inv_var.item вызове функции gets ().

gets(inv_var.item);

Здесь функции gets () будет передан символьный указатель на начало области памяти, отведенной элементу item.

Если вам нужно получить доступ к отдельным элементам массива inv_var. item, используйте индексацию. Например, с помощью этого кода можно посимвольно вывести на экран содержимое массива inv_var.item.

int t;

for(t=0; inv_var.item[t]; t++) cout << inv var.item[t];

Массивы структур

Структуры могут быть элементами массивов. И в самом деле, массивы структур используются довольно часто. Чтобы объявить массив структур, необходимо сначала определить структуру, а затем объявить массив элементов этого структурного типа. Например, чтобы объявить 100-элементный массив структур типа inv_type (который определен выше), достаточно записать следующее:

inv_type invtry[100];

Чтобы получить доступ к конкретной структуре в массиве структур, необходимо индексировать имя структуры. Например, чтобы отобразить на экране содержимое члена on_hand третьей структуры, используйте такую инструкцию.

cout << invtry[2].on_hand;

Подобно всем переменным массивов, у массивов структур индексирование начинается с нуля.

Простой пример инвентаризации склада

Чтобы продемонстрировать применение структур, разработаем простую программу управления складом, в которой для хранения информации о товарах, размещенных на складе компании, используется массив структур типа inv_type. Различные функции, определенные в этой программе, взаимодействуют со структурой и ее элементами по-разному.

Инвентарная ведомость будет храниться в структурах типа inv_type, организованных в массиве invtry.

const int SIZE = 100;

struct inv_type {

char item[40]; // наименование товара

double cost; // стоимость

double retail; // розничная цена

int on_hand; // имеющееся в наличии количество

int lead_time; // число дней до пополнения запасов

} invtry[SIZE];

Размер массива выбран произвольно. При желании его можно легко изменить. Обратите внимание на то, что размерность массива задана с использованием const переменной. А поскольку размер массива во всей программе используется несколько применение const-переменной для этой цели весьма оправданно. Чтобы изменить размер массива, достаточно изменить значение константной переменной SIZE, а затем рекомпилировать программу. Использование const-переменной для определения «магического числа», которое часто употребляется в программе, — обычная практика в профессиональном С++-коде.

Разрабатываемая программа должна обеспечить выполнение следующих действий:

  • ввод информации о товарах, хранимых на складе;

  • отображение инвентарной ведомости;

  • модификация заданного элемента.

Прежде всего напишем функцию main (), которая должна иметь примерно такой

int main() {

char choice;

init_list();

for (; ; ) {

choice = menu ();

switch(choice) {

case 'e': enter (); break;

case 'd': display();break;

case 'u': update();break;

case 'q': return 0;

} } }

Функция main () начинается с вызова функции init_list (), которая инициализирует массив структур. Затем организован цикл, который отображает меню и обрабатывает команду, выбранную пользователем. Приведем код функции init_list ().

// Инициализация массива структур,

void init_list()

int t;

// Имя нулевой длины означает пустое имя.

for(t=0; t<SIZE; t++) *invtry[t].item = '\0';

Функция init_list () подготавливает массив структур для использования, помещая в первый байт поля item нулевой символ. Предполагается, что если поле item пустое, то структура, в которой оно содержится, попросту не используется,

Функция menu_select () отображает команды меню и принимает вариант, выбранный пользователем.

// Получение команды меню, выбранной пользователем

int menu(){

char ch;

cout « '\n' ;

do {

cout << "(E)nter\n"; // Ввести новый элемент.

cout << "(D)isplay\n"; // Отобразить всю ведомость.

cout << "(U)pdate\n"; // Изменить элемент.

cout << " (Q)uit\n\n"; // Выйти из программы.

cout << "Выберите команду: ";

cin >> ch;} while (!strchr("eduq", tolower (ch) ) );

return tolower(ch); }

Пользователь выбирает из предложенного меню команду, вводя нужную букву. Например, чтобы отобразить всю инвентарную ведомость, нажмите букву "D".

Функция menu () вызывает библиотечную функцию C++ strchr (), которая имеет такой прототип.

char *strchr(const char *str, int ch) ;

Эта функция просматривает строку, адресуемую указателем str, на предмет вхождения в нее символа, который хранится в младшем байте переменной ch. Если такой символ "обнаружится, функция возвратит указатель на него. И в этом случае значение, возвращаемое функцией, по определению будет истинным. Но если совпадения символов не произойдет, функция возвратит нулевой указатель, который по определению представляет собой значение ЛОЖЬ. Так здесь организована проверка того, являются ли значения, вводимые пользователем, допустимыми командами меню.

Функция enter () предваряет вызов функции input (), которая "подсказывает" пользователю порядок ввода данных и принимает их. Рассмотрим код обеих функций.

//Ввод элементов в инвентарную ведомость.

void enter (){

int i;

// Находим первую свободную структуру.

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

if (!*invtry[i].item) break;

// Если массив полон, значение i будет равно SIZE,

if (i==SIZE) {

cout << "Список полон.\n"; return; }

input (i); }

// Ввод информации.

void input (int i) {

cout << "Товар: "; cin >> invtry[i].item;

cout << "Стоимость: "; cin >> invtry[i],cost;

cout << "Розничная цена: "; cin >> invtry[i].retail;

cout << "В наличии: "; cin >> invtry[i].on_hand;

cout << "Время до пополнения запасов (в днях): "; cin >> invtry[i].lead_time; }

Функция enter () сначала находит пустую структуру. Для этого проверяется поле item каждого (по очереди) элемента массива invtry, начиная с первого. Если item оказывается пустым, то предполагается, что структура, к которой оно относится, еще ничем не занята. Если не отыщется ни одной свободной структуры при проверке всего массива структур, управляющая переменная цикла i станет равной его размеру. Это говорит о том, что массив полон, и в него уже нельзя ничего добавить. Если в массиве найдется свободный элемент, будет вызвана функция input () для получения информации о товаре, вводимой пользователем. Если вас интересует, почему код данных о новом товаре не является частью функции enter (), то ответ таков: функция input () используется также и функцией update (), о которой речь впереди.

Поскольку информация о товарах на складе может меняться, программа ведения инвентарной ведомости должна позволять вносить изменения в ее отдельные элементы Это реализуется путем вызова функции update ().

// Модификация существующего элемента.

void update() {

int i;

char name[80];

cout << "Введите наименование товара: "; cin >> name;

for(i=0; i<SIZE; i++) if(!strcmp(name, invtry[i].item)) break;

if(i==SIZE) { cout << "Товар не найден.\n"; return; }

cout << "Введите новую информацию.\п"; input (i); }

Эта функция предлагает пользователю ввести наименование товара, информацию о котором ему нужно изменить. Затем она просматривает весь список существующих элементов, и если указанный товар в нем имеется, то вызывается функция input (), которая обеспечивает прием от пользователя новой информации.

Нам осталось рассмотреть функцию display (). Она выводит на экран инвентарную ведомость в полном объеме. Код функции display () выглядит так.

// Отображение на экране инвентарной ведомости.

void display() {

int t;

for(t=0; t<SIZE; t++) {

if(*invtry[t].item) {

cout << invtry[t].item << '\n';

cout << "Стоимость: $" << invtry[t].cost;

cout << "\nB розницу: $";

cout << invtry[t].retail << '\n';

cout << "В наличии: " << invtry[t].on_hand;

cout << "\nДо пополнения осталось: ";

cout << invtry[t].lead_time << " дней\n\n";

}}

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

//Простая программа ведения инвентарной ведомости,

//в которой используется массив структур.

#include <iostream>

#include <cctype>

#include <cstring>

#include <cstdlib> .

using namespace std;

const int SIZE = 100;

struct inv_type {

char item[40]; // наименование товара

double cost; // стоимость

double retail; // розничная цена

int on_hand; // имеющееся в наличии количество

int lead_time; // число дней до пополнения запасов

} invtry[SIZE];

void enter(), init_list(), display(); void update (), input(int i); int menu();

int main() Х

char choice; init_list();

for(;;) {choice = menu();

switch(choice) {

case ‘e’: enter(); break;

case ‘d’: display(); break;

case ‘u’: update(); break;

case ‘q’: return 0;

}}}

// Инициализация массива структур.

void init_list() {

int t;

// Имя нулевой длины означает пустое имя.

for(t=0; t<SIZE; t++) *invtry[t].item = '\0';

// Получение команды меню, выбранной пользователем.

int menu()

{

char ch; cout << '\n';

do {

cout << "(E)nter\n"; // Ввести новый элемент.

cout << "(D)isplay\n"; // Отобразить всю ведомость.

cout << "(U)pdate\n"; // Изменить элемент.

cout << " (Q)uit\n\n"; // Выйти из программы.

cout << "Выберите команду: ";

cin >> ch;

} while (!strchr("eduq", tolower(ch))); return tolower(ch); }

// Ввод элементов в инвентарную ведомость.

void enter () {

int i;

// Находим первую свободную структуру.

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

if(!*invtry[1].item) break;

// Если массив полон, значение i будет равно SIZE,

if (i==SIZE) {cout << "Список полон.\n"; return;}

input(i); }

// Ввод информации.

void input (int i) {

cout << "Товар: "; cin >> invtry[i].item;

cout << "Стоимость: "; cin >> invtry[i].cost;

cout << "Розничная цена: "; cin >> invtry[i].retail;

cout << "В наличии: "; cin >> invtry[i].on_hand;

cout << "Время до пополнения запасов (в днях) cin >> invtry[i].lead_time;

//Модификация существующего элемента.

void update(){

int i;

char name[80];

cout << "Введите наименование товара: "; cin >> name;

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

if(!strcmp(name, invtry[i].item)) break;

if(i==SIZE) {cout << "Товар не найден.\n"; return; }

cout << "Введите новую информацию.\n";

input (i); }

//Отображение на экране инвентарной ведомости,

void display() {

int t;

for(t=0; t<SIZE; t++) {

if(*invtry[t].item) {cout << invtry[t].item << '\n';

cout << "Стоимость: S" << invtry[t].cost;

cout << "\nB розницу: S";

cout << invtry[t].retail << '\n';

cout << "В наличии: " << invtry[t].on_hand;

cout << "\nДо пополнения осталось: ";

cout << invtry[t].lead_time << " дней\n\n";

}}}

Использование указателей на структуры и оператора "стрелка"

В C++ указатели на структуры можно использовать таким же способом, как и указатели на переменные любого другого типа. Однако использование указателей на структуры имеет ряд особенностей, которые необходимо учитывать.

Указатель на структуру объявляется так же, как указатель на любую другую переменную, т.е. с помощью символа "*", поставленного перед именем структурной переменной. Например, используя определенную выше структуру inv_type, можно записать следующую инструкцию, которая объявляет переменную inv_pointer указателем на данные типа inv_type:

inv_type *inv_pointer;

Чтобы найти адрес структурной переменной, необходимо перед ее именем разместить оператор "&". Например, предположим, с помощью следующего кода мы определяем структуру, объявляем структурную переменную и указатель на структуру определенного нами типа.

struct bal {

float balance;

char name[80]; } person;

bal *p; // Объявляем указатель на структуру.

Тогда при выполнении инструкции

р = &person;

в указатель р будет помещен адрес структурной переменной person.

К членам структуры можно получить доступ с помощью указателя на эту структуру Но в этом случае используется не оператор "точка", а оператор "->". Например, при выполнении этой инструкции мы получаем доступ к полю balance через указатель p:

p->balance

Оператор "->" называется оператором "стрелка". Он образуется с использованием знаков "минус" и "больше".

Оператор "стрелка" (->) позволяет получить доступ к членам структуры с помощью указателя.

Указатель на структуру можно использовать в качестве параметра функции. Важно помнить о таком способе передачи параметров, поскольку он работает гораздо быстрее, чем в случае, когда функции "собственной персоной" передается объемная структура (Передача указателя всегда происходит быстрее, чем передача самой структуры.)

Чтобы получить доступ к членам структуры, используйте оператор "точка".

Чтобы получить доступ к членам структуры с помощью указателя, используйте оператор "стрелка".

Пример использования указателей на структуры

В качестве интересного примера использования указателей на структуры можно рассмотреть С++-функции времени и даты. Эти функции считывают значения текущего системного времени и даты. Для их использования в программу необходимо включить заголовок <ctime>. Этот заголовок поддерживает два типа даты, требуемые упомянутыми функциями. Один из этих типов, time_t, предназначен для представления системного времени и даты в виде длинного целочисленного значения, которое используется в качечестве календарного времени. Второй тип представляет собой структуру tm, которая содержит отдельные элементы даты и времени. Такое представление времени называют поэлементным. Структура tm имеет следующий формат.

struct tm {

int tm_sec; /* секунды, 0-61 */

int tm_min; /* минуты, 0-5 9 */

int tm_hour; /* часы, 0-23 */

int tm_mday; /* день месяца, 1-31 */

int tm_mon; /* месяц, начиная с января, 0-11 */

int tm_year; /* год после 1900 */

int tm_wday; /* день, начиная с воскресенья, 0-6 */

int tm_yday; /* день, начиная с 1-го января, 0-365 */

int tm_isdst; /* индикатор летнего времени */

}

Значение tm_isdst положительно, если действует режим летнего времени (Daylight Saving Time), равно нулю, если не действует, и отрицательно, если информация об этом недоступна.

Основным средством определения времени и даты в C++ является функция time (), которая имеет такой прототип:

time_t time(time_t * сиrtime);

Функция time () возвращает текущее календарное время системы. Если в системе отсчет времени не производится, возвращается значение -1. Функцию time () можно вызывать либо с нулевым указателем, либо с указателем на переменную curtime типа time_t. В последнем случае этой переменной будет присвоено значение текущего календарного времени.

Чтобы преобразовать календарное время в поэлементное, используйте функцию localtime (), которая имеет такой прототип:

struct tm *localtime(const time_t *curtime);

Функция localtime () возвращает указатель на поэлементную форму параметра curtime, представленного в виде структуры tm. Значение curtime представляет л­кальное время. Его обычно получают с помощью функции time ().

Структура, используемая функцией localtime () для хранения времени в поэлементной форме, размещается в памяти статически и перезаписывается при каждом вызове этой функции. Если нужно сохранить содержимое этой структуры, скопируйте его в какую-нибудь другую область памяти.

Следующая программа демонстрирует использование функций time() и localtime(), отображая на экране текущее системное время.

// Эта программа отображает текущее системное время.

#include <iostream>

#include <ctime>

using namespace std;

int main() {

struct tm *ptr;

time_t lt;

lt = time('\0');

ptr = localtime(&lt);

cout << ptr->tm_hour << ':' << ptr->tm_min;

cout << ':' << ptr->tm_sec;

return 0; }

Вот один из возможных результатов выполнения этой программы:

14:52:30

Несмотря на то что ваши программы могут использовать поэлементную форму представления времени и даты (как показано в предыдущем примере), проще всего сгенерировать строку времени и даты с помощью функции asctime (), прототип которой выглядит так:

char *asctime(const struct tm *ptr);

Функция asctime () возвращает указатель на строку, которая содержит преобразования информации, хранимой в адресуемой параметром ptr структуре, и имеет следующую форму.

день месяц число часы:минуты:секунды год\п\0

Указатель на структуру, передаваемый функции asctime(), часто получают с помощью функции localtime ().

Область памяти, используемая функцией asctime () для хранения форматированной строки результата, представляет собой символьный массив (статически выделяемый в памяти), который перезаписывается при каждом вызове этой функции. Если нужно сохранить содержимое данной строки, скопируйте его в какую-нибудь другую область памяти.

В следующей программе демонстрируется использование функции asctime () для отображения системного времени и даты.

// Эта программа отображает текущее системное время.

#include <iostream>

#include <ctime>

using namespace std;

int main() {

struct tm *ptr;

time_t lt;

lt = time('\0');

ptr = localtime(&lit);

cout << asctime(ptr);

return 0; }

Вот один из возможных результатов выполнения этой программы.

Wed Jul 28 15:05:51 2004

В языке C++ предусмотрены и другие функции даты и времени, с которыми можно познакомиться, обратившись к документации, прилагаемой к вашему компилятору.