ТОП 10:

Объявление структурных типов.



Структуры языка Си

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

Структуры ещё не являются полноценным типом данных, потому что тип данных это множество значений + множество операций с ними. Операции со структурами можно выполнять с помощью функций, но эти функции определяются отдельно от структурного типа.

Объявление структурных типов.

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

struct complex {
float real ;
float imag;
} ;

или можно было описать в одной строке – float real, imag;

Выше объявлен структурный тип с названием struct complex

Struct complex z;

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

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

struct my_struct {

struct my_struct ms;

} ;

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

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

Struct my_struct {
float real;
float image;
} ; z1,z2, zz[10];

Объявления структурных типов могут быть размещены на глобально м или локальном уровне. На локальном уровне они должны быть помещены в начало блока вместе с остальными определениями. Расположение объявления влияет на область действия объявления. Можно создавать структурный тип без имени. Это имеет смысл в том случае, если нам нужно только в одном месте программы определить переменную этого структурного типа. Анонимным структурным типом больше нигде нельзя будет воспользоваться. Имена структурных типов могут совпадать с именами своих полей и с именами переменных.

struct name {
int name;
} ; name;

Но делать этого не нужно.

Операции со структурными переменными

Все структурные типы обладают рядом общих операций

1) Взятие адреса структурной переменной
struct my_struct {
int field1;
float field2:
} x;
&x

2) Переменной структурного типа можно присваивать значение структурной переменной того же типа. Выполняется побайтовое копирование.

3) Переменную структурного типа можно передавать в качестве аргумента в функцию и возвращать из функции. Можно так же передавать указатель на структурную переменную
my_struct f(my_struct ms)

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

 

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

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

struct complex {

float real;

float imag;

};

struct complex z={1.2, -5};

struct complex z1={1};

Статические глобальные структурные переменные по обычным правилам обнуляются.

 

struct test {

int a[3];

int b;

};

struct test z2={1,2,3,4};

// struct (a — 1,2,3; b — 4)

Struct test z3 = {{1,2},4};

Есть возможность определять массивы структур, причём эти массивы можно инициализировать по обычным правилам.

struct complex z4[] = {{1}, {2,3}, {4,5}};

struct complex z5[3]={1,2,3,4,,5};

z5[3]={{1,2},{3,4},{5}};

Операции со структурами

Доступ к полям структуры

Структуры – это агрегатный тип, который строится из данных разных типов. Каждое поле (элемент структуры) имеет имя.

 

Есть два способа обратиться к отдельному полю. Если мы имеем структурную переменную, то для обращения к отдельным полям используется операция «.» (точка).

struct complex z, *pz=&z;

Z.real, z.imag

(*pz).real, (*pz),imag

Если доступ к полю осуществляется через указатель, то применяется операция «стрелка» (->)

pz->real, pz->imag

Вычисление размера структуры

К структурному типу и структурным переменным можно применять операцию sizeof.

Sizeof(struct, complex)

Sizeof(z)

Вообще говоря, размер структурного типа можно посчитать как сумму размеров его полей, однако реально этот размер может оказаться больше. Процессор хорошо обрабатывает информацию, которая представлена машинными словами. Однако, структура может содердать поля, которые занимают одно из машинных слов не полностью. Предположим, что в системе с 4-байтнм машинным словом определён структурный тип. Массив s займёт оно машинное слово и один байт из следующего. Если экономить память, то поле b так же будет расположено в двуз машинных словах. Такое расположение данных снизит скорость обработки. Обычно для тсруктур данных выполняется выравнивание полей на границу машинного слова. При этом могут быть небольшие потери памяти, но зато серьёзный выигрыш в производительности. В результате размер структурного типа будет несколько больше, чем сумма размеров его полей.

struct test {

char s[5];

int b;

}

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

Оператор typedef (type definition)

После определения структурного типа, его везде нужно использовать вместе со служебным словом struct. Это утяжеляет текст программы, поэтому в Си добавлен оператор typedef, позволяющий создавать простые синонимы для имён структурных типов.

typedef struct {int x; int y;} point;

typedef struct {point p1,p2;} rect;

int square(rect z) {
return(z.p2.x-z.p1.x)*(z.p2.y-z.p1.y);
}

Файловый ввод-вывод

Потоки

Программа может считывать данные из каких то источников и выводить данные на экран, файл или другие устройства. Чтобы управлять этими операциями, система программирования должна предлагать соответствующие функции. Так как имеется большое разнообразие устройств ввода-вывода, то создать библиотеку, позволяющую работать с любым из них невозможно. Кроме того, ориентация на конкретные устройства будет ограничивать переносимость программы. При разработке языка Си было предложено для выполнения операций ввода-вывода использовать понятие абстрактного потока. Т.е. программа всегда вводит или выводит данные из потоков, которые не привязаны к конкретным устройствам и являются универсальными. Взаимодействие с физическими устройства обеспечивается ОС с помощью драйверов. Система программирования должна обладать функциями, которые позволяют работать с потоками. В Си – две таких библиотеки. Библиотека стандартного уровня предлагает набор высокоуровневых функций, позволяющих программисту работать с потоками, не вникая в мелкие детали взаимодействия. Библиотека системного уровня позволяет программировать тонкие детали операций ввода-вывода. Ею пользуются в основном при системном программировании, когда необходимо добиться максимально высокой скорости выполнения операций, экономии памяти и т.д.

