Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual_Studio_2010

.pdf
Скачиваний:
109
Добавлен:
03.03.2016
Размер:
5.94 Mб
Скачать

записываться в файл. Второй и третий параметры перемножаются, поэтому их можно поменять местами.

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

Возможный вариант выполнения программы при записи информации в двоичный файл представлен на рис.13.6.

Рис. 13.6. Вариант записи информации в двоичный файл

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

Рис. 13.7. Результат чтения из двоичного файла

231

Задание6

1.Перезапишите информацию из файла data12.dat в двоичный файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа. Символьные массивы полей структуры задайте с помощью указателей.

2.Запись данных произведите в двоичный файл с различными расширениями: .txt, .bin, .doc. Определите размеры этих файлов в байтах.

3.Напишите программу записи массива структур в двоичный файл и чтения из него всей структуры с выводом результатов на консоль.

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

1.Как определяется структура в языке С?

2.Как объявляется структура в языке С?

3.Какими способами можно объявить новые структурные переменные?

4.Какие форматы используются для доступа к элементам структуры?

5.Что такое вложенная структура?

6.Какой уровень вложенности структур поддерживается стандартом С89?

7.Как объявляется массив структур?

8.Как объявляется указатель на структуру?

9.Как инициализируется указатель на структуру?

10.Как осуществляется инициализация полей структуры, определенных как символьные массивы?

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

12.Является ли тег структуры именем ее типа?

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1.Керниган Б. У. Язык программирования С : пер. с англ./Б. У. Керниган, Д. М. Ритчи. – 2-е изд. – М. : Вильямс, 2007. – 304 с.

2.Шилдт Г. Полный справочник по С : пер. с англ./Г. Шилдт. – 4-е изд. –

М. : Вильямс, 2007. – 704 с.

3.Демидович Е. М. Основы алгоритмизации и программирования. Язык Си : учеб. пособие/Е. М. Демидович. – 2-е изд., испр. и доп. – СПб. : БХВ-

Петербург, 2008. – 440 с.

4.Дейтл Х. М. Как программировать на С : пер. с англ./Х. М. Дейтл, П. Дж. Дейтл. – 4-е изд. – М. : Бином-Пресс, 2006. – 912 с.

5.Прата С. Язык программирования С. Лекции и упражнения : пер. с англ. / С. Прата. – 5-е изд. – М. : Вильямс, 2006. – 960 с.

232

Тема 14

ОБЪЕДИНЕНИЯ И ПЕРЕЧИСЛИМЫЕ ТИПЫ В ЯЗЫКЕ С

Рассматриваются вопросы создания и использования объединений и перечислимых типов в языке программирования С.

ТЕОРЕТИЧЕСКАЯ ЧАСТЬ

1.1. Объединения

Объединение (union) – это тип, который позволяет хранить различные типы данных в одном пространстве памяти (но не одновременно) [1].

Объединения образуются во многом подобно структурам. Существуют шаблоны объединений и переменные типа объединения. Они могут быть определены с помощью одного или двух действий, причем в последнем случае используется дескриптор объединения [1].

Пример шаблона объединения с дескриптором hold:

union hold { int digit; double bigf; char letter;

};

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

Пример определения трех переменных объединения:

union hold fit; // переменная объединения типа hold union hold save[10]; // массив из 10 переменных объединения union hold *ptr; // указатель на переменную типа hold

Первое объявление определяет единственную переменную fit. Компилятор выделяет пространство памяти, достаточное для того, чтобы хранить наибольший из описанных вариантов, а именно тип double, который требует обычно 8 байт. Второе объявление save[10] создает массив с 10 элементами, каждый из которых имеет размер в 8 байт. В третьем объявлении указатель может содержать адрес объединения hold.

Рассмотрим варианты инициализации объединения [1]:

union hold valA;// создали переменную valA по шаблону union hold valA.letter = 'R';

union hold valB = valB;// инициализация одного объединения другим union hold valC = {88};// инициализация числового элемента

Рассмотрим варианты использования объединения:

union hold fit;

fit.digit = 23; // число 23 хранится в переменной fit, 2 байта fit.bigf = 6.78; // число 23 затерто, хранится 6.78, 8 байтов fit.letter = 'h';// число 6.78 затерто, хранится символ h,1 байт

233

Операция точки показывает, какой тип данных используется в текущий момент [1]. За один раз запоминается только одно значение. Нельзя одновременно хранить значение типа char и значение типа int, даже если для этого имеется достаточно пространства (памяти для 8 байт). Следить за тем, какие значения на текущий момент хранятся в объединении, входит в обязанности программиста. Бывает, что на различных этапах выполнения программы одни переменные могут быть не нужны, в то время как другие, наоборот, используются только в текущей части программы, поэтому объединения экономят пространство, вместо того чтобы тратить память на не использующиеся в данный момент переменные.

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

