Использование структур с функциями

ПРАКТИЧЕСКОЕ ЗАНЯТИЕ 15

Тема: « Разработка программ, использующих структуры, объединения и перечисления »

Цели:

-научиться создавать и использовать структуры, объединения и перечисления.

-изучить передачу структур в функции по значению и по ссылке.

-научиться создавать битовые поля в структурах.

 

Характер занятия: репродуктивный.

Форма организации: индивидуальная.

Обеспечение занятия: тетрадь с лекциями, тетрадь для практических работ, компьютер.

 

Требования к знаниям студентов

Перед выполнением практической работы студент должен

знать :

- отличия структуры от массива;

- отличия объединения от структуры и массива;

- отличия перечисления от базовых типов языка;

после выполнения практической работы студент должен

уметь:

- использовать структуры для создания записей данных;

- применять объединения для экономии памяти и сокращения кода программы;

- создавать перечисления и обращаться к его элементам;

- использовать структуры и объединения в качестве параметров функций.

 

Теоретический материал

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

Описания структур

Рассмотрим следующее описание структуры:

struct card ( char *face; char *suit;

};

Ключевое слово struct определяет структуру. Идентификатор card является именем-этикеткой структуры. Имя-этикетка именует структуру, и используется совместно с ключевым словом struct для объявления переменных типа структуры. В данном примере тип структуры — struct card. Переменные, объявленные внутри скобок структуры, являются элементами структуры.

Элементы одной структуры должны иметь уникальные имена, но две различных структуры могут содержать одинаковые имена. Каждое определение структуры должно заканчиваться точкой с запятой.

Элементы структуры могут быть переменными основных типов данных (то есть int, float и т.д.), или агрегатами, такими, как массивы или же другие структуры. Элемент структуры не может быть структурой того же типа, в которой он содержится, но его можно объявить как указатель на тип структуры, в которую он входит, такая структура, называется структурой, ссылающейся на себя.

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

struct card a, deck[52], *cPtr;

объявляет а — переменную типа struct card, deck — массив из 52 элементов типа struct card и cPtr — указатель на struct card. Можно объявить переменные данной структуры и по-другому:

struct card {

char *face;

char *suit; } a, deck[52], *cPtr;

Имя-этикетка не является для структуры обязательным. Если определение структуры не содержит имя-этикетку, переменные для этой структуры могут быть объявлены только в определении структуры, но не отдельным объявлением.

Следующие операции над структурами являются единственно допустимыми: присваивание переменных-структур переменным того же типа, взятие адреса (&) структуры, обращение к элементам структуры и применение операции sizeof для определения ее размера.

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

Инициализация структур

Структуры можно инициализировать, как и массивы, используя список инициализации:

struct card a = {"Three", "Hearts"};

создает переменную а типа struct card (структура определена выше) и присваивает элементу face значение "Three", а элементу suit значение "Hearts".

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

 

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

Для обращения к элементам структур используются две операции: операция элемента структуры (.), также называемая операцией-точкой, и операция указателя структуры (->), также называемая операцией-стрелкой. Операция элемента структуры обращается к элементу через имя переменной структуры.

Например, для того чтобы напечатать элемент suit структуры а из предыдущего объявления, можно написать оператор

printf("%s", a.suit);

Предположим, что переменная aPtr была объявлена как указатель на struct card и ей был присвоен адрес структуры а. Чтобы напечатать элемент suit структуры а при помощи указателя aPtr, напишите оператор

Выражение aPtr->suit эквивалентно (*aPtr).suit, которое, обращаясь по адресу, содержащемуся в указателе, находит структуру а и обращается к элементу suit, используя операцию элемента структуры. Скобки в данном случае необходимы по той причине, что операция элемента структуры (.) имеет более высокий приоритет, чем операция косвенной адресации (*). Операции указателя структуры и элемента структуры, наряду с круглыми скобками и квадратными скобками ([]), использовавшимися для индексов массивов, являются операциями наивысшего приоритета и ассоциируются слева направо.

Программа на рис.1 демонстрирует использование операций элемента структуры и указателя структуры. С помощью операции элемента структуры элементам а присваиваются значения "Асе" и "Spades" соответственно. Указателю aPtr присваивается адрес структуры а. Оператор printf печатает элементы структуры а, используя операцию элемента структуры с именем переменной а, операцию указателя структуры с указателем aPtr, и операцию элемента структуры с указателем aPtr, к которому применена операция косвенной адресации.

