Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Программирование в Visual С++ 2005 8 страница



 

Таблица 2.7. Таблица истинности для операции битового исключающего ИЛИ

 

Применяя те же значения переменных, что вы использовали с обычной операци-
ей ИЛИ, можете посмотреть на результат такого оператора:


Эта операция может быть представлена следующим образом:

 

Объединение их исключающим ИЛИ даст:

 

Переменная result получает значение 0x1В, или 27 в десятичной нотации.

Операция л имеет одно довольно неожиданное свойство. Предположим, что у вас
есть две переменных типа char — first со значением 'А' и last со значением 'Z', что
в двоичном виде представляется как 0100 0001 и0101 1010. Если вы запишете такие
операторы:

 


то в результате first и last обменяются значениями без использования какой-либо
памяти для промежуточного результата. Это работает с любыми целыми числами.

Битовое НЕ

Битовое НЕ (-) принимает единственный операнд, в котором инвертирует все
биты: 1 становится 0, а 0 становится 1. Таким образом, если выполнить оператор

то если letter 1 равно 0100 0001, переменная result получит значение 1011 1110, то
есть ОхВЕ, или 190 в десятичной нотации.

Битовые операции сдвига

Эти операции сдвигают значение целочисленных переменных на указанное коли-
чество битов вправо или влево. Операция» предназначена для сдвига вправо, а опе-
рация «— для сдвига влево. Биты, которые при этом "выпадают" за край значения,
теряются. На рис. 2.11 показан эффект от сдвига значения 2-байтной переменной
влево и вправо.

Вы объявляете и инициализируете переменную по имени number с помощью сле-
дующего оператора:

unsigned int number = 16387U;


Данные, переменные и вычисления 111

Рис. 2.11. Сдвиг 2-байтной переменной влево и вправо

Как было показано ранее в этой главе, беззнаковые целочисленные литералы пи-
шутся с добавлением к числу буквы U или и. Вы можете сдвинуть содержимое этой
переменной влево следующим образом:

Левый операнд операции сдвига — это значение, которое нужно сдвинуть, а
количество бит, на которое необходимо сдвинуть, задается правым операндом.
Иллюстрация на рис. 2.11 демонстрирует эффект от операции сдвига. Как видите,
сдвиг значения 16 387 на две позиции влево дает в результате 12. Такое значительное
изменение значения объясняется потерей крайних бит, который уходят "за край" зна-
чения.

Вы также можете сдвинуть значение вправо. Вернем переменной number началь-
ное значение 16 387. Затем вы можете написать так:



Это сдвигает значение 16 387 на две позиции вправо, что дает в результате значе-
ние 4096. Сдвиг вправо на две позиции равнозначен делению на 4 (без остатка). Это
также показано на рис. 2.11.

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

Другими словами, это эквивалентно умножению на 2". Но будьте осторожны: как
вы видели на примере сдвига влево переменной number, если значащие биты теряют-
ся, то результат будет совсем не таким, какой вы ожидаете. Однако это не отличается
от операции умножения. Если вы умножите 2-байтное число на 4, то получите тот же
результат, поэтому сдвиг влево и умножение все-таки эквивалентны. Проблема точно-
сти возникает, когда результат умножения выходит за пределы допустимых значений
2-байтного целого.

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


112 Глава 2

 

Если же нет, то компилятор выдаст сообщение, но все же вы должны быть осторож-
ны. Например, если вы хотите вывести результат сдвига переменной number на 2
бита влево, то должны написать следующий оператор:

 


Здесь скобки очень важны. Без них операция сдвига интерпретировалась бы ком-
пилятором как операция потока, поэтому вы не получили бы должного результата; на
вывод было бы отправлено значение number, а за ним 2.

Операция сдвига вправо в основном подобна операции сдвига влево. Например,
предположим, что переменная number имеет значение 24, и выполняется следующий
оператор:

В результате этого number получит значение 6, разделив исходное значение на 4.
Однако сдвиг вправо работает специальным образом со знаковыми целочисленны-
ми типами, которые содержат отрицательные значения (то есть, у которых бит зна-
ка — крайний левый — равен 1). В этом случае бит знака распространяется вправо.
Например, можно объявить и инициализировать переменную number типа char с
десятичным значением -104:

Теперь вы можете сдвинуть его вправо на 2 бита с помощью следующего оператора:

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

 

Время хранения и область видимости

Все переменные имеют ограниченное время жизни при выполнении программы.
Они появляются в точке, где вы их объявили, а затем в некоторой точке они исчеза-
ют — не позднее момента завершения программы. Насколько долго существует кон-
кретная переменная, определяется свойством, называемым временем хранения (stor-
age duration). Существуют три разных вида времени хранения переменных.

 


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

