ТОП 10:

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



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

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 {ошибка}







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

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