/* Применение операций элемента структуры

и указателя структуры */

#include <stdio.h>

struct card {

char *face;

char *suit; };

main () {

struct card a;

struct card *aPtr;                                                           

a.face ="Ace";

a.suit ="Spades";

aPtr =&a;

printf("%s%s%s\n%s%s%s\n%s%s%s\n",      

a.face, " of ", a.suit,

aPtr->face, " of ", aPtr->suit,

(*aPtr) .face, " of ", (*aPtr).suit);

return 0; }

Вывод на экран :

Ace of Spades Ace of Spades Ace of Spades

Рис. 1. Использование операций элемента структуры и указателя структуры

 

Использование структур с функциями

Структуры могут передаваться функциям:

· посредством передачи отдельных элементов структуры или передачи всей структуры (вызовом по значению);

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

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

Используя структуру, можно осуществить вызов массива по значению. Для вызова массива по значению создается структура, содержащая массив как элемент. Вызывая структуру по значению, мы и массив вызываем по значению.

Typedef

Ключевое слово typedef предоставляет программисту механизм для создания синонимов (или псевдонимов) для ранее определенных типов данных. Часто используют typedef для того, чтобы дать укороченное имя структурному типу. Например, оператор

typedef struct card Card;

определяет новый тип с именем Card, как синоним типа struct card. Пишущие на С часто используют typedef, чтобы определить тип структуры, при этом отпадает необходимость в имени-этикетке. Например, следующее определение

typedef struct {

char  *face;

char  *suit;} Card;

создает тип Card без использования отдельного оператора typedef. \

Теперь Card можно использовать для объявления переменных типа struct card. Объявление

Card deck [52];

описывает массив, состоящий из 52 структур Card (т.е. переменных типа struct card). Создание нового имени с помощью typedef не создает нового типа; typedef просто определяет новое имя для уже существующего типа, которое может использоваться как псевдоним последнего. Осмысленно выбранный псевдоним позволяет сделать программу само документированной.

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

 

Объединения

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

На различных этапах выполнения программы одни переменные могут оказаться невостребованными, в то время как другие, наоборот, используются только в этой части программы, поэтому объединения экономят пространство вместо того, чтобы впустую тратить память на не использующиеся в данный момент переменные. Элементы объединения могут принадлежать к любому типу. Число байтов, используемое для хранения объединения, должно быть, по крайней мере, достаточным для хранения наибольшего из элементов. В большинстве случаев объединения содержат два или более типа данных. Ссылаться в данный момент времени можно только на один элемент и, соответственно, только один тип данных. Задача программиста — обеспечить, чтобы на данные, хранящиеся в объединении, ссылались как на данные соответствующего типа.

Объединение объявляется с помощью ключевого слова union. Формат объявления тот же, что и в случае структуры. Объявление union

union number { int x; float y; }; означает, что number является типом union с элементами int x и float у. Определение объединения обычно располагается в программе перед функцией main, поэтому определение может использоваться для объявления переменных во всех функциях программы.

Над объединениями можно выполнять следующие операции: присваивание объединения другому объединению того же типа, взятие адреса (&), доступ к элементам объединения с использованием операций элемента структуры и указателя структуры. Объединения не могут сравниваться по тем же самым причинам, что и структуры.

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

union number value = {10};

является допустимой инициализацией переменной value, потому что объединение инициализировано как int, однако объявление следующего вида будет ошибочным:

union number value = {1.43};

Программа на рис. 4 объявляет переменную value типа union number и отображает значения, хранящиеся в объединении, как тип int и как float. Результат выполнения программы зависит от реализации. Вывод программы свидетельствует, что внутреннее представление значения float может значительно отличаться от представления int.

/* Пример объединения */

#include <stdio.h>

union number {

    int x;

    float y;};

int main(){

union number value;

value.x= 100;

printf("%s\n%s\n%s%d\n%s%f\n\n",

              "Put a value in the integer member",

              "and printf both members.", "int: ", value.x, "float: ", value.y);

value.y= 100.0;

printf("%s\n%s\n%s%d\n%s%f\n", "Put a value in the floating member",

              "and printf both members.", "int: ", value.x, "float: ", value.y);

return 0;}

Рис. 4. Вывод на печать значения элемента объединения в формате каждого из типов данных

 

Битовые поля

В С существует возможность задать число битов для хранения элемента структуры или объединения типа unsigned или int посредством определения битовых полей. Битовые поля позволяют лучше использовать память, храня данные в минимально требуемом количестве бит. Элементы — битовые поля должны быть объявлены как int или unsigned.