Другое свойство, присущее переменным — это область видимости (scope).
Область видимости переменной — это просто часть программы, на протяжении ко-
торой имя данной переменной определено. Вне этой области видимости вы не мо-
жете ссылаться на ее имя — любая попытка сделать это вызовет ошибку компиляции.
Обратите внимание, что переменная все еще может существовать вне этой области
видимости, даже несмотря на то, что вы не можете обратиться к ней по имени. Чуть
позднее будут даны примеры подобной ситуации.


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

Автоматические переменные

Все переменные, которые вы объявляли до сих пор, объявлялись внутри блока —
то есть, внутри пары фигурных скобок. Такие переменные называются автоматиче-
скими, и о них говорят, что у них локальная область видимости, или область види-
мости блока. Автоматическая переменная "видима", начиная с точки, в которой она
объявлена, и до конца блока, содержащего ее объявление. Пространство, которое за-
нимает автоматическая переменная, выделяется автоматически в области памяти, на-
зываемой стеком, которая специально предназначена для этой цели. По умолчанию
размер стека составляет 1 Мбайт, чего достаточно для большинства случаев, хотя
если его не хватает, вы можете увеличить размер стека, установив опцию проекта
/STACK в необходимое значение по своему выбору.

Автоматическая переменная "рождается" в момент ее определения, и для нее
выделяется пространство в стеке, а прекращает свое существование в конце блока,
содержащего ее определение. Это происходит в точке, где находится закрывающая
фигурная скобка, которая соответствует первой открывающей фигурной скобке,
предшествующей объявлению этой переменной. Каждый раз, когда выполняется блок
операторов, содержащий объявление автоматической переменной, переменная созда-
ется вновь, и если вы специфицируете начальное значение автоматической перемен-
ной, она повторно инициализируется при каждом создании. Когда автоматическая
переменная, исчезает, занятая ею память в стеке освобождается для использования
другими автоматическими переменными.

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

Эффект области видимости автоматических переменных демонстрируется на сле-
дующем примере.



рассказал об области видимости.



Вывод этого примера:

Описание полученных результатов

Первые два оператора объявляют и определяют две целочисленных переменных,
countl и count3, с начальными значениями 10 и 50 соответственно. Обе эти пере-
менные существуют, начиная с этой точки и до закрывающей скобки в конце про-
граммы. Область видимости этих переменных также распространяется до закрываю-
щей скобки в конце main ().

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

Вслед за определением переменной значение countl выводится на экран, что дает
первую из строк, показанных выше. Далее идет новая открывающая фигурная скобка,
с которой начинается новый блок. Две переменных, countl и count2, определены в
этом блоке, со значениями 20 и 30 соответственно. Переменная countl, объявленная
здесь, отличается от первой переменной countl. Первая countl все еще существу-
ет, но ее имя замаскировано второй переменной countl. Любое упоминание имени
countl после объявления внутри вложенного блока ссылается на countl, объявлен-
ную в этом блоке.

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

Значение во второй строке вывода демонстрирует, что внутри вложенного блока
обращение к имени countl означает обращение к переменной, имеющей область ви-
димости этого блока, то есть объявленной во внутренних фигурных скобках:

 



Данные, переменные и вычисления 115

 

Если бы здесь использовалась внешняя переменная count 1, то было бы выведено
ее значение — 10. Далее значение count 1 увеличивается следующим оператором:

Инкремент касается переменной, имеющей внутреннюю область видимости блока,
поскольку внешняя все еще сокрыта. Однако count 3, которая была определена во внеш-
ней области видимости, увеличивается в следующем операторе без всяких проблем:

Это доказывает, что переменные, объявленные в начале внешней области види-
мости, доступны также и во вложенной области. (Обратите внимание, что если бы
count3 была объявлена после закрывающей скобки вложенного блока, она бы также
существовала во внешней области видимости, но в этом случае она бы еще не суще-
ствовала во вложенном блоке.)

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

Если вы снимите комментарий со следующей строки:

 

 


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

Это потому, что в этой точке count2 вышла из своей области видимости.

Размещение объявлений переменных

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

Можно поместить объявление переменной вне любой функции, входящей в про-
грамму. В следующем разделе будет рассказано о последствиях таких объявлений.

Глобальные переменные

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


116 Глава 2

 

