Главная страница
Навигация по странице:

  • Выделение памяти для структур.

  • Курс на Си. Подбельский. Курс программирования на Си. В., Фомин С. С. Курс программирования на языке Си Учебник


    Скачать 1.57 Mb.
    НазваниеВ., Фомин С. С. Курс программирования на языке Си Учебник
    АнкорКурс на Си
    Дата18.02.2023
    Размер1.57 Mb.
    Формат файлаdocx
    Имя файлаПодбельский. Курс программирования на Си.docx
    ТипУчебник
    #943863
    страница19 из 42
    1   ...   15   16   17   18   19   20   21   22   ...   42
    Глава 6

    СТРУКТУРЫ И ОБЪЕДИНЕНИЯ

      1. Структурные типы и структуры

    Производные типы. Из базовых типов (и типов, определенных программистом) можно формировать производные типы, к которым относятся: указатели, массивы, функции, структуры и объединения. Указатели, массивы, структуры и объединения являются производ­ными типами данных. Иногда в качестве производного типа в ру­ководствах по языку Си указывают строки, однако напоминаем, что в соответствии с синтаксисом языка строковый тип в нем отсутству­ет. Строка определяется как частный случай одномерного массива, то есть как массив с элементами типа char, последний элемент ко­торого равен '\0'.

    Указатели и массивы подробно рассмотрены в предыдущих гла­вах. Эта глава посвящена структурам и объединениям. Прежде чем рассматривать их особенности и возможности, введем некоторые терминологические соглашения стандарта, которые не всегда по­нятны сами собой.

    Данные базовых типов (int, float, ...) считаются скалярными дан­ными. Массивы и структуры являются агрегирующими типами данных, в отличие от объединений и скалярных данных, которые относятся к неагрегирующим типам. С массивами и скалярными данными мы уже хорошо знакомы, поэтому различие между агре­гирующими и неагрегирующими типами поясним на примере мас­сивов и скалярных данных базовых типов. Обычно агрегирующий тип включает несколько компонентов. Например, массив long B[12] состоит из 12 элементов, причем каждый элемент имеет тип long. Язык Си позволяет определять и одноэлементные массивы. Так, массив float A[1] состоит из одного элемента А[0]. Однако одно­элементные массивы есть слишком частный случай обыкновенных массивов со многими элементами. Итак, в общем случае массив есть агрегирующий тип данных, состоящий из набора однотипных элементов, размещенных в памяти подряд в соответствии с ростом индекса. Для агрегирующего типа данных (например, для массива) в основной памяти ЭВМ выделяется такое количество памяти, что­бы сохранять (разместить) одновременно значения всех элементов. Этим свойством массивов мы уже неоднократно пользовались. На­пример, следующее выражение позволяет вычислить количество элементов массива B[ ]:

    sizeof (B) / sizeof (B [0])

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

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

    • название товара (char*);

    • оптовая (закупочная) цена (long);

    • торговая наценка в процентах (float);

    • объем партии товара (int);

    • дата поступления партии товара (char [9]).

    В перечислении элементов структуры «товары на складе» добав­лены выбранные программистом типы этих элементов. В соответ­ствии со смыслом компоненты могут иметь любой из типов дан­ных, допустимых в языке. Так как товаров на складе может быть много, для определения отдельных структур, содержащих сведения о конкретных товарах, программист вводит производный тип, на­зываемый структурным. Для нашего примера его можно ввести, например, так:

    struct goods {

    char* name; /* Наименование */

    long price; /* Цена оптовая */ float percent; /* Наценка в % */ int vol; /* Объем партии */ char date [9]; /* Дата поставки партии */ };

    Здесь struct - спецификатор структурного типа (служебное сло­во); goods - предложенное программистом название (имя) структур­ного типа. (Используя транслитерацию английского термина «tag», название структурного типа в русскоязычной литературе по языку Си довольно часто называют тегом.) В фигурных скобках размеща­ются описания элементов, которые будут входить в каждый объект типа goods. Итак, формат определения структурного типа таков:

    struct имя_структурного_типа { определения_элементов };

    где struct - спецификатор структурного типа; имя_структурного_ типа - идентификатор, произвольно выбираемый программистом; определения_элементов - совокупность одного или более описаний объектов, каждый из которых служит прототипом для элементов структур вводимого структурного типа.

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

    Конструкция struct имя_структурного_типа играет ту же роль, что и спецификаторы типов, например double или int. С помощью struct goods можно либо определить конкретную структуру (как, например, объект структурного типа struct goods), либо указатель на структуры такого типа. Пример:

    /* Определение структуры с именем food */ struct goods food;

    /* Определение указателя point_to на структуры*/ struct goods *point_to;

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

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

    typedef struct {определения_элементов} обозначение_структурного_типа;

    Пример:

    typedef struct

    { double real;

    double imag;

    }

    complex;

    Приведенное определение вводит структурный тип struct {double real; double imag;} и присваивает ему обозначение (название, имя) complex. С помощью этого обозначения можно вводить структуры (объекты) так же, как с обычным именем структурного типа (напри­мер, struct goods в предыдущем примере). Пример:

    /* Определены две структуры: */ complex sigma, alfa;

    Структурный тип, которому программист назначает имя с по­мощью typedef, может в то же время иметь второе имя, вводимое стандартным образом после служебного слова struct. В качестве примера введем определение структурного типа «рациональная дробь». Будем считать, что рациональная дробь - это пара целых чисел (m;n), где число n отлично от нуля. Определение такой дроби:

    typedef struct rational_fraction

    { int numerator; /* Числитель */ int denominator; /* Знаменатель */

    } fraction;

    Здесь fraction - обозначение структурного типа, вводимое с по­мощью typedef. Имя rational_fraction введено для того же струк­турного типа стандартным способом. После такого определения структуры типа «рациональная дробь» могут вводиться как с по­мощью названия fraction, так и с помощью обозначения struct rational_fraction.

    С помощью директивы #define можно вводить имена типов по­добно тому, как это делается с помощью typedef. Например, сведе­ния о книге могут быть введены таким образом:

    #define BOOK struct \

    Содержание 3

    ПРЕДИСЛОВИЕ 12

    Глава 1 16

    БАЗОВЫЕ ПОНЯТИЯ ЯЗЫКА 16

    1.1.Алфавит, идентификаторы, служебные слова 17

    1.2.Литералы 20

    1.3.Переменные и именованные 27

    константы 27

    1.4. Операции 35

    1.5. Разделители 44

    1.6.Выражения 49

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

    Глава 2 61

    ВВЕДЕНИЕ 61

    В ПРОГРАММИРОВАНИЕ НА СИ 61

    2.1.Структура и компоненты простой программы 61

    2.2.Элементарные средства 71

    программирования 71

    2.3. Операторы цикла 92

    2.4. Массивы и вложение 108

    операторов цикла 108

    2.5.Функции 123

    2.6.Переключатели 135

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

    Глава 3 140

    ПРЕПРОЦЕССОРНЫЕ СРЕДСТВА 140

    3.1.Стадии и директивы препроцессорной обработки 141

    HUB 143

    3.2.Замены в тексте 145

    3.3.Включение текстов из файлов 150

    3.4.Условная компиляция 152

    3.5.Макроподстановки средствами 157

    препроцессора 157

    3.6.Вспомогательные директивы 163

    3.7.Встроенные макроимена 165

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

    Глава 4 169

    УКАЗАТЕЛИ, МАССИВЫ, СТРОКИ 169

    4.1.Указатели на объекты 169

    4.2.Указатели и массивы 178

    4.3. Символьная информация и строки 193

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

    Глава 5 204

    ФУНКЦИИ 204

    5.1. Общие сведения о функциях 204

    5.2.Указатели в параметрах функций 209

    5.3.Массивы и строки 214

    как параметры функций 214

    5.4.Указатели на функции 223

    5.5.Функции с переменным 237

    количеством аргументов 237

    5.6.Рекурсивные функции 249

    5.7.Классы памяти 252

    и организация программ 252

    5.8. Параметры функции main( ) 259

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

    Глава 6 264

    СТРУКТУРЫ И ОБЪЕДИНЕНИЯ 264

    6.1.Структурные типы и структуры 264

    6.2.Структуры, массивы и указатели 278

    6.3.Структуры и функции 289

    6.4.Динамические информационные структуры 293

    6.5.Объединения и битовые поля 300

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

    Глава 7 312

    ВВОД И ВЫВОД 312

    7.1. Потоковый ввод-вывод 312

    7.2. Ввод-вывод нижнего уровня 349

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

    Глава 8 360

    ПОДГОТОВКА И ВЫПОЛНЕНИЕ 360

    ПРОГРАММ 360

    8.1.Схема подготовки программ 360

    8.2.Подготовка программ 362

    в операционной системе UNIX 362

    8.3. Утилита make 364

    8.4. Библиотеки объектных модулей 368

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

    Приложение 1 376

    ТАБЛИЦЫ КОДОВ ASCII 376

    HUB 381

    Приложение 2 384

    Константы предельных значений 384

    Приложение 3 386

    Стандартная библиотека функций языка Си 386

    Приложение 4 397

    МОДЕЛИ ПРЕДСТАВЛЕНИЯ 397

    ЧИСЕЛ НА РАЗЛИЧНЫХ 397

    КОМПЬЮТЕРНЫХ ПЛАТФОРМАХ 397

    Литература 400

    Предметный указатель 401

    }

    Здесь BOOK - препроцессорный идентификатор, вслед за ко­торым в нескольких строках размещена «строка замещения». Об­ратите внимание на использование символа продолжения '\' для переноса строковой константы. Кроме того, отметим отсутствие точки с запятой после закрывающей скобки '}'. Далее в программе можно определять конкретные объекты-структуры или указатели с помощью имени BOOK, введенного на препроцессорном уровне:

    BOOK ss, *pi;

    После препроцессорных замен и исключения комментариев это предложение примет вид:

    struct

    {

    char author[20];

    char title [80]; int year;

    }

    ss, *pi;

    Определение структур. Определения элементов (компонентов) структурного типа внешне подобны определениям данных соответ­ствующих типов. Однако имеется существенное отличие. При опре­делении структурного типа его компонентам не выделяется память, и их нельзя инициализировать. Другими словами, структурный тип не является объектом.

    Из нашего примера определения структурного типа с названием goods следует, что наименование товара будет связано с указателем типа char*, имеющим имя name. Оптовая цена единицы товара будет значением элемента типа long с названием price. Торговая наценка будет значением элемента типа float с именем percent и т. д. Все это следует из приведенного определения структурного типа с на­званием goods. Но прежде чем элементы, введенные в определении структурного типа, смогут получить значения, должна быть опре­делена хотя бы одна структура (то есть структурный объект) этого типа. Например, следующее определение вводит две структуры, то есть два объекта, типа goods:

    struct goods coat, tea;

    Итак, если структурный тип определен и известно его имя, то формат определения конкретных структур (объектов структурного типа) имеет вид:

    struct имя_структурного_типа список_структур;

    где список_структур - список выбранных пользователем имен (идентификаторов).

    Выше показано, что для структурного типа, имя которого введе­но с помощью служебного слова typedef, определение структур не должно содержать спецификатора типа struct. Например, указатель на структуры для представления комплексных чисел с помощью определенного выше обозначения структурного типа complex мож­но определить так:

    complex *p_comp;

    Выше был определен структурный тип «рациональная дробь», для которого введены два имени. С их помощью так вводятся конк­ретные структуры:

    /* Две структуры: */

    struct rational_fraction ratio_1, ratio_2;

    /* Две структуры: */

    fraction zin, rat_X_Y;

    Кроме изложенной последовательности определения структур (определяется структурный тип, затем с использованием его имени определяются структуры), в языке Си имеются еще две схемы их определения. Во-первых, структуры могут быть определены одно­временно с определением структурного типа:

    struct имя_структурного_типа

    { определения_элементов }

    список_структур;

    Пример одновременного определения структурного типа и струк­тур (объектов):

    struct student

    {

    char name [15]; /* Имя */

    char surname [20]; /* Фамилия */ int year; /* Курс */

    } student_1, student_2, student_3;

    Здесь определен структурный тип с именем student и три кон­кретные структуры student_1, student_2, student_3, которые явля­ются полноправными объектами. В каждую из этих трех структур входят элементы, позволяющие представить имя (name), фамилию (surname), курс (year), на котором обучается студент.

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

    struct student leader, freshman;

    Следующий вариант определения структур является некоторым упрощением приведенного варианта. Дело в том, что можно опре­делять структуры, приведя «внутреннее строение» структурного ти­па, но не вводя его названия. Такой безымянный структурный тип обычно используется в программе для однократного определения структур:

    struct

    { определения_элементов }

    список_структур;

    В качестве примера определим структуры, описывающие конфи­гурацию персонального компьютера с такими характеристиками:

    • тип процессора (char [10]);

    • рабочая частота в ГГц (int);

    • объем основной памяти в Гбайтах (int);

    • емкость жесткого диска в Гбайтах (int).

    Пример определения структур безымянного типа:

    struct {

    char processor [10]; int frequency; int memory;

    int disk;

    } ACER, HP, Compaq;

    В данном случае введены три структуры (три объекта) с именами ACER, HP, Compaq. В каждую из определенных структур входят элементы, в которые можно будет занести сведения о характеристи­ках конкретных ПК. Структурный тип «компьютер» не именован. Поэтому если в программе потребуется определять другие структу­ры с таким же составом элементов, то придется полностью повто­рить приведенное выше определение структурного типа.

    Выделение памяти для структур. Мы уже договорились, что опре­деление структурного типа не связано с выделением памяти, а при каждом определении структуры (объекта) ей выделяется память в таком количестве, чтобы могли разместиться данные всех элемен­тов. На рис. 6.1 приведены условные схемы распределения памяти для одного из объектов (структур) типа goods. На первой схеме эле­менты структуры размещены подряд без пропусков между ними. Од­нако никаких гарантий о таком непрерывном размещении элементов структур стандарт языка Си не дает. Причиной появления неисполь­зованных участков памяти («дыр») могут явиться требования вырав­нивания данных по границам участков адресного пространства. Эти требования зависят от реализации, от аппаратных возможностей сис­темы и иногда от режимов (опций) работы компилятора. На рис. 6.1 условно изображен и второй вариант с пропуском участка памяти между элементами float percent и int vol. Пропуск («дыра») может быть и после последнего элемента структуры. В этом случае память для следующего объекта, определенного в программе, будет выделена не сразу после структуры, а с промежутком, оставляемым для вы­равнивания по принятой границе участка адресного пространства.

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

    Определение структурного типа:
    1   ...   15   16   17   18   19   20   21   22   ...   42


    написать администратору сайта