Рассмотрим следующее определение структуры: *

struct bitCard {

unsigned face : 4; unsigned suit : 2; unsigned color : 1; } ;

Оно содержит три битовых поля типа unsigned — face, suit и color, — используемых для представления колоды из 52 карт. Битовое поле объявляется с помощью двоеточия (:) и целой константы, задающей ширину поля, то есть число бит, выделяемых для хранения элемента, помещаемых после имен элементов типа unsigned или int. Константа, задающая ширину, должна быть целым числом, значение которого можно выбрать от 0 до полного числа бит, используемого для хранения int в вашей системе. Наши примеры проверялись на компьютерах с 2-байтовыми (16-битными) целыми.

Данное выше определение структуры показывает, что элемент face хранится в 4-х битах, элемент suit в 2-х битах, а элемент color в 1-м бите. Число отводимых бит определяется диапазоном значений для каждого элемента структуры. В элементе face хранятся значения величин от 0 (туз) до 12 (король) — в четырех битах могут храниться значения от 0 до 15. В элементе suit хранятся значения величин от 0 до 3 (0 = бубны, 1 = червы, 2 = трефы, 3 = пики) — в двух битах могут храниться значения от 0 до 3. И наконец, в элементе color хранятся значения цвета 0 (красная) или 1 (черная) — в одном бите могут храниться значения 0 или 1.

Программа на рис. 5 создает массив deck, содержащий 52 структуры struct bitCard. Функция fil-lDeck размещает 52 карты в массиве deck, а функция deal распечатывает карты. Заметим, что обращение к битовым полям происходит точно так же, как и к любым другим элементам структуры. Элемент color включен для того, чтобы иметь возможность отображать цвет карты в системах, которые позволяют выводить цвета.

Допускается определение неименованного битового поля; такое поле используется как заполнитель структуры. Например, определение структуры

struct example (

unsigned a : 13;

unsigned : 3;

unsigned b : 4; };

использует неименованное поле шириной в 3 бита — в этих трех битах невозможно что-либо сохранить. Элемент b (в случае, если ваш компьютер имеет двухбайтовые слова) будет размещен в другой ячейке памяти.

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

struct example { unsigned a : 13; unsigned   : 0; unsigned b : 4; }; объявляет битовое поле без имени длиной 0 бит для того, чтобы перескочить через оставшиеся биты (столько, сколько их будет) ячейки памяти, в которой хранится а, и выровнять b по границе следующей ячейки.

/* Пример применения битовых полей */

# include < stdio . h >  

struct bitCard {

unsigned face : 4;

unsigned suit : 2;

unsigned color : 1;

};

typedef struct bitCard Card;

void fillDeck(Card *);

void deal(Card *);

main () {

Card deck[52];

fillDeck(deck);

deal(deck);

return 0; }

void fillDeck(Card *wDeck) {

int i;

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

{ wDeck[i].face = i % 13;

wDeck[i].suit = i / 13;

wDeck [ i ]. color = i / 26; } }

/* Функция deal печатает колоду в две колонки */

/* Колонка 1 содержит карты 0-25, индекс kl */

/* Колонка 2 содержит карты 26-51, индекс к2 */

void deal(Card *wDeck) {

int k1, k2;

for (k1 =0, k2 = k1 + 26; k1 <= 25; k1++, k2++)

{ printf("Card:%3d Suit:%2d Color:%2d",

wDeck[k1].face, wDeck[k1].suit, wDeck[k1].color);

printf("Card:%3d Suit:%2d Color:%2d\n",

wDeck[k2].face, wDeck[k2].suit, wDeck[k2].color); }

}

Рис. 5. Использование битовых полей для хранения колоды карт

Перечислимые константы

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

enum months {JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};

создает новый тип enum months, в котором идентификаторам автоматически присваиваются значения от 0 до 11. Для того чтобы перенумеровать месяцы от 1 до 12, можно задать перечислимый тип следующим образом:

 enum months (JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};

Так как значение первого идентификатора явно устанавливается равным 1, значение остальных увеличивается, начиная с 1, и в результате мы получаем значения от 1 до 12. Идентификаторы в перечислении должны иметь уникальное имя. Значения каждой перечислимой константы можно задавать явно путем присвоения значения идентификатору непосредственно в определении. Несколько элементов в списке перечисления могут иметь одно и тоже целое значение. В программе на рис. 6 перечислимая переменная months используется в структуре for для вывода на печать месяцев года из массива monthName. Отметим, что мы задали monthName[0] как пустую строку '"'. Некоторые программисты, возможно, предпочтут установить для monthName[0] значение ***ERROR***, чтобы показать — произошла логическая ошибка.

