Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 209
Текст из файла (страница 209)
В ядре операционной системы или в отладчике тип РР)ч) может использоваться следующим образом: во!зг рот оу РМ вузгет (РРЖ*р) ( У... г!'(р->г(!гзу) ( гг копирование на диск р->а!ггу = дз ) гУ ... ) У содержимое изменилосл Как это ни странно, применение битовых полей для упаковки нескольких переменных в одном байте не всегда экономит память. Это лишь экономит память под данные, но объем кода, манипулирующего такими данными, увеличивается на большинстве машин. Известно, что программы сжимаются существенно, когда битовые поля конвертируются в символы.
Еще известно, что обращение к битовым полям выполняется медленнее, чем обращение к переменным типа !а!или сйае. Битовые поля — это просто удобное средство для сокрытия применения логических побитовых операций (96.2.4) для извлечения информации из части машинного слова и для упаковки в часть машинного слова. за исг РРП ( ипз!апез! !пг РР)Ч: 11) зпг: 3; ипз!япег! тг ССА: 3; Ьоо! попгеаслаЫе: 1з Ьоо! д!гзу: 1з Ьоо! ва!Ы: 1; Ьоо! я!оЬа1: 1; )' гг' физический номер страницы памяти для )10000 С.8. Экономия памяти 977 С.8.2. Объединения Объединение (ип(оп) — зто структура, в которой все члены расположены по одному и тому же адресу, так что в памяти машины объединение занимает ровно столько места, сколько требуется для его наибольшего поля. Естественно, в каждый конкретный момент времени объединение может хранить какое-то одно значение.
Например, рассмотрим отдельную запись символьной таблицы, содержащую имя и значение: епит Туре (5,1)г зи ис1 Ептгу ( сйаг* пате; Туре 1; сйаг* з; Уиспользуем з если 1==5 (пт 1; ~У используем 1 если 1==1 ): гойЦ(Епггу* р) ( (Т(р->1 == Я) соиг «р->з; У... Поскольку членами е и (нельзя пользоваться одновременно, то выделяемое под них место в памяти тратится впустую.
Этого можно избежать, сделав зти переменные членами обьединения (ипюп): ип)оп Иа1ие ( сйаг* з; т11; )' В языке С++ нет встроенных средств слежения за тем, какого типа данные харанятся в объединении, так что зта задача ложится на плечи программиста: тгисг Епоу ( сйаг* пате; Туре 1; )га(ие ю У используем кз если 1==51 используем в1 если 1==1 )' гоЫ Т(Ептгу' р) ( (1(р->1 == 5) соил « р->г.з) гу ... ) К сожалению, переход на объединение заставляет нас также изменить код, использующий з, на у.з.
Этого можно избежать применением анонимного объединения (опопутоиз ип(оп), которое не имеет имени и, значит, не является типом. Оно просто гарантирует, что его члены расположены по одному и тому же адресу памяти: Приложение С. Технические подробности 978 мгисг Еглгу ( сйаг* лапает Туре 1; ил(ол ( с)ьаг* зз // используем з если 1==5 1ш 1; /У используем 1 если (==1 )' иоЫу'(Еллу* р) ( (Т(р->1 == 5) соиг «р->з; // ...
) В результате, код, использующий Ептгу, не претерпевает изменений. К сожалению, невозможно гарантировать, что обьединение всегда читается корректно, то есть в соответствии с типом записанного в него значения (что порождает тонкие ошибки). Поэтому целесообразно инкапсулировать объединение, что может дать гарантию соответствия между читаемым значением и его типом (5!0.6(20)).
Объединения иногда используются неразумно с целью преобразования типов. Обычно это свойственно программистам с опытом работы на языках, не имеющих операций явного преобразования типов и где приходится из-за этого пускаться на всякие хитрости. Следующий пример показывает преобразование из 1лг в 1лг*, основанное на их битовой эквивалентности: ип(оп РиИде ( 1пг 1г 1пг* р; )' (лг* сйеаг (1пз 1) ( РиИае а; а.1=1; гегигл а.р; У плохо ) Это, на самом деле, вовсе и не преобразование: на некоторых машинах злг и 1лг" занимают разный объем памяти, а на других!лене может иметь нечетный адрес. Такое применение объединений опасно и непереносимо, а для преобразования типов существуют явные и переносимые способы (96.2.7). Иногда объединения преднамеренно используются для того, чтобы избежать преобразования типов.
Например, Гиь(яе можно применить с тем, чтобы узнать числовое представление нулевого указателя: 1лг гаа)п ( ) ( ГиФде Тоог С9. Управление памятью 979 1оо.р = Ог сои( « Чпе гпгекег га1ие оу' тпе ротгег О й " « /оо. 1 « ' '~п '; ) С.В.З. Объединения и классы Встречаются нетривиальные объединения, у которых имеются поля с размером, много большим размеров других, часто используемых полей. Поскольку размер объединения совпадает с наибольшим размером его полей, то память расходуется напрасно.
Этого можно избежать, применив вместо объединений производные классы. Объединения не могут иметь поля данных, имеющих тип класса с конструктором, деструктором и операцией присваивания, ибо компилятор не знает, какого типа член объединения ему нужно уничтожать. С.9. Управление памятью В С++ имеются три фундаментальных способа использования памяти: Статическая память, в которую компоновщик помещает объект на все время выполнения программы.
В статической памяти располагаются глобальные переменные и переменные из пространств имен, статические члены классов (510.2.4) и статические локальные переменные функций Я7.1.2). Объект, размещаемый в статической памяти, конструируется один раз и сохраняется до окончания работы программы. У него все время один и тот же адрес. Статические объекты могут вызывать проблемы в многопоточных программах (с разделяемым адресным пространством), поскольку они используются совместно и во избежание проблем требуется их блокировка.
Автоматическая память, в которой располагаются аргументы функций и локальные переменные. При каждом входе в функцию (блок) создается их копия. Такая память создается и уничтожается автоматически — отсюда и ее название. Ее также называют стоковой памятью (тетогу оп гпе ласк). Если вы хотите явно подчеркнуть характер такой памяти, С++ предоставляет для этого необязательное ключевое слово аиго.
Свободная память, которую программы запрашивают явным образом для помещения в нее динамически создаваемых объектов, и которую они могут явным образом освободить по исчерпании нужды в этих объектах (операции пеи и ае1еге). Когда программе нужна дополнительная память, она запрашивает ее у операционной системы операцией печч. Свободную память называют также динамической памятью (Йупатк тетогу) или кучей (Леар). Что касается программиста, то автоматическая и статическая память используются просто, очевидно и неявно. Интерес представляет лишь использование свободной памяти. Хотя выделение памяти (операцией пои>) и просто, при отсутствии единой и согласованной стратегии освобождения памяти и передачи ее назад менеджеру свободной памяти, вся память может быть исчерпана, что особо характерно для долго работающих программ. Простейшая стратегия заключается в том, чтобы передать объекты в свободной памяти под управление автоматических объектов.
Как следствие, многие контейнеры 9ВО Приложение С. Технические подробности именно так и реализуются, то есть управляют элементами, расположенными в свободной памяти (925.7). Например, автоматический объект типа Я)зля (911.12) управляет последовательностью символов, расположенных в свободной памяти, и автоматически освобождает память, когда он сам выходит из области видимости. Все стандартные контейнеры (516.3, глава 17, глава 20, 922.4) могут так реализовываться.
С.9.1. Автоматическая сборка мусора Когда описанного выше регулярного подхода недостаточно, программист мог бы воспользоваться менеджером памяти, который ищет висячие объекты (на которые никто не ссылается) и уничтожает их (освобождает занятую ими память). Это называют автоматической сборкой мусора или просто сборкой мусора (яаг6а~е сойесбоп). Соответствующий менеджер, естественно, называется сборщиком мусора (яагдаяе сойесгог). Фундаментальная идея, лежащая в основе сборки мусора, заключается в том, что если в программе нет ссылок на некоторый объект, то этот объект для нее потерян и его самое время уничтожить сборщиком мусора. Например: гоЫ Г() ( !пг* р = пет (пг; р=О! айаг* О = пегг сйаг; ) Здесь присваиванием р=О мы обрываем связь программы и объекта типа зпг, так что память, выделенную под этот объект можно безболезненно освободить и предоставить для новых объектов.
Поэтому последующую переменную типа с7заг можно расположить в той же области памяти, и указатель О будет иметь то же самое значение, что было у переменной р. Стандарт не требует обязательной реализации автоматической сборки мусора, но она явочным порядком внедряется там, где ручная сборка мусора обходится слишком дорого. При сравнении альтернатив принимайте во внимание производительность программы, расход памяти, надежность, переносимость, материальные затраты на программирование, стоимость сборщика мусора и предсказуемость поведения программы.
С.9.1.1. Замаскированные указатели Что означает, что на обьект нет ссылок? Рассмотрим пример: го(О.Г() !пг* р = пет шг; (опО !1 = гешгегрге! саве<!опя> (р) ь ОкГГГГОООО (оп№ !2 = ге(пгегрге! сам<(опя> (р) ь ОяООООГГГГ р=о; У точка №1: в этой точке нет указателя на!п! р = гешгегрге! салг<шг*> (а) (2); ,(г теперь (п( снова адресуется указателем С.9. Управление памятью 981 Указатели, хранящиеся в программе в переменных иных типов, называются «замаскированными указателями», В частности, указатель, первоначально хранившийся в переменной р, был замаскирован в целых переменных 11 и 12.