int x;

ptr = &fit;

x = ptr -> digit;

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

Фактически объединение является структурой, в которой все элементы имеют нулевое смещение от ее начала. Она имеет достаточную длину, чтобы в нее поместился самый длинный элемент, и при этом выравнивание выполняется правильно для всех типов данных [2]. Над объединением разрешено выполнять те же операции, что и над структурой: присваивать или копировать как единое целое, брать его адрес и обращаться к отдельным элементам.

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

Смысловое отличие объединения от структуры состоит в том, что записать информацию в объединение можно с помощью одного из его элементов, а выбрать данные – посредством другого элемента того же объединения [3].

К объединениям может быть применен оператор typedef, после чего можно вводить обозначения объединяющих типов, не требующие служебного слова union. Рассмотрим пример:

typedef union data

{

char str[80]; int a; double x;

} new_data;

Определения новых переменных (например, student1, student2) объединений будут выглядеть таким образом:

234

new_data student1, student2;

Объединения не относятся ни к скалярным данным, ни к данным агрегирующих типов [3]. Они не могут сравниваться операциями == и != по тем же самым причинам, что и структуры, поскольку элементы объединения необязательно хранятся в последовательных байтах памяти.

Объединения часто используются для специального преобразования типов, поскольку к находящимся в них данным можно обращаться разными способами [4].

1.2. Перечислимые типы

Перечислимый тип (enumerated type) служит для объявления символических имен, представляющих целочисленные константы [1]. Можно сказать, что это тип данных, заданных списком принадлежащих ему значений.

Назначение перечислимых типов заключается в том, чтобы повысить удобочитаемость программы [1]. Синтаксис в этом случае аналогичен тому, который используется для описания структур.

Примеры объявления перечислимого типа:

enum spectrum {red, orange, yellow, green, blue, violet}; enum spectrum color;

Первое объявление устанавливает spectrum как имя дескриптора, который позволяет использовать enum spectrum в качестве имени типа. Второе объявление делает color переменной этого типа. Идентификаторы, заключенные в фигурные скобки, перечисляют возможные значения, которые может принимать переменная spectrum. Соответственно возможными значениями color выступают red, orange, yellow и т. д. Но эти возможные значения являются целочисленными, т. е. 0, 1, 2, 3, 4, 5. Другими словами, значения в enum начинаются с 0, если не задано иное число, и всегда увеличиваются на 1.

В общем случае перечислимые константы имеют тип int, но перечислимые переменные не так жестко привязаны к целочисленному типу данных, поскольку он может содержать перечислимые константы. Например, перечислимые константы переменной spectrum имеют диапазон [0; 5] (как в массиве), поэтому компилятор может выбрать тип unsigned char для представления переменной color.

В языке программирования С к перечислимой переменной можно применять операции инкрементирования ++ и декрементирования ––. Например,

for (color = red; color <= violet; ++color)

{

;

}

По умолчанию константам в перечислимом списке присваиваются целые значения – 0, 1, 2 и т. д. В то же время возможны присваиваемые значения, например

enum levels {low = 100, medium = 500, high = 2000};

235

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

enum feline {cat, lynx = 10, puma, tiger};

В этом случае cat (кошка) получает значение 0 по умолчанию, lynx (рысь), puma (пума), tiger (тигр) – соответственно 10, 11, 12.

Перечисления особенно полезны там, где не требуется преобразования значений (целого типа) в имена (массив символов), в частности используются в компиляторах для создания таблицы соответствия символов [4].

ПРАКТИЧЕСКАЯ ЧАСТЬ

Пример 1. Для переменной типа объединения предусмотреть ввод и вывод элементов ее полей.

Программный код решения примера

#include <stdio.h>

#include <conio.h> #include <string.h> #define N 79

union hold

{

char str[N+1]; double bigf; char ch;

int digit;

};

int main (void) { double D2; int i = 0,

digit2;

char str2[80], ch2;

union hold fit, *PTR = &fit;

//PTR = &fit; // вариант взятия адреса

printf("\n\t Fields of the \"union\":\n \

1) string, 2) double, 3) character, 4) integer\n");

do {

printf("\n Enter %d field of the \"union\": ", i+1); _flushall();

if (i == 0)

{

gets_s(str2, N);

236

strcpy_s(PTR->str, strlen(str2) + 1, str2); printf(" The first field: %s\n", PTR->str); i++;

}

else if (i == 1)

{

scanf_s("%lf", &D2);

printf(" The second field: %1.4f\n", PTR->bigf = D2); i++;

}

else if (i == 2)

{

scanf_s("%c", &ch2);

printf(" The third field: %c\n", PTR->ch = ch2); i++;

}

else

{

scanf_s("%d", &digit2);

printf(" The fourth field: %d\n", PTR->digit = digit2); i++;

}

} while (i < 4);