Fopen, fread, fwrite – стандартный уровень

Open, read, write – системный уровень.

Различают буферизованные и не буферизованные операции ввода-вывода. Буферизация подразумевает, что обмен информации ведётся через некоторую промежуточную область памяти, которая называется буфером и создаётся в оперативной памяти. Буферизация позволяет повысить быстродействие операций ввода-вывода, потому что они выполняются реже и бОльшими блоками.

Потоки различаются по типу передаваемой информации на текстовые и двоичные. В разных операционных системах имеются небольшие отличия при сохранении текстовой информации. Например, в Windows конец строки обозначается двумя кодами, а в Linux – одним. В Windows есть специальный признак конца файла, в Linux – нет. Поэтому, когда происходит чтение или запись текстовой информации, итоговые файлы будут немного различаться. При обработке текстовых файлов из Linux в Windows могут быть добавлены дополнительные коды (и наоборот). Для текстовых файлов эти операции безопасны, однако по внешнему виду отличить текстовый файл от двоичного – невозможно. Например, в двоичном файле могут встретиться коды 10,13 (двоичные коды, связанные с переводом строки) которые никак не связаны с переводом строки. Очевидно, что никакие преобразования здесь недопустимы. Функции ввода и вывода по-разному работают с текстовыми и двоичными потоками.

Дескрипторы файлов (потоков).

Когда программа загружается в память для выполнения, для неё создаётся таблица открытых файлов. Программа одновременно может работать с несколькими файлами, часть из них может использоваться для чтения, часть для записи. Каждому файлу соответствует свой поток. Каким образом библиотечные функции узнают, с каким из потоков они должны взаимодействовать? Для этого операционная система с каждым потоком связывает дескриптор (handle). Реально, дескриптор это номер потока. При загрузке программы автоматически создаётся несколько стандартных потоков (с дескрипторами от 0 до 4), поэтому потоки, открываемые программистом будут иметь большее значение дескрипторов (от 5 и дальше).

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

Библиотечный файл stdio.h

typedef struct {

unsigned flags;

char fd; /*file descriptor*/

unsigned char* buffer;

unsigned char* curp;

…}FILE;

Значит давайте запишем следующее, чтоооо

Я вам приводил дескриптор от 0 до 4 и есть структуры типа …….

Для стандартных потоков ввода-вывода в Си определяются стандартные имена для их структур типа файла.

Stdin – стандартный поток ввода (дескриптор 0), по умолчанию связан с клавиатурой.

Stdout – стандартный поток вывода, по умолчанию связан с дисплеем (дескриптор 1).

Stderr – стандартный поток вывода ошибок (дескриптор 2(наверн)).

Stdout может быть перенаправлен например в память, но если в программе произошла ошибка, то программист может не увидеть сообщение. Поэтому stderr отличается от stdout тем, что его нельзя перенаправить.

Stdaux – дополнительный поток для коммуникационных сетей

Stdprn – стандартный поток вывода на печать.

fprintf(FILE* fp, char* format,…)

printf -> fprintf(stdout,…)

 

fprintf(stderr, …)

Открытие и закрытие файлов

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

1) Получить от операционной системы ещё не занятый дескриптор потока.

2) Выделить память для буфера

3) Заполнить поля структуры типа файла

4) И т.д…

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

Для открытия потоков (файлов) используется функция fopen со следующим прототипом:

FILE*fopen(имя_файла, режим_открытия). Оба аргумента – строки.

При задании имени файла может быть указан путь. Нужно не забывать удваивать бэк-слеши.

«C:\\programs\\data\\mydata.txt»

Если пусть не указан, то файл ищется в той же папке, где находится программа. Строка режим открытия описывает особенности использования потоков. Поток может быт открыт для чтения, записи или дозаписи. Если файл не существует, то в режиме чтения будет ошибка, а в режиме записи или дозаписи будет создан новый файл. Есть менее ограничивающие режимы. «r+» - открыть существующий файл для чтения и записи, «w+» - создать новый файл для чтения и записи. В режимах w w+ , если файл уже существовал, то старое его содержимое будет затёрто и запись начнётся с начала файла. «a+» — дополнить или создать файл с возможностью чтения и записи. Кроме этого, можно указать, каким должен рассматриваться файл – бинарным или текстовым. Для этого используется символы-делители (b,t), которые дописываются к рассмотренным режимам («w+t», «wb»).

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

int fclose(FILE*fp). При успешном завершении функция возвращает 0, при ошибке – не ноль.

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

FILE *fp;

fp = fopen(“my_file.txt”, “r”);