/* Применение перечислимого типа */

# include < stdio . h >

enum months {JAN = 1, FEB, MAR, APR, MAY, JUN,JUL, AUG, SEP, OCT, NOV, DEC};

main(){

enum months month;

char *monthName[]={"","January","February","March","April","May","June","July","August","September","October","November","December"};

for(month = JAN;month <= DEC; month++)

printf("%2d%11s\n", month, monthName[month]);}

Рис. 6. Использование перечисления

Порядок выполнения работы

1. Изучите теоретический материал.

2. Напишите программу, согласно заданному в варианте задания условию.

3. Ответьте на контрольные вопросы.

 

ВАРИАНТЫ ЗАДАНИЙ

Вариант 1

Напишите определения для следующих структур и объединений:

a) Структуры inventory, содержащей следующие элементы: символьный массив partName[30], целую переменную partNumber, действительную переменную price, целую переменную stock и целую переменную reorder.

b) Объединения data, содержащего char с, short s, long 1, float f, double d.

c) Структуры с именем address, содержащей символьные массивы streetAddress[25], city[20], state[3] и zipCode[6].

d) Структуры student, содержащей массивы firstName[15] и last-NameЈ15], а также переменную homeAddress типа struct address из вопроса (с).

e) Структуры test, содержащей 16 битовых полей шириной в один бит. В качестве имен битовых полей возьмите буквы от а до р.

Вариант 2

Задано определение структуры и объявлены переменные:

struct customer (

char lastName[15]; char firstName[15] ; int customerNumber;

struct {

char phoneNumber[11];

char address[50];

char city[15];

char state[3];

char zipCode[6]; } personal;

} customerRecord , * customerPtr ;

customerPtr = & customerRecord ;

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

a) Элементу lastName структуры customerRecord.

b) Элементу lastName структуры, на которую указывает customerPtr.

c) Элементу firstName структуры customerRecord.

d) Элементу firstName структуры, на которую указывает customerPtr.

e) Элементу customerNumber структуры customerRecord.

f) Элементу customerNumber структуры, на которую указывает customerPtr.

g) Элементу phoneNumber элемента personal структуры customer-Record.

h) Элементу phoneNumber элемента personal структуры, на которую указывает customerPtr.

i) Элементу address элемента personal структуры customerRecord.

j) Элементу address элемента personal структуры, на которую указывает customerPtr.

к) Элементу city элемента personal структуры customerRecord.

l) Элементу city элемента personal структуры, на которую указывает customerPtr.

m) Элементу state элемента personal структуры customerRecord.

n) Элементу state элемента personal структуры, на которую указывает customerPtr.

о) Элементу zipCode элемента personal структуры customerRecord.

р) Элементу zipCode элемента personal структуры, на которую указывает customerPtr.

Вариант 3

Создайте объединение integer с элементами char с, short s, int i и long 1. Напишите программу, которая вводит значения типов char, short, int, long, и сохраните значения в переменных типа union integer. Каждая переменная-объединение должна быть напечатана как char , short , int и long . Всегда ли значения печатаются правильно?

Вариант 4

Создайте объединение floatingpoint с элементами float f , double d и long double 1. Напишите программу, которая вводит значения типов float , double и long double . Сохраните значения в переменных типа union floatingpoint . Каждая переменная-объединение должна быть напечатана как float , double и long double . Всегда ли значения печатаются правильно?

Вариант 5

Нижеследующая программа вызывает функцию multiple , чтобы определить, является ли целое число, введенное с клавиатуры, кратным некоторому целому числу X. Исследуйте функцию multiple и скажите, чему равно значение X.

/* Программа определяет, является ли целое число кратным х */ # include < stdio . h >

int multiple (int);

main ()

{

int y;

print("Enter an integer between 1 and 32000; "); scanf("%d", &y);

if (multiple(y))

printf("%d is a multiple of X\n", y); else

printf("%d is not a multiple of X\n", y) ;

return 0; }

int multiple(int num) {

int i, mask = 1, mult = 1;

for (i = 1; i <= 10; i++, mask «= 1) if ((num & mask) != 0) ( mult = 0; break ; }

return mult ; }

 6. Что делает следующая программа?

