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

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

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

Приведем пример распределения памяти под следующие структуры:

struct structA {

char cA;

char sA[4];

float fA;}aSl;

Под переменную aSl типа структуры structA выделяется 9 байт:

Элемент

char cA

char SА[4]

float fA

Байты

1

2

3

5

5

6

7

9

struct structB {

int iA:2;

int iB:3;

int :6;

unsigned int iD:4;} aS2;

Под переменную aS2 типа структуры structB выделяется 2 байта:

Элемент

 

iD

Не используется

iB

iA

Байты

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

Для знаковых полей (например, int) старший левый бит из общего числа битов, выделяемых под данное битовое поле, интерпретируется как знак. Например, битовое значение 11 для поля iA будет восприниматься как -1, а значение 11 для поля iD - как 3.

Доступ к элементам структуры

Для обращения к отдельным элементам структуры используются операторы . и ->.

Доступ к элементам структуры может иметь следующее формальной описание:

struct имя_структуры {

тип_элемента элемент_структуры1;

тип_элемента элемент_структуры2;

} перем_структуры;

// Доступ к элементу структуры

перем_структуры.элемент_структуры 1;

// Доступ через указатель на структуру

// Объявление указателя на структуру

имя_структуры *указатель_структуры=& перем_структуры;

указатель_структуры- >элемент_структуры 1;

Элементы структуры могут иметь модификаторы доступа public (по умолчанию), private и protected.

Пример:

struct structA {

char cA;

charsA[4];

float fA;} aSl,*prtaSl=&aSl; // prtaSl - указатель на структуру aSl

struct structB {

struct structA aS2;

} bSl,*prtbSl=&bSl;

aSl.cA= 'F' // Доступ к элементу сА

prtaSl->cA= 'F' // Доступ к элементу сА через указатель

(*prtaSl).cA= 'F' // Доступ к элементу сА

(prtbSl->aS2).cA='F' // Доступ к элементу вложенной структуры

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

Пример:

struct structA { struct structA *psA; char cA; } sA;

Использование структур с функциями

Структуры или отдельные элементы структуры можно передавать в функции как по ссылке или указателю, так и по значению. В первом случае передается только указатель на структуру, а во втором - в стек копируется все содержание структуры. При значительных размерах структур это может привести к переполнению стека.

Пример:

// Передача структуры в функции

struct structA {int A; char В;} sA, *psA=&sA;

void Function 1 (struct structA sA); // Передача по значению

void Function2(struct structA *psA); // Передача по указателю

// Доступ к элементу структуры, переданной по указателю,

// записывается следующим образом: psA->A

void Function3(struct structA &sA); // Передача по ссылке

Функции могут возвращать структуру или указатель на структуру.

Пример:

struct structA {int A; char В;};

struct structA Function3(void); // Функция возвращает структуру

struct structA *Function4(void); // Функция возвращает указатель на структуру

Массивы структур

Массив типа структуры создается, как и любой массив другого типа, Указанием справа от имени переменной числа элементов массива в квадратных скобках. Доступ к элементу массива в этом случае имеет следующий синтаксис:

имя_массива[индекс_элемента_массива] .элемент_структуры.

При использовании указателей на массив структур следует:

  1. Присвоить указателю адрес первого элемента массива;

  2. Доступ к элементам массива выполнять, изменяя этот указатель адреса.

Пример:

struct structA { int A; char В;} sA[10], *psA;

*psA=&sA[0];

// доступ к первому элементу массива структур

scanf( "%d", &psA->A); // функция scanf требует адрес переменной

gets (psA->sA); // вывод строки на консоль

// переход ко второму элементу массива

PSA++;

// доступ к следующему элементу массива (второму)

// переход к третьему элементу массива

psA++; // можно также записать: *psA=&sA[2];

Объединения

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

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

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

Пример:

union UnionPrint // Объявление объединения, состоящего из 3 переменных

{ char chVar;

int iVar;

char *sVar;

};

// Объявление переменной PrintType перечислимого типа

enum PrintType { CHAR_T, INT_T, STRING_T };

void Print(UnionPrint Var, PrintType Type )

{ switch( Type )

{

case CHAR.T:

printf( "%c", Var.chVar);

break;

case INT_T:

printf( "%d", Var.iVar);

break;

case STRING_T:

printf( "%s", Var.sVar);

break;

}}

Выделение памяти под объединение

При создании переменной типа объединения память под все элементыобъединения выделяется исходя из размера наибольшего элемента.

Приведем пример распределения памяти под следующее объединение:

union unionA {

char cA;

char sA[6];

float fA;}aUl;

Под переменную aUl типа объединения unionA выделяется 6 байт:

Элемент

char сА

char sA[4]

float fA

Байт

1

1

2

3

4

5

6

1

2

3

4

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

Инициализировать объединение при его объявлении можно только заданием значения первого элемента объединения.

Пример:

union unionA { char cA; char sA[6]; float fA;} aUl={'F '};

Доступ к элементам объединения

Доступ к элементам объединения выполняется с помощью операторов . и &, аналогично доступу к элементам структур.

Элементы объединения не могут иметь модификаторов доступа и всегда воспринимаются как общедоступные (public).