if (fp==NULL) {

puts(“Ошибка”);

exit(1);

}

Или if (fp=fopen(…)) {

}

Else {ошибка}

Блочный ввод-вывод

Часто приходится работать с массивами структурных переменных. Если такой массив нужно записать в файл, то можно использовать операции форматного вывода, а именно, создать цикл, в котором с помощью fprintf в файл будут записываться отдельные поля структурных переменных. Аналогично при вводе (чтении информации из файла). Получается трудоёмкая операция, которая может быть упрощена. Можно посчитать размер массива структурных переменных и, зная адрес начала и размер, считать весь массив, как блок байт памяти. Тогда весь массив можно записать в файл одним блоком, и точно так же восстановить блок памяти при чтении из памяти. Для этого используются функции fread и fwrite.

unsigned fread(void *ptr, unsigned size, unsigned n, FILE *fp).

ptr – указатель на блок памяти, с которого будет считываться нформация, size – размер одного элемента данных, N – количество элементов данных. Функция возвращает количество прочитанных элементов.

Аналогичные элементы у функции fwrite, она возвращает количество записанных элементов.

Основные понятия ООП.

ООП основано на методе моделирования, в основе которого лежит понятие объекта. Объектная модель строится на 4 главных принципах:

1) Абстрагирование
Абстрагирование – процесс отвлечения от некоторых свойств и отношений между объектами. Рассматривая предметную область, мы выделяем в ней существенные и несущественные факторы. Отбрасывая в дальнейшем из рассмотрения несущественные факторы, мы выполняем абстрагирование. Абстрагирование – одно из средств построения классификаций. Например, понятие «занятие» является абстракцией, потому что в нём мы отказываемся от того, какого типа эти занятия. Понятие «лекция» является абстракцией, потому что мы отвлекаемся от содержания предмета и акцентируем внимание на форме занятий. На основе таких абстракций мы можем построить классификацию: лекции, лабораторные, практические и т.д. Сразу построить хорошую модель путём абстрагирования удаётся не всегда, поэтому процесс моделирования циклический. Если модель работает плохо, то возможно мы отбросим существенный фактор. В программирование абстрагирование внедрялось в несколько этапов. Сначала появились подпрограммы (функции). Каждую подпрограмму можно рассматривать, как параметризованную абстракцию части кода. При развитии структурного программирования появились идеи абстракции данных. Например, очереди или стек – это абстракции, потому что они говорят о внешних свойствах структур данных, а не о том, как они устроены внутренне. ООП привнесло новый принцип абстрагирования. В основе лежат классы объектов, которые сочетают абстракцию кода и абстракцию данных.

2) Модульность
Модульность тесно связана с декомпозицией задач. Это разбиение большой задачи на совокупность небольших. Решение большой задачи складывается из решения маленьких. Это упрощает решение задач, потому что небольшую задачу проще решить и легче отладить программу. Однако такое механическое сложение подзадач работает не всегда. Если моделируемая система сложная, то она помимо свойств своих объектов имеет дополнительные системные свойства (например, движение, складывающееся из отдельных кадров в кино). В программировании было предложено несколько принципов разбиения на модули. В начале в роли модуля выступала функция (или подпрограмма). Затем, когда появилась возможность раздельной компиляции в роли модулей стали выступать отдельные файлы. В ООП модуль образуется описанием класса объектов.

3) Инкапсуляция
Инкапсуляция основана не нескольких принципах.
1) Все объекты одного класса ведут себя одинаково. В программах это означает, что они имеют одни и те же методы.
2) Каждый объект хранит информацию о своём состоянии. Состояние объекта - это набор значений и свойств объекта.
3) Состояние объекта может меняться
4) Состояние объекта может влиять на его поведение
5) Состояние объекта может меняться только в результате выполнения его методов.

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

Структуры языка Си

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

Структуры ещё не являются полноценным типом данных, потому что тип данных это множество значений + множество операций с ними. Операции со структурами можно выполнять с помощью функций, но эти функции определяются отдельно от структурного типа.

Объявление структурных типов.

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

struct complex {
float real ;
float imag;
} ;

или можно было описать в одной строке – float real, imag;

Выше объявлен структурный тип с названием struct complex

Struct complex z;

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

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

struct my_struct {

struct my_struct ms;

} ;

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

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

Struct my_struct {
float real;
float image;
} ; z1,z2, zz[10];

Объявления структурных типов могут быть размещены на глобально м или локальном уровне. На локальном уровне они должны быть помещены в начало блока вместе с остальными определениями. Расположение объявления влияет на область действия объявления. Можно создавать структурный тип без имени. Это имеет смысл в том случае, если нам нужно только в одном месте программы определить переменную этого структурного типа. Анонимным структурным типом больше нигде нельзя будет воспользоваться. Имена структурных типов могут совпадать с именами своих полей и с именами переменных.

struct name {
int name;
} ; name;

Но делать этого не нужно.







Последнее изменение этой страницы: 2016-04-18; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 34.226.244.70 (0.018 с.)