#include <stdio.h>

int mystery(unsigned);

main () {

unsigned x;

printf("Enter an integer: "); scanf("%u", Sx);

printf("The result is %d\n", mystery(x) ) ; return 0; }

int mystery(unsigned bits) {

unsigned i, mask = 1 << 15, total = 0;

for (i = 1; i <= 16; i++, bits «= 1) if ( (bits & mask) == mask) ++total;

return total % 2 == 0 ? 1 : 0; 1

Форма отчёта: отчет выполняется в тетрадях по практическим работам в письменном виде.

Содержание отчета:

1. Тема работы.

2. Цель работы.

3. Выполненные задания.

 

Система оценки: двухбалльная.

 

Контрольные вопросы

 

1. Заполните пропуски в каждом из следующих утверждений.

а)________ — это объединение логически связанных переменных под одним именем.

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

c) Ключевое слово ______  объявляет структуру.

d) Ключевое слово______ используется для задания синонимов ранее определенных типов данных.

e) Ключевое слово_____ используется, чтобы ввести опреде-ление объединения.

f) Имя структуры называют _____  структуры.

g) Обращение к элементу структуры возможно либо с помощью опе-рации , либо с помощью операции ________________________ .

h) ________  является набором целых чисел, представленных

идентификаторами.

2. Установите, являются следующие утверждения верными или неверными; если утверждение неверно, объясните, почему.

a) Структуры могут содержать только один тип данных.

b) Два объединения можно сравнить между собой, чтобы определить, равны ли они между собой.

c) Наличие имени-этикетки у структуры является необязательным.

d)Элементы различных структур должны иметь уникальные имена.

e) Ключевое слово typedef используется для определения новых типов данных.

f) Структуры всегда передаются функциям по ссылке.

g) Структуры нельзя сравнивать.

3. Напишите один или несколько операторов С, выполняющих каж-дое из следующих действий:

a) Определите структуру с названием part, содержащую переменную partNumber типа int и массив partName типа char, значения элементов которого могут иметь длину до 25 символов.

b)Определите, что Part является синонимом типа struct part.

c) Используйте Part для объявления переменной а типа struct part, массива b[10] типа struct part и переменной ptr — указателя на struct part.

d)Считайте с клавиатуры partNumber и partName в отдельныеэлементы переменной а.

e) Присвойте значение переменной а третьему элементу массива b.

f) Присвойте адрес массива b указателю ptr.

g) Выведите на печать значение элемента 3 массива Ь, используяпеременную ptr и операцию указателя структуры для обращения кее элементам.

4.Найдите ошибки в каждом из следующих примеров:

а) Предположим, что struct card была определена как содержащая два указателя на тип char, а именно face и suit. Кроме того, была объявлена переменная с типа struct card и переменная cPtr как указатель на struct card. Переменной cPtr был присвоен адрес переменной с.

printf("%s\n", *cPtr->face);

b) Предположим, было определено, что struct card содержит двауказателя типа char, а именно face и suit. Кроме того, было объявлено, что массив hearts[13] принадлежит к типу struct card. Следующий оператор должен вывести на печать face для десятого элемента.

printf ("%s\n", hearts.face);

c) union values (

char w; float x; double y; 1 v = 11.27};

d) struct person {

char lastName[15]; char firstName[15] ; int age; }

e) Предположим, что struct person была определена как в примере(d), разумеется, с соответствующими исправлениями.

person d;

f) Предположим, было объявлено, что переменная р принадлежитк типу struct person, и переменная с объявлена принадлежащей ктипу struct card.

р = с;

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

 

1. Х.Дейтел Как программировать на C; Пер. с англ. под ред. В. Тимофеева. - М. : БИНОМ, 2000. - 1005 с. : ил.; 24

2. Язык программирования С / Брайан Керниган, Деннис Ритчи ; [пер. с англ. и ред. В. Л. Бродового]. - 2-е изд., перераб. и доп. - Москва [и др.] : Вильямс, 2007. - 289 с.; 23 см.

3. Бьерн Страуструп Язык программирования C++; Пер. с англ. С. Анисимова и М. Кононова под ред. Ф. Андреева, А. Ушакова. - 3. изд. - М. : Binom Pablishers ; СПб. : Нев. диалект, 2004. - 990 с. : ил.; 24 см.

 


Дата добавления: 2020-12-22; просмотров: 77; Мы поможем в написании вашей работы!

Поделиться с друзьями:




Мы поможем в написании ваших работ!