русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Структуры и объединения


Дата добавления: 2015-06-12; просмотров: 513; Нарушение авторских прав


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

struct s { ...

это просто краткая форма описания

class s { public: ...

Поименованное объединение определяется как структура, все члены которой имеют один и тот же адрес ($$R.9.5). Если известно, что в каждый момент времени используется значение только одного члена структуры, то объявив ее объединением, можно сэкономить память. Например, можно использовать объединение для хранения лексем транслятора С:

union tok_val {

char* p; // строка

char v[8]; // идентификатор (не более 8 символов)

long i; // значения целых

double d; // значения чисел с плавающей точкой

};

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

void strange(int i)

{

tok_val x;

if (i)

x.p = "2";

else

x.d = 2;

sqrt(x.d); // ошибка, если i != 0

}

Кроме того, определенное таким образом объединение нельзя инициализировать таким кажущимся вполне естественным способом:

tok_val val1 = 12; // ошибка: int присваивается tok_val

tok_val val2 = "12"; // ошибка: char* присваивается tok_val

Для правильной инициализации надо использовать конструкторы:

union tok_val {

char* p; // строка

char v[8]; // идентификатор (не более 8 символов)

long i; // значения целых

double d; // значения чисел с плавающей точкой

tok_val(const char*); // нужно выбирать между p и v

tok_val(int ii) { i = ii; }

tok_val(double dd) { d = dd; }

};

Эти описания позволяют разрешить с помощью типа членов неоднозначность при перегрузке имени функции (см. $$4.6.6 и $$7.3). Например:

void f()

{

tok_val a = 10; // a.i = 10

tok_val b = 10.0; // b.d = 10.0

}

Если это невозможно (например, для типов char* и char[8] или int и char и т.д.), то определить, какой член инициализируется, можно, изучив инициализатор при выполнении программы, или введя дополнительный параметр. Например:



tok_val::tok_val(const char* pp)

{

if (strlen(pp) <= 8)

strncpy(v,pp,8); // короткая строка

else

p = pp; // длинная строка

}

Но лучше подобной неоднозначности избегать.

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

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

class tok_val {

public:

enum Tag { I, D, S, N };

private:

union {

const char* p;

char v[8];

long i;

double d;

};

Tag tag;

 

void check(Tag t) { if (tag != t) error(); }

public:

Tag get_tag() { return tag; }

tok_val(const char* pp);

tok_val(long ii) { i = ii; tag = I; }

tok_val(double dd) { d = dd; tag = D; }

long& ival() { check(I); return i; }

double& fval() { check(D); return d; }

const char*& sval() { check(S); return p; }

char* id() { check(N); return v; }

};

 

tok_val::tok_val(const char* pp)

{

if (strlen(pp) <= 8) { // короткая строка

tag = N;

strncpy(v,pp,8);

}

else { // длинная строка

tag = S;

p = pp; // записывается только указатель

}

}

Использовать класс tok_val можно так:

void f()

{

tok_val t1("короткая"); // присваивается v

tok_val t2("длинная строка"); // присваивается p

char s[8];

strncpy(s,t1.id(),8); // нормально

strncpy(s,t2.id(),8); // check() выдаст ошибку

}

Описав тип Tag и функцию get_tag() в общей части, мы гарантируем, что тип tok_val можно использовать как тип параметра. Таким образом, появляется надежная в смысле типов альтернатива описанию параметров с эллипсисом. Вот, например, описание функции обработки ошибок, которая может иметь один, два, или три параметра с типами char*, int или double:

extern tok_val no_arg;

void error(

const char* format,

tok_val a1 = no_arg,

tok_val a2 = no_arg,

tok_val a3 = no_arg);



<== предыдущая лекция | следующая лекция ==>
Статические члены | Локальные переменные


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 1.733 сек.