Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Лекция 8: структуры и объединения.

Лекция 1: введение в программирование. | Лекция 2: представление данных в компьютере. | Часть 1: хранение данных в компьютере. | Часть 2: типы данных языка Си. | Часть 1: основные операторы и их приоритеты. | Часть 2: функции. | Лекция 5: Функция main, функции ввода-вывода, препроцессор. | Лекция 6: массивы и строки, библиотечные функции ввода-вывода. | Часть 2: бинарные деревья. | Часть 3: динамическое программирование. |


Читайте также:
  1. II. Структуры среды
  2. IT Анализ структуры
  3. Professional SPA Collection (Профессиональная СПА Коллекция).
  4. Анализ динамики и структуры товарооборота
  5. Анализ наличия, движения и структуры основных фондов.
  6. Анализ организационной структуры, существующей на предприятии
  7. Анализ показателей финансовых результатов (анализ состава и структуры прибыли до налогообложения).

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

Разумеется, такая организация работы с данными была бы неудобна. Для работы с данными, имеющими чёткую организацию, языки высокого уровня предоставляют специальные средства. В языке Си это структуры. Структура представляет собой тип данных в виде упорядоченного набора разнородных переменных. Переменные, из которых составлена структура, называются её полями или элементами. Элементом структуры может быть что угодно, кроме этой же структуры (но не указателя на неё). В общем случае объявление структуры выглядит так: struct <имя структуры>[30] {<поля структуры>} [<объявление переменных>];

Структуры могут объявляться как вне функций, так и внутри них[31].

Данные в структуре, как и в массиве, расположены последовательно, причём поля, объявленные раньше, записываются в младшие байты, а объявленные старше – в старшие. Иными словами, расположение полей в памяти осуществляется в порядке возрастания с появлением новых полей. Но поскольку каждое поле имеет свой размер и свой тип, обращение к элементам по индексу невозможно. Поэтому каждому полю структуры присваивается своё индивидуальное имя. Следует помнить, что каждая переменная, имеющая структурный тип (как и всякая другая переменная) имеет собственное уникальное имя, но имена соответственных полей у них одинаковые. Для обращения к элементу структуры существует оператор. (точка). Слева от точки пишется имя переменной, а справа – поля структуры.

Например:

struct sample

{

short int l;

short int r;

};

 

struct sample subtraction(struct sample);

 

int main()

{

struct sample sound[1024];

/*some code*/

return 0;

}

struct sample subtraction(struct sample source)

{

struct sample temp;

temp.r=source.l-source.r;

temp.l=temp.r;

return temp;

}

В начале программы объявляется новый структурный тип данных sample, соответствующий одному сэмплу 16-битной стерео выборки. Функция subtraction имеет тип struct sample, она вычитает из правого канала левый[32]. Из переменных структурного типа может быть составлен массив – sound в функции main.

Неудобно каждый раз писать длинную конструкцию struct sample. К тому же, если в программе есть несколько структурных типов, то при беглом взгляде на код придётся каждый раз замедляться около слова struct – то есть, читаемость кода от этого ухудшается. Можно было бы использовать define для того, чтобы избавиться от «балласта» в виде struct, но есть способ лучше. Оператор typedef <тип> <синоним> синонимы типов данных. То есть, после строки

typedef char bool;

обозначения char и bool будут абсолютно равноправны. В отношении структур это выглядит так:

typedef struct SAMPLE

{

short int l;

short int r;

} sample;

Теперь вместо struct SAMPLE можно везде писать просто sample. А поскольку имя структуры является необязательным параметром, можно написать и так:

typedef struct

{

short int l;

short int r;

} sample;

Как уже было сказано, элементом структуры не может быть она сама, но моет быть указатель на неё. То есть, конструкция

typedef struct NODE

{

char* data;

struct NODE next;

} node;

является ошибочной, но правильной (и часто применяющейся) будет такая:

typedef struct NODE

{

char* data;

struct NODE* next;

} node;

Представим теперь, что нужно обратиться к символу, лежащему по адресу, указанному в какой-то переменной типа node, на которую ссылается переменная a этого же типа. Подобное обращение выглядит следующим образом:

*((*(a.next)).data);

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

a->next->data;

Гораздо короче и понятнее. А обращение

a->next->next->next->data;

Без использования -> выглядело бы так:

*((*((*((*(a.next)).next)).next)).data);

Данный пример делает очевидными преимущества применения оператора ->.

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

Синтаксис объединений такой же, как и структур, но вместо «struct» пишется «union». Например:

typedef union

{

int eax;

struct

{

char l;

char h;

} a;

short int ax;

} cpu_reg;

Это объединение имитирует структуру 32-разрядного регистра центрального процессора. Обращение к его полям выглядит так:

a.eax=0x0000000;

a.a.l=0xff;

a.a.h=0xff;

a.eax=0xffff0000;

a.ax=0xffff;

Из этого примера следует первое применение объединений: тип данных для хранения значений, полученных из регистров процессора. Но это не единственное применение.

Объединения можно использовать для осуществления присваивания между переменными разных типов в обход системы преобразования типов языка Си. Допустим, нам надо вывести на экран двоичное представление числа с плавающей точкой. Из примера, приведённого в лекции 7 очевидно, как это сделать с целым числом: с помощью сдвига и целочисленного деления. Однако попытка присвоить целому числу значения с плавающей точкой приведёт к преобразованию типа и до неузнаваемости изменит двоичное представление числа. Значит, нужен способ передавать значения между типами с сохранением двоичного представления. Для этого можно создать объединение:

typedef union

{

float f;

unsigned int i;

} transformer;

Теперь мы можем написать конструкцию вида:

transformer buffer;

unsigned int number;

buffer.f=source;

number=buffer.i;

Считая, что нам уже дана переменная source типа float, в переменной number будет находится число, в двоичном представлении эквивалентное source. После этого написать функцию вывода на экран не составляет труда.


Дата добавления: 2015-11-13; просмотров: 41 | Нарушение авторских прав


<== предыдущая страница | следующая страница ==>
Лекция 7: операторы выбора, безусловный переход, циклы.| Лекция 9: связные списки.

mybiblioteka.su - 2015-2024 год. (0.011 сек.)