Преобразование типов

Язык C++ позволяет выполнять преобразование значения одного типа в значение другого типа. Преобразование типа бывает явным и неявным.

Явное преобразование называется приведением типов. При приведении типов перед преобразовываемой переменной в скобках указывается имя типа, к которому она приводится.

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

Пример:

int iVar; float fVar;

fVar=10*(float)iVar; // Перед умножением выполняется приведение типа

// int переменной iVar к типу float

// При вычислении выражения переменная iVar будет приведена к типу float,

// но ее тип останется int

Преобразование типов между базовыми типами и преобразование типа Для указателей, ссылок и указателей на члены производного типа называются стандартным преобразованием типов. В языке C++ автоматически выполняется стандартное преобразование типов в том случае, если выражение содержит переменные различных типов.

Основные правила для автоматического преобразования типов состоят в следующем:

  • типы всех переменных, используемых в выражении справа от операторе присваивания сначала преобразовываются к наиболее "широкому" типу а затем выполняется вычисление выражения;

  • тип результата будет преобразован к типу значения, расположенное слева от оператора присваивания.

Пример:

long lnuml, Inum2;

int inum;

// Значение переменной inum преобразовывается к типу long,

// а затем выполняется присваивание

lnuml = inum;

// или умножение.

lnum2 = inum * lnum2;

При преобразовании целых типов char, int, long и типов, образуемых из них, таких, как short, signed и unsigned, выполняются следующие правила:

  • при преобразовании целого типа signed к типу unsigned не происходив изменения значения битов и поэтому для отрицательных чисел значение переменной типа unsigned будет отличаться от значения переменной типа signed;

Пример:

short i = -3; unsigned short us;

us = i; // Переменная us будет равна 65533

  • при преобразовании целого типа unsigned к типу signed не происходит изменения значения битов и поэтому значения переменной типа unsigned могут отличаться от значения переменной типа signed;

Пример:

short i; unsigned short us = 65533;

i = us; // Переменная i будет равна -З

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

При преобразовании типов с плавающей точкой выполняются следующие правила:

  • допускается автоматическое преобразование типа float к double и double к long double, значение переменной при этом не изменяется;

  • преобразование к более "короткому" типу с плавающей точкой (например, double к float) происходит корректно только в том случае, если нет потери значения, в противном случае результат будет неопределен;

  • преобразование типа с плавающей точкой к целому типу приводит к усечению дробной части (например, -3.5 преобразуется к 3).

В следующей таблице приведены правила преобразования типов для арифметических выражений.

Условие

Преобразование

Один из операндов имеет тип long double

Все другие операнды преобразовываются к типу long double

Нет операндов типа long double и один из операндов имеет тип double

Все другие операнды преобразовываются к типу double

Нет операндов типа long double или double и один из операндов имеет тип float

Все другие операнды преобразовываются к типу float

Нет операндов типа с плавающей точкой

Преобразование операндов целого типа выполняется к типу имеющегося в выражении наиболее "широкого" операнда

Один операнд имеет тип unsigned long

Операнды преобразовываются к типу unsigned long.

Нет операндов типа unsigned long и один Операнд имеет тип long, а другой - тип unsigned int

Операнды преобразовываются к типу unsigned long

Нет операндов типа unsigned long и один операнд имеет тип long

Операнды преобразовываются к типу long

Нет операндов типа unsigned long или long, и один операнд имеет тип unsigned int

Операнды преобразовываются к типу unsigned int

Один операнд имеет тип int

Операнды преобразовываются к типу int

Пример:

float fVar;

double dVar;

int iVar;

unsigned long ulVar;

dVar = iVar * ulVar; // iVar преобразовывается к типу unsigned long;

// а результат преобразовывается к типу double.

dVar = ulVar + fVar; // ulVar преобразовывается к типу float;

// а результат преобразовывается к типу double.

Преобразование типа указателей

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

Целое константное выражение, равное нулю, или выражение, приводимое к типу void *, преобразовывается к указателю, называемому null-указателем. Такой указатель может использоваться для сравнения и определения существования объекта.

Указатель типа void может быть преобразован к указателю любого другого типа только явным приведением типа. Обратно, указатель любого типа может быть неявно преобразован к указателю типа void.

Это может быть использовано для передачи параметров функции, для которой формальный параметр может быть указателем различных типов, например int * или float *. В этом случае прототип функции будет выглядеть следующим образом: Functl(void *pVar); а вызов функции будет выполняться как int* piVar; float * pfVar;... Functl(piVar); или Functl(pfVar);.

В теле функции для работы с указателем надо будет использовать явное преобразование типа, например (int *)pVar.

Указатель на любой объект, не являющийся const или volatile может быть неявно преобразован к указателю типа void *.

Указатель на функцию может быть преобразован к типу void *, если тип void * является достаточным для размещения этого указателя.

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

  • специфицируемый базовый класс является доступным и преобразование является недвусмысленным;

  • используется явное преобразование типа указателя (результатом этого является указатель на "подобъект" - часть объекта, описанная в базовом классе).

Соседние файлы в папке VC++Баженова