printf("\n 1 field: %s\n 2 field: %1.4f\n \ 3 field: %c\n 4 field: %d\n", \

PTR->str, PTR->bigf, PTR->ch, PTR->digit);

printf("\n Press any key: ");

_getch(); return 0;

}

Результат выполнения программы показан на рис.14.1.

237

Рис. 14.1. Результат заполнения полей объединения

Как видно из рис. 14.1, заполнение четырех полей объединения возможно поочередно. После «прохода» всех полей строчные поля не сохранились.

Задание1

1.В программе переставьте очередность полей объединения. Проанализируйте результат выполнения измененной программы.

2.Определите размерность объединения с помощью оператора sizeof().

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

4.Вместо операции стрелка примените операцию точка.

5.С помощью оператора typedef создайте переменную объединения с именем compX, где Х – номер компьютера, на котором выполняется лабораторная работа.

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

Программный код решения примера

#include <stdio.h>

#include <conio.h>

// #define _USE_MATH_DEFINES

#include <math.h>

const double pi = 3.14159265358979;

238

int main (void)

{

double Ltr, AK;

struct figure

{

double area, perimeter; int type;

union select { double R[3]; // circle double E[2]; // ellipse

double Trap[3]; //trapezium

}geom_fig;

}geom, *PTR = &geom;

printf("\n Figures: 1 - Circle, 2 - Ellipse, 3 - trapezium"); printf("\n\n Select figure: ");

scanf_s("%d", &(PTR->type));

switch (PTR->type)

{

case 1:

PTR->geom_fig.R[0] = 5.0; // радиус круга

PTR->geom_fig.R[1] = 1.5; // x0 PTR->geom_fig.R[2] = 2.5; // y0

PTR->area = pi*(PTR->geom_fig.R[0])*(PTR->geom_fig.R[0]);

PTR->perimeter = 2*pi*(PTR->geom_fig.R[0]); printf("\n %d) Circle:\n R = %1.4f, \

x0 = %1.4f, y0 = %1.4f, area = %1.4f, L = %1.4f\n", \ PTR->type, PTR->geom_fig.R[0], PTR->geom_fig.R[1], PTR-

>geom_fig.R[2], \

PTR->area, PTR->perimeter); break;

case 2:

PTR->geom_fig.E[0] = 5.0; // a - большая полуось PTR->geom_fig.E[1] = 4.0; // b - малая полуось

PTR->area = pi*(PTR->geom_fig.E[0])*(PTR->geom_fig.E[1]); PTR->perimeter = 2.0*pi*(PTR->geom_fig.E[0])*\ (PTR->geom_fig.E[0] - PTR->geom_fig.E[1])/(PTR->geom_fig.E[0]);

printf("\n %d) - Ellipse:\n a = %1.4f, b = %1.4f, \ area = %1.4f, L = %1.4f\n", \

PTR->type, PTR->geom_fig.E[0], PTR->geom_fig.E[1], \

PTR->area,PTR->perimeter); break;

case 3:

PTR->geom_fig.Trap[0] = 12.0;// AD

PTR->geom_fig.Trap[1] = 7.0; // BD PTR->geom_fig.Trap[2] = 5.5; // h

239

AK = (PTR->geom_fig.Trap[0] - PTR->geom_fig.Trap[1])/2.0; Ltr = (PTR->geom_fig.Trap[0] + PTR->geom_fig.Trap[1]) + \

2.0*sqrt(AK*AK + (PTR->geom_fig.Trap[2])*(PTR->geom_fig.Trap[2])

);

PTR->area = 0.5*(PTR->geom_fig.Trap[0] + PTR- >geom_fig.Trap[1])*\

(PTR->geom_fig.Trap[2]); PTR->perimeter = Ltr;

printf("\n %d) Trapezium:\n AD = %.4f, BC = %.4f, h = %.4f, \ Area = %1.4f, L = %1.4f\n", \

PTR->type, PTR->geom_fig.Trap[0], PTR->geom_fig.Trap[1], \ PTR->geom_fig.Trap[2], PTR->area, PTR->perimeter);

break;

default :

printf("\n\t Error! Break\n"); break;

}

printf("\n Press any key: ");

_getch(); return 0;

}

В программе одним из полей структуры geom является объединение. В зависимости от выбора типа – метки активного элемента type – происходит вывод на консоль параметров фигуры, ее площади и длины граничной линии (периметр трапеции и т. д.). Компонент type используется для указания, какой из полей объединения geom_fig является активным в данный момент. Подобную структуру называют переменной структурой.

Определение числа (M_PI) может быть выполнено с помощью препроцессорного определения #define _USE_MATH_DEFINES и подключения математической библиотеки #include <math.h>. В программе это определение числа закомментировано, используется прямое определение числа с помощью типа const double.

Результат выполнения программы представлен на рис.14.2.

240

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