Пространство имен структур

Имена тегов структур разделяют общее пространство имен с тегами объединений и перечислимых данных (однако в С++ имена входящих в структуру перечислимых данных находятся в другом адресном пространстве). Это означает, что в пределах одного контекста такие теги должны иметь уникальные имена. Однако, имена тегов не обязаны отличаться от идентификаторов, находящихся в трех других адресных пространствах: пространстве имен меток, пространстве (пространствах) имен компонентов и едином адресном пространстве (которое состоит из имен переменных, функций, имен typedef и нумераторов).

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

goto s;...struct s { // так можно; теги и имена меток находятся в разных // адресных пространствах int s; // так можно; теги, имена меток и имена компонентов // дятся в разных адресных пространствах float s;// так нельзя: повторение имени компонентов структур} s; // так можно; пространства имен переменных различны // В С++ это допустимо только если s не имеет // конструктора. union s { // так нельзя: повторение имен в пространстве тегов int s; // так можно: новое пространство компонентов float f;} f; // так можно: пространство имен переменных struct t { int s; // так можно: следующее пространство имен компоненто ...} s; // так нельзя: повторение имен переменных

Битовые поля

Кажется расточительным использовать для признака, принимающего только

два значения ( например: да, нет) тип char, но объект типа char является в С++

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

есть возможность собрать переменные с малым диапазоном значений воедино,

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

выделяется память в словах типа char, int, long (signed или unsigned). Член

структуры является полем, если в его определении после имени указано число

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

используются для выравнивания полей на границу целого, так как не допускается

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

signed поддерживаются только синтаксисчески. Это следует учитывать при

создании переносимых программ!

В Турбо Си битовые поля располагаются в номерах разрядов от меньшего к

большему. В других компиляторах Си может быть принят другой порядок. Это тоже

следует учитывать при написании переносимых программ!

Ниже приведен пример программы, распечатывающей характеристики ПЭВМ, с

использованием полей бит:

#include <bios.h>

void main(void)

{

struct equipment {

unsigned flop_boot : 1 ; // подключен ли винчестер

unsigned s8087 : 1 ; // сопроцессор

unsigned mram : 2 ; // размер блока RAM

unsigned video_mode : 2 ; // режим видеомонитора

unsigned numflop : 2 ; // количество НГМД

// 00 - 1, 01 - 2, 10 - 3, 11 - 4

unsigned dma : 1 ; // DMA

unsigned ports : 3 ; // количество портов с интерфейсом RS232

unsigned game_adap : 1 ; // джойстик

unsigned : 1 ;

unsigned numprint : 2 ; // количество печатающих устройств

} eq;

unsigned int * i;

i = (unsigned int *) &eq;

*i = biosequip();

printf("Количество дисководов - %d\n",eq.numflop+1);

printf("Количество портов - %d\n",eq.ports);

if (eq.s8087)

printf("Сопроцессор есть\n");

else

printf("Сопроцессора нет\n");

}

В этом примере элементами структурной переменной eq являются поля бит,

описывающие список установленного оборудования в BIOS. Этот список получается

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

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

целое значение записывается по адресу структуры и далее интерпретируется, как

состоящее из полей бит.

Нельзя брать адрес поля бит.

Применяя поля для упаковки нескольких переменных в целочисленное

слово, мы необязательно сэкономим память. Экономится память для данных, но на

большинстве машин одновременно возрастает объем команд, нужных для работы с

упакованными данными. Известны даже такие программы, которые значительно

сокращались в объеме, если двоичные переменные, задаваемые полями,

преобразовывались в переменные типа char! Кроме того, доступ к char или

обычно происходит намного быстрее, чем доступ к полю. Поля - это просто

удобная краткая форма задания логических операций для извлечения или занесения

информации в части слова.

Структура может содержать любые комбинации битовых полей с данными других типов.

Целочисленные компоненты типа signed или unsigned можно объявить битовыми полями шириной от 1 до 16 бит. Ширина битового поля и его опциональный идентификатор задаются следующим образом:

спецификатор-типа <идентификатор-битового поля>:ширина;

где спецификатор-типа это char, unsigned char, int или unsigned int. Битовые поля располагаются с нижнего и кончая саршим битом слова. Выражение "ширина" должно быть задано и должно давать целочисленную константу со значением в диапазоне от 0 до 16.

Если идентификатор битового поля опущен, то число битов, заданное выражением "ширина", распределяется в памяти, но поле при этом остается недоступным программе. Это позволяет создавать битовые шаблоны для, например, аппаратных регистров компьютера, в которых некоторые биты не используются. Пример:

struct mystruct { int i:2; unsigned j:5; int :4; int k:1; unsigned m:4;} a, b, c;

создает следующее распределение памяти:
Пространство имен структур - student2.ru

Целочисленные поля хранятся в виде дополнения до двух, причем крайний левый бит помещается в MSB (наиболее значащий бит). Для битового поля типа Int (например, signed) MSB интерпретируется как знаковый бит. Битовое поле шириной 2, содержащее двоичное 11, будет, следовательно, в случае типа unsigned интерпретироваться как 3, а в случае Int как -1. В предыдущем примере допустимое выражение a.i = 6 поместит в a.i двоичное 10 = -2, не выдавая каких-либо предупреждений. Поле k типа signed int шириной 1 может содержать только значения -1 и 0, так как битовый шаблон 1 будет интерпретирован как -1.

Примечание

Битовые поля могут быть объявлены только в структурах, объединениях и классах. Доступ к ним выполняется теми же селекторами компонентов (. и ->), что используются для доступа к компонентам других типов. Кроме того, битовые поля вызывают некоторые проблемы с созданием переносимых кодов, поскольку организация битов в байтах и байтов в словах зависит от конкретной машины.

Выражение &mystruct.x недопустимо, так как x это идентификатор битового поля, а никакой гарантии, что mystruct.x имеет адрес на границе байта, нет.

Стек

Следующий пример демонстрирует использование стека в программе, реализующей постфиксный калькулятор для целочисленных выражений. Для начала необходимо модифицировать функции push() и pop(), как показано ниже. Следует знать, что стек будет размешаться в динамически распределяемой памяти, а не в массиве фиксированного размера. Хотя применение динамического распределения памяти и не требуется в таком простом примере, мы увидим, как использовать динамическую память для хранения данных стека.

int *p; /* указатель на область свободной памяти */int *tos; /* указатель на вершину стека */int *bos; /* указатель на дно стека */ /* Занесение элемента в стек. */void push(int i){ if(p > bos) { printf("Стек полон\n"); return; } *p = i; p++;} /* Получение верхнего элемента из стека. */int pop(void){ p--; if(p < tos) { printf("Стек пуст\n"); return 0; } return *p;}

Перед использованием этих функций необходимо выделить память из области свободной памяти с помощью функции malloc() и присвоить переменой tos адрес начала этой области, а переменной bos — адрес ее конца.

Текст программы постфиксного калькулятора целиком приведен ниже.

/* Простой калькулятор с четырмя действиями. */ #include <stdio.h>#include <stdlib.h> #define MAX 100 int *p; /* указатель на область свободной памяти */int *tos; /* указатель на вершину стека */int *bos; /* указатель на дно стека */ void push(int i);int pop(void); int main(void){ int a, b; char s[80]; p = (int *) malloc(MAX*sizeof(int)); /* получить память для стека */ if(!p) { printf("Ошибка при выделении памяти\n"); exit(1); } tos = p; bos = p + MAX-1; printf("Калькулятор с четырьмя действиями\n"); printf("Нажмите 'q' для выхода\n"); do { printf(": "); gets(s); switch(*s) { case '+': a = pop(); b = pop(); printf("%d\n", a+b); push(a+b); break; case '-': a = pop(); b = pop(); printf("%d\n", b-a); push(b-a); break; case '*': a = pop(); b = pop(); printf("%d\n", b*a); push(b*a); break; case '/': a = pop(); b = pop(); if(a==0) { printf("Деление на 0.\n"); break; } printf("%d\n", b/a); push(b/a); break; case '.': /* показать содержимое вершины стека */ a = pop(); push(a); printf("Текущее значение на вершине стека: %d\n", a); break; default: push(atoi(s)); } } while(*s != 'q'); return 0;} /* Занесение элемента в стек. */void push(int i){ if(p > bos) { printf("Стек полон\n"); return; } *p = i; p++;} /* Получение верхнего элемента из стека. */int pop(void){ p--; if(p < tos) { printf("Стек пуст\n"); return 0; } return *p;}

Наши рекомендации