Глобальные переменные также по умолчанию имеют статическое время жизни.
Глобальные переменные со статическим временем жизни существуют с момента нача-
ла выполнения программы и до момента ее завершения. Если вы не специфицируете
начальное значение глобальной переменной, то по умолчанию она инициализируется
нулем. Инициализация глобальных переменных происходит перед началом выполне-
ния функции main (), поэтому они всегда доступны в любой части кода, находящейся
внутри области видимости переменной.

На рис. 2.12 показано содержимое исходного файла Expample. срр, и стрелками
указана область видимости каждой переменной.


Puc. 2.12. Области видимости переменных


Переменная valuel, которая появляется в начале файла, объявлена с глобаль-
ной областью видимости, как и value4, которая появляется после функции main ().
Область видимости каждой глобальной переменной простирается от точки ее опре-
деления до конца файла. Даже несмотря на то, что value4 существует в момент на-
чала выполнения программы, к ней нельзя обратиться из тела main (), потому что
main () не находится в ее области видимости. Для того чтобы main () могла обратить-
ся к переменной value4, ее объявление следует переместить в начало файла. Обе
переменные — и valuel, и value4 — по умолчанию будут инициализированы нулем,
что отличает их от автоматических переменных. Обратите внимание, что локальная
переменная по имени valuel в function () скрывает глобальную переменную с тем
же именем.


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

Реальные программы обычно состоят из множества операторов, существенного
количества функций и огромного числа переменных. Объявление всех переменные
глобальными значительно увеличивает риск непреднамеренной их модификации, а
также существенно затрудняет работу по их именованию. К тому же они будут зани-
мать память в течение всего времени выполнения программы. Сохраняя переменные
локальными по отношению к функции или блоку, вы можете быть уверенными, что
они почти полностью защищены от внешних эффектов, потому что они существуют
и занимают память, только начиная с точки их объявления и до завершения включа-
ющего блока. При этом становится легче управлять всем процессом разработки.

Если в ввзглянете на панель Class View (Представление классов) в правой части
окна IDE для любого примера из тех, что были рассмотрены до сих пор, и развернете
дерево классов проекта, щелкнув на знаке +, то увидите там элемент по имени Global
Functions and Variables (Глобальные функции и переменные). Если щелкнуть на нем,
можно увидеть список всего того в вашей программе, что имеет глобальную область
видимости. Это включает все глобальные функции, а также все объявленные глобаль-
ные переменные.


Практическое занятие


Операция разрешения контекста


 



Описание полученных результатов

Выделенные полужирным строки кода указывают изменения, которые я внес в
предыдущий пример. Поговорим только о них. Объявление переменной countl,
предшествующее определению функции main () — глобальное, поэтому в принципе
она доступна на всем протяжении функции main (). Эта глобальная переменная ини-
циализируется значением 100:

 

 


Однако у вас есть две других переменных countl, определенные внутри main (),
поэтому по всей программе глобальная переменная countl скрыта локальными
countl. Первый новый оператор вывода:

 

 


Здесь используется операция разрешения контекста (::), поясняющая компиля-
тору, что вы хотите обратиться к глобальной переменной countl, а не к локальной.
Посмотрев на вывод программы, можно убедиться, что это работает.

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

 


Оно отображает значение 100, как и ранее — "длинная рука" операции разрешения
контекста, использованной в такой манере, всегда достанет глобальную переменную.

Ранее вы уже видели, что можно сослаться на имя в пространстве имен std, дополнив его
именем этого пространства имен— как, например, в случае std::cout и std::endl.
Компилятор ищет указанное имя в пространстве имен, имя которого совпадает с левым
операндом операции разрешения контекста. В предыдущем примере вы использовали опера-
цию разрешения контекста для поиска в глобальном пространстве переменной countl. Тем,
что перед оператором не было указано имя пространства имен, вы сообщили компилятору,
что для поиска имени он должен обратиться к глобальному пространству имен.


Данные, переменные и вычисления 119

 

Вы узнаете еще больше об этой операции, когда в главе 9 пойдет речь об объектно-
ориентированном программировании, где она применяется очень широко.

Статические переменные

Вероятно, рано или поздно вам понадобится иметь переменную, которая опреде-
лена и доступна локально, но продолжает существовать после выхода из блока, в ко-
тором она объявлена. Другими словами, необходимо иметь возможность объявить
переменную, имеющую область видимости в пределах блока, но обеспечить ей ста-
тическое время хранения. Спецификатор static обеспечивает такую возможность,
а потребность в ней станет более очевидной, когда мы будем говорить о функциях в
главе 5.

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

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

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

 

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

Я уже несколько раз упоминал о пространствах имен, так что теперь наступило
время дать более точное представление об этом понятии. Пространства имен не при-
меняются в библиотеках, поддерживающих MFC, но библиотеки, поддерживающие
CLR и Windows Forms, используют их интенсивно, и стандартная библиотека ANSI
С++, конечно, тоже.

Вы уже знаете, что все имена, используемые в стандартной библиотеке ISO/ANSI
С++, определены в пространстве имен std. Это значит, что все имена, встречающие-
ся в стандартной библиотеке, имеют дополнительное квалифицирующее имя — std,
поэтому cout, например — это на самом деле std:: cout. Вы можете видеть примене-
ние полных квалифицированных имен в следующем тривиальном примере.


120 Глава 2

 

Объявление переменной value находится вне определения main (). Об этом
объявлении также говорят, как об области видимости глобального пространства имен,
потому что объявление переменной находится вне какого-либо пространства имен.
Переменная доступна в любом месте main (), равно как и из определения любой дру-
гой функции, которая может встретиться в том же исходном файле. Я поместил объ-
явление value вне main (), просто чтобы продемонстрировать в следующем разделе,
как его можно поместить в пространство имен.

Обратите внимание на отсутствие объявления using для cout и endl. В данном
случае оно не нужно, поскольку имена из пространства имен std квалифицированы
полностью. Может быть, поступать так и неразумно, но вы можете использовать cout
в качестве имени своей переменной, и это не приведет ни к какой путанице, потому
что cout будет отличаться от std::cout. Таким образом, пространства имен предо-
ставляют возможность отделить имена, используемые в одной части программы, от
имен, применяемых в другой. Это чрезвычайно ценно, когда речь идет о крупном
проекте, в работе над которым задействовано несколько групп программистов, зани-
мающихся разными частями программы. Каждая команда может иметь собственное
пространство имен, и в этом случае можно не беспокоиться о том, что они непредна-
меренно используют одно и то же имя для различных функций.

Взгляните на следующую строку кода:

Этот оператор называется директивой using.

Эффект от его применения состоит в том, что все имена из пространства std
импортируются в ваш исходный файл, так что вы можете из своей программы ссы-
латься на все, что определено в этом пространстве имен, без квалифицированного
имени. То есть, вы можете писать имя cout вместо std::cout и end! вместо std::
endl. Недостатком такого применения директивы using является то, что сводится
на нет основная причина применения пространств имен — то есть, предотвращение
непреднамеренных конфликтов. Самый безопасный способ доступа к именам из про-
странства имен — это квалифицировать каждое имя явно вместе с наименованием
пространства имен; к сожалению, это делает код очень многословным и снижает его
читабельность. Другая возможность — представить объявлением using только те име-
на, которые вы используете в своем коде, например:

 

 


Эти операторы называются объявлениями using. Каждый оператор представляет
одно имя из указанного пространства и позволяет применять его без квалификации
внутри последующего программного кода. Это обеспечивает более удобный способ
импортирования из пространства имен, поскольку импортируются лишь те имена, ко-
торые действительно нужны в программе. Поскольку Microsoft установила прецедент
импорта всего пространства имен System для кода C++/CLI, я буду придерживаться
этого в примерах C++/CLI. Но вообще я рекомендую использовать в своем коде объ-
явления using вместо директив using, когда вы пишете программы сколько-нибудь
значительного размера.

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


Данные, переменные и вычисления 121

 

Объявление пространства имен

Для объявления пространства имен применяется ключевое слово namespace:

 

 


Это определяет пространство имен по имени myStuf f. Все объявления имен
в коде между фигурными скобками будут определены внутри пространства имен
myStuf f, поэтому для доступа к любому из этих имен из точки, находящейся вне дан-
ного пространства, имя должно быть квалифицированным, то есть, снабженным пре-
фиксом , либо должно присутствовать объявление using, идентифицирую-
щее, что данное имя относится к пространству myStuf f.

Вы не можете объявить пространство имен внутри функции. Оно предназначе-
но для другого применения: вы используете в вашей программе пространства имен
как контейнер для функций, глобальных переменных и других именованных сущно-
стей наподобие классов. Функция main (), где начинается выполнение программы,
всегда должна быть в глобальном пространстве имен, иначе компилятор не распозна-
ет ее.

Вы можете поместить переменную value из предыдущего примера в пространство
имен:


Дата добавления: 2015-11-04; просмотров: 20 | Нарушение авторских прав







mybiblioteka.su - 2015-2024 год. (0.029 сек.)







<== предыдущая лекция | следующая лекция ==>