Объектные технологии конструирования ПО Лекция № 6 6.
Объектные технологии конструирования ПО Лекция № 6 6. 1 Принципы использования переменных 6. 2 Именование переменных 6. 3 Основные типы данных 6. 4 Нестандартные типы данных
Лекция 6. Принципы использования переменных Грамотное объявление переменных Неявное объявление переменных – попытка использовать необъявленную переменную (например, в Microsoft Visual Basic), в результате чего компилятор может автоматически объявить ее для вас (это зависит от параметров компилятора). n Отключите неявные объявления. Некоторые компи - ляторы позволяют запретить неявные объявления : в Visual Basic для этого служит директива Option Explicit On , которая заставляет объявлять все используемые переменные. n Объявляйте все переменные : печатая имя новой переменной, объявите ее, даже если компилятор этого не требует.
Лекция 6. Принципы использования переменных Грамотное объявление переменных n Используйте конвенции именования. Чтобы не использовать две переменные там, где предполагается одна, задайте конвенцию употребления популярных суффиксов в именах переменных. n Проверяйте имена переменных. Просматривайте список перекрестных ссылок, сгенерированный компилятором , позволяющий узнать о применении двух переменных с похожими именами , могут указывать на объявленные, но неиспользованные переменные.
Лекция 6. Принципы использования переменных Неверная инициализация данных При неверной инициализации проблемы объясняются тем, что перемен - ная имеет не то первоначальное значение, которое ожидается. n Переменной не было присвоено значения. Она имеет такое случайное значение, которое находилось в ячейках памяти при запуске программы. n Значение переменной устарело : переменной было присвоено значение, но оно утратило свою актуальность. n Одним частям переменной были присвоены значения, а другим нет.
Лекция 6. Принципы использования переменных Правила инициализации переменных n Инициализируйте каждую переменную при ее объявлении. Пример (C++): float student. Grades[ MAX_STUDENTS ] = { 0. 0 }; n Инициализируйте каждую переменную там, где она используется в первый раз. Пример хорошей инициализации переменных (Visual Basic): Dim accountlndex As Integer accountlndex = 0 ' использование переменной accountlndex n Объявляйте и определяйте каждую переменную непосредственно перед первым обращением к ней. Пример хорошей инициализации переменных (Java): int account. Index = 0; // использование переменной account. Index.
Лекция 6. Принципы использования переменных Правила инициализации переменных n Объявляйте переменные по мере воз - можности как final или const. Объявление переменных как final (в Java ) или const (в C++) предотвращает изменение ее значения после инициализации ; они полезны для определения констант класса. n Уделяйте особое внимание счетчикам и аккумуляторам. Переменные i , j , k , sum часто играют роль счетчиков или аккумуляторов. Нередко программисты забывают обнулить счетчик или аккумулятор перед его использованием в очередной раз. n Инициализируйте данные-члены класса в его конструкторе. Если в конструкторе выделяется память, в деструкторе ее следует освободить.
Лекция 6. Принципы использования переменных Правила инициализации переменных n Проверяйте необходимость повторной ини - циализации. Убедитесь в том , что команда инициализации входит в повторяющийся фрагмент кода. n Инициализируйте именованные константы один раз; переменные инициализируйте в исполняемом коде. Если переменные служат для имитации именованных констант, следует инициализировать их один раз при запуске программы; истинные переменные инициализируйте в исполняемом коде недалеко от места их вызова.
Лекция 6. Принципы использования переменных Правила инициализации переменных n Настройте компилятор так, чтобы он автомати- чески инициализировал все переменные. Докумен - тируйте использование параметров компилятора – без такой документации предположения, основанные на конкретных параметрах компилятора, определить трудно. n Проверяйте корректность входных параметров : Прежде чем присвоить входные значения чему-либо, убедитесь, что они допустимы. n Инициализируйте рабочую память при запуске программы. Инициализация рабочей памяти известным значением облегчает поиск ошибок инициализации.
Лекция 6. Принципы использования переменных Области видимости n Областью видимости – фрагмент программы, в котором переменная известна и может быть использована. n Переменная с ограниченной областью видимости известна только в небольшом фрагменте программы: индекс в теле цикла. n Переменная с большой областью видимости известна во многих местах программы. n Область видимости можно понимать как “ известность ” переменной в программе.
Лекция 6. Принципы использования переменных Области видимости Локализуйте обращения к переменным. Код, расположенный между обращениями к переменной, является “ окном уязвимости ” : чем больше окно, тем выше вероятность, что в его пределах будет добавлен новый код, искажающий значение переменной. Поэтому обращения к переменной всегда целесообразно локализовать, группируя их вместе.
Лекция 6. Принципы использования переменных Области видимости Одним из методов оценки степени сгруппированности обращений к переменной является определение "интервала" (span) между обращениями. Пример интервалов, равных 1 и 0 (Java): а = 0; b = 0; с = 0; b = а + 1; b = b / с; Между первым и вторым обращениями к b – одна строка кода, а между вторым и третьим обращениями строк нет, поэтому интервалы равны соответственно 1 и 0.
Лекция 6. Принципы использования переменных Области видимости Делайте время жизни переменных как можно короче. • С интервалом между обращениями к переменной тесно связано “ время жизни ” переменной – общее число строк, на протяжении которых переменная используется. Жизнь переменной начинается при первом обращении к ней, а заканчивается при последнем. • В отличие от интервала время жизни переменной не зависит от числа обращений к ней между первым и последним обращениями. • Интервал и время ее жизни нужно делать как можно короче: это уменьшает окно уязвимости, снижая вероятность неверного или неумышленного изменения переменной между нужными обращениями к ней.
Лекция 6. Принципы использования переменных Области видимости Пример короткого времени жизни переменных (Java): 25 record. Index = 0; 26 while ( recordlndex < record. Count ) { 27. . . 28 record. Index = record. Index + 1; … 62 total = 0; 63 done = false; 64 while ( !done ) { 69 if ( total > projected. Total ) { 70 done = true; Времена жизни переменных равны: recordlndex (строка 28 - строка 25 + 1) = 4 total (строка 69 - строка 62 + 1) = 8 done (строка 70 - строка 63 + 1) = 8 Среднее время жизни (4 + 8) / 3 = 7
Лекция 6. Принципы использования переменных Советы по минимизации области видимости n Инициализируйте переменные, исполь - зуемые в цикле, непосредственно перед циклам, а не в начале метода, содержащего цикл : это снизит вероят - ность того, что при изменении цикла вы забудете изменить инициализацию исполь- зуемых в нем переменных. n Не присваивайте переменной значение вплоть до его использования : иногда трудно найти строку, в которой переменной было присвоено ее значение. Пример объявления и инициализации переменных (C++): int receipt. Index = 0; float daily. Receipts = Todays. Receipts(); double total. Receipts = Total. Receipts( daily. Receipts );
Лекция 6. Принципы использования переменных Советы по минимизации области видимости n Начинайте с самой ограниченной области видимости и расширяйте ее только при необходимости. • Чтобы минимизировать область видимости переменной, постарайтесь сделать ее как можно более локальной. • Область видимости гораздо сложнее сжать, чем расширить: превратить глобальную переменную в переменную класса сложнее, чем наоборот. • Выбирайте наименьшую возможную область видимости переменной: делайте переменную локальной для отдельного цикла, локальной для конкретного метода, затем – закрытой переменной класса, затем – защищенной, далее – включайте ее в пакет и в крайнем случае делайте глобальной.
Лекция 6. Принципы использования переменных Персистентность “ Персистентность ” – характеризует длительность существования данных. Некоторые переменные “живут”: n пока выполняется конкретный блок кода или метод: например, переменные, объявленные внутри цикла for языка C++ или Java; n сколько вы им позволяете: в Java переменные, созданные при помощи оператора new , “ живут ” до сборки мусора; в C++ переменные существуют, пока не будут уничтожены с помощью оператора delete;
Лекция 6. Принципы использования переменных Персистентность n до завершения программы: этому описанию соот - ветствуют глобальные переменные в большинстве языков, а также статические переменные в языках C++ и Java; n всегда: такими переменными могут быть значения, которые вы храните в БД между запусками программы. Главная проблема персистентности возникает, когда программист предполагает, что переменная существует дольше, чем есть на самом деле.
Лекция 6. Принципы использования переменных Решение проблемы персистентности n Включайте в программу отладочный код для проверки важных переменных на предмет допустимости их значений. n Завершив работу с переменными, присваивайте им “ недопустимые значения ” : после освобождения памяти при помощи оператора delete можно установить указатель в null. n Исходите из того, что данные не являются персистентными : если при возврате из метода переменная имеет определенное значение, не предполагайте, что оно будет таким же при следующем вызове метода. n Объявляйте и инициализируйте все данные перед их
Лекция 6. Принципы использования переменных Время связывания Время связывания – момент, когда переменная и ее значение связываются вместе. Выгоднее использовать более позднее связывание: чем позже оно выполняется, тем более гибким будет n код. при написании кода; n при компиляции (с использованием именованной константы); n при загрузке программы (путем чтения значения из внешнего источника, такого как реестр Windows); n при создании объекта (например, путем чтения значения при каждом создании окна); n по требованию (например, посредством чтения значения при каждой перерисовке окна).
Лекция 6. Принципы использования переменных Пример связывания во время написания кода (Java): title. Bar. color = 0 x. FF ; // 0 x. FF – шестнадцатеричное значение синего цвета. Пример связывания во время компиляции (Java): private static final int C 0 L 0 R_BLUE = 0 x. FF; private static final int TITLE_BAR_C 0 L 0 R = C 0 L 0 R_BLUE; title. Bar. color = TITLE_BAR_COLOR; Пример связывания в период выполнения (Java): title. Bar. color = Read. Title. Bar. Color();
Лекция 6. Принципы использования переменных Связи между данными и управляющими структурами Последовательные данные соответствуют последовательности команд: Последовательные данные ( sequential data ) – это набор блоков данных, используемых в определенном порядке. Если есть пять команд подряд, обрабатывающих пять разных значений, они формируют последователь- ность команд.
Лекция 6. Принципы использования переменных Связи между данными и управляющими структурами Селективные данные соответствуют операторам if и case: Селективные данные (selective data ) – набор, допускающий использование одного и только одного элемента данных в каждый конкретный момент времени. Соответствующими командами, выполняющими фактический выбор данных, являются операторы if - then-else или case.
Лекция 6. Принципы использования переменных Связи между данными и управляющими структурами Итеративные данные соответствуют циклам: Итеративные данные ( iterative data ) представляют собой данные одного типа, повторяющиеся более одного раза. Обычно они хранятся как элементы контейнера, записи файла или элементы массива.
Лекция 6. Принципы использования переменных Единственность цели каждой переменной n Избегайте переменных, имеющих скрытый смысл. Использования переменной более чем с одной целью заключается в том, что разные значения переменной имеют разный смысл. n Убеждайтесь в том, что используются все объявленные переменные. n Используйте каждую переменную только с одной целью : не вызывайте одну переменную в двух разных местах для решения двух разных задач. В таких случаях переменной приходится присваивать неудачное имя, соответствующее одной из ее целей.
Лекция 6. Именование переменных Как именовать переменные? Имя переменной нельзя выбирать, как кличку собаке: опираясь на его вычурность или звучание. Переменная и ее имя формируют по идее одну сущность. Выбирайте имена переменных всей тщательностью. Пример неудачного именования переменных (Java): х = х - хх; ххх = fido + Sales. Tax( fido); x = x + Late. Fee(x 1, x) + xxx; Пример удачного именования переменных (Java): balance = balance - last. Payment; monthly. Total = new. Purchases + Sales. Tax( new. Purchases ); balance = balance + Late. Fee( customerl. D, balance ) + monthly. Total;
Лекция 6. Именование переменных Принцип именование переменных Важнейший принцип именования переменных состоит в том, что имя должно полно и точно опи - сывать сущность, представляемую переменной. n Один эффективный способ выбора хорошего имени предполагает формулирование сути переменной в словах. n Оптимальным именем переменной часто является само это высказывание: оно легко читаемо и однозначно, его нельзя спутать с чем-либо другим и легко запомнить.
Лекция 6. Именование переменных Примеры удачных и неудачных имен n Имена должны быть максимально конкретны. Имена х, temp , i достаточно общие для того, чтобы их можно было использовать более чем с одной целью. n Хорошее имя чаще всего описывает проблему, а не ее решение: оно выражает что, а не как. Суть переменной Удачные имена Неудачные имена Сумма, на которую на данный running. Total, check. Total written, ct, checks, CHKTTL момент выписаны чеки velocity, train. Velocity, Скорость поезда velt, v, tv, x, xl, x 2, train cd, velocityln. Mph Текущая дата current. Date, todays. Date current, c, x, xl, x 2, date lpp, Число строк на странице lines. Per. Page lines, l, x, xl, x 2
Лекция 6. Именование переменных Оптимальная длина имени переменной n Оптимальная длина имени лежит между длинами имен х и maximum. Number. Of. Points. In. Modern. Olympics. n Слишком короткие имена страдают от недостатка смысла. n Слишком длинные имена надоедает печатать, они могут сделать неясной визуальную структуру программы. number. Of. People. On. The. Us. Olympic. Team Слишком длинные имена: number. Of. Seats. In. The. Stadium maximum. Number. Of. Points. In. Modern. Olympics n, nр, ntm, Слишком короткие имена: n, nsisd, т, тр, max, points num. Team. Members, team. Member. Count То, что надо: num. Seats. In. Stadium, seat. Count team. Points. Max, points. Record
Лекция 6. Именование переменных Имена переменных и области видимости n Если вы присваиваете переменной короткое имя, то длина имени должна говорит ь о том, что она имеет ограниченную область действия. n Более длинные имена лучше присваивать редко используемым или глобальным переменным, а более короткие – локальным переменным или переменным, вызываемым в циклах. n С короткими именами связано много проблем, и программисты, придерживающиеся политики защитного программирования, избегают их.
Лекция 6. Именование переменных Имена переменных и области видимости Дополняйте имена, относящиеся к глобальному пространству имен, спецификаторами : Если есть переменные, относящиеся к глобальному пространству имен, следует разделить глобальное пространство имен на части и предотвратить конфликты имен. В C ++ и С # для разделения глобального пространства имен применяют ключевое слово namespace , в Java – пакеты. Пример ключевого слова namespace (C++): namespace User. Interface. Subsystem { // объявления переменных } namespace Database. Subsystem { // объявления переменных }
Лекция 6. Именование переменных Спецификаторы вычисляемых значений Используя переменные, содержащие вычисляемые значения (суммы, средние величины, максимумы) , необходимо дополнять имена спецификаторами Total , Sum , Average , Max , Min Record, String или Pointer, указывая его в конце имени. Пример: revenue. Total, expense. Average. Это улучшает заметность и читаемость пере- менной , предотвращает путаницу, возможную при наличии в одной программе имен total. Revenue и revenue. Total.
Лекция 6. Именование переменных Именование индексов циклов Как правило, в качестве индексов циклов используют переменные i, j и k: Пример простого имени индекса цикла (Java): for ( i = first. Item; i < last. Item; i++ ) { data[ i ] = 0; } Если переменную предполагается использовать вне цикла, ей следует присвоить более выразительное имя. Пример удачного описательного имени индекса цикла (Java): record. Count = 0; while ( more. Scores() ) { score[ record. Count ] = Get. Next. Score(); record. Count++; }
Лекция 6. Именование переменных Именование индексов циклов Если у вас есть несколько вложенных циклов, присвойте индексам более длинные имена: Пример удачного именования индексов вложенных циклов (Java): for ( team. Index = 0; team. Index < team. Count; team. Index++ ) { for ( event. Index = 0; event. Index < event. Count[ team. Index ]; event. Index++ ) { score[ team. Index ][ event. Index ] = 0; } }
Лекция 6. Именование переменных статуса Переменные статуса характеризуют состояние программы. n Старайтесь не присваивать переменной статуса имя flag. Имя флага не должно включать фрагмент flag, так как оно ничего не говорит о сути флага; для ясности флагам следует присваивать выразительные значения. Примеры загадочных флагов (C++): if (flag ). . . if (status. Flag & 0 x 0 F ). . . if (print. Flag == 16 ). . . Примеры грамотного использования переменных статуса (C++): if ( data. Ready ). . . if ( character. Type & PRINTABLE_CHAR ). . . if ( report. Type == Report. Type_Annual ) …
Лекция 6. Именование переменных статуса Объявление переменных статуса (C++): // возможные значения переменной Character. Type const int LETTER = 0 x 01; const int DIGIT = 0 x 02; const int PRINTABLE_CHAR = ( LETTER | DIGIT | PUNCTUATION | LINE_DRAW ); const int CONTROL_CHARACTER = 0 x 80; // возможные значения переменной Report. Type enum Report. Type { Report. Type_Daily, Report. Type_Monthly, Report. Type_Quarterly, Report. Type_All };
Лекция 6. Именование переменных Именование временных переменных Временные переменные служат для хранения промежуточных результатов вычислений и служебных значений программы. Пример неинформативного имени "временной" переменной (C++): // Вычисление корней квадратного уравнения. temp = sqrt( b^2 - 4*а*с ); root[0] = ( - b + temp ) / ( 2 * a ); root[1] = ( - b - temp ) / ( 2 * a ); Пример замены "временной" переменной на реальную переменную (C++): discriminant = sqrt( b^2 - 4*а*с ); root[0] = ( - b + discriminant ) / ( 2 * a ); root[1] = ( - b - discriminant ) / ( 2 * a );
Лекция 6. Именование переменных Именование булевых переменных Наиболее полезные имена булевых переменных: n done – признак завершения цикла или другой операции: присвойте false до выполнения действия и true – после его завершения. n error – признак ошибки. n found – определения того, обнаружено ли некоторое значение. n success – признак успешного завершения операции. Можно дополнять имена префиксом is: is. Done, is. Error и т. д.
Лекция 6. Именование переменных Именование булевых переменных n Присваивайте булевым переменным имена, подразумевающие значение true или false. Имена вроде status и source. File не годятся, так как при этом значения true или false не имеют ясного смысла. n Используйте утвердительные имена буле - вых переменных. Имена, основанные на отрицании ( not. Found , not. Done ), при выполнении над переменной операции отрицания становятся менее понятны.
Лекция 6. Именование переменных Именование перечислений n Принадлежность переменных к тому или иному перечислению можно пояснить, дополнив их имена префиксами, такими как Color_, Planet_ или Month_. n Члены перечислений являются константами, поэтому имена перечислений следует форматировать как имена констант. n В некоторых языках перечисления рассматриваются скорее как классы, а именам членов перечисления всегда предшествует имя самого перечисления, например, Color_Red или Planet_Earth. n Если вы используете подобный язык, повторять префикс не имеет смысла, поэтому можно считать префиксом само имя перечисления и сократить имена до Color. Red и Planet. Earth.
Лекция 6. Именование переменных Именование констант n Имя константы должно характеризо - вать абстрактную сущность, представ- ляемую константой, а не конкретное значение. n Имя FIVE – плохое имя константы (независимо от того, имеет ли она значение 5. 0) , а CYCLES_NEEDED – хорошее имя.
Лекция 6. Именование переменных Зачем нужны конвенции? n Позволяют больше принимать как данное. Приняв одно общее решение вместо нескольких более узких, можно сосредоточиться на более важных аспектах кода. n Помогают использовать знания, полученные при работе над предыдущими проектами. n Ускоряют изучение кода нового проекта. n Они подавляют “ размножение ” имен. Не применяя конвенцию, можно легко присвоить одной сущности два разных имени. Пример: общее число баллов и point. Total, и total. Points. n Компенсируют слабости языка : можно использовать конвенции для имитации именованных констант и перечислений. Конвенции разграничевают локальные данные, данные класса и глобальные данные.
Лекция 6. Именование переменных Когда использовать конвенцию? n над проектом работают несколько программистов; n программу будут изменять и сопро - вождать другие программисты; n обзор программы выполняют другие программисты из вашей компании; n программа так велика, что вы не можете полностью охватить ее умом, а вынуждены рассматривать по частям; n программа будет использоваться длительное время, из- за чего вам, возможно, придется вернуться к ней через несколько недель или месяцев;
Лекция 6. Именование переменных Степень формальности конвенции n Конвенциям именования могут соответствовать разные степени формальности. Неформальная конвенция может быть совсем простой: "Используйте выразительные имена". n Оптимальная степень формальности конвенции опреде - ляется числом людей, работающих над программой, размером программы и ожидаемым временем ее использования. n При работе над крошечными проектами следование строгой конвенции окажется пустой тратой сил. n В случае более крупных проектов, реализуемых с участием нескольких программистов, формальная кон- венция – важнейшее средство улучшения удобочитае - мости программы.
Лекция 6. Именование переменных Конвенция, не зависящая от языка n Проведите различие между именами переменных и именами методов: имена объектов начинаются со строчной буквы, а имена методов – с прописной: variable - Name, но Routine. Name(). n Проведите различие между классами и объектами: имена типов могут отличаются от имен переменных регистром первой буквы , регистром всех букв , дополнительным префиксом “t_”, “a” и др. n Идентифицируйте глобальные переменные: например, при помощи префикса g_. n Идентифицируйте переменные-члены: например, при помощи префикса т_. n Идентифицируйте определения типов , именованных констант, элементов перечисления и др.
Лекция 6. Именование переменных Конвенция С++ n имена указателей дополняются префиксом р; n имена констант, типов, определяемых с помощью директивы typedef, и макросов препроцессора включают ТОЛЬКО-ЗАГЛАВНЫЕ_БУКВЫ; n имена классов и других типов содержат буквы. Обоих. Регистров; n первое слово в именах переменных и методов начинается со строчной буквы, а все последующие слова – с заглавной: имя. Переменной. Или. Метода; n символ подчеркивания используется только в именах, состоящих полностью из заглавных букв, и после некоторых префиксов.
Лекция 6. Именование переменных Конвенция Java n i и j – имена целочисленных индексов; n имена констант включают ТОЛЬКО_ЗАГЛАВ- НЫЕ_БУКВЫ, а слова разделяются символами подчеркивания; n все слова в именах классов и интерфейсов начинаются с заглавной буквы: Имя. Класса - Или. Интерфейса; n в именах переменных и методов с заглавной буквы начинаются все слова, кроме первого: имя. Переменой. Или. Метода; n символ подчеркивания служит разделителем только в именах, полностью состоящих из заглавных букв; n имена методов доступа начинаются с префикса get или set.
Лекция 6. Именование переменных Сокращение имен переменных n используйте стандартные , общепри - нятые аббревиатуры; n удаляйте все гласные, не являющие - ся первыми буквами имен ( screen – scrn, integer – intgr); n удаляйте артикли и союзы (and, or, the, и др), суффиксы ing, ed; n сохраняйте одну или несколько первых букв каждого слова; n "обрезайте" слова после первой, второй или третьей буквы; n проверяйте, чтобы смысл имени переменной в ходе сокращения не искажался; n используйте эти способы, пока не сократите имя каждой переменной до 8 -20 символов.
Лекция 6. Именование переменных Имена, которые следует избегать n Избегайте обманчивых имен или аббревиатур: убедитесь в том, что имя не является двусмысленным. n Избегайте имен, имеющих похожие значения: если есть вероятность, что вы можете спутать имена двух переменных и это не приведет к ошибке компиляции, переименуйте обе переменных. Например: пары имен input и input. Value, record. Num и num. Records. n Избегайте имен, имеющих похожее звучание, таких как wrap и rap. n Избегайте имен, включающих цифры: если необходимо использовать цифры в именах, используйте вместо отдельных переменных массив. Пример: избегайте имен file 1 и file 2.
Лекция 6. Именование переменных Имена, которые следует избегать n Проводите различие между именами не только по регистру букв: избегайте сокращения понятий типа fired , final review duty и full revenue disbursal соответственно до frd , FRD и Frd. Их связь с конкретными значениями произвольна и непонятна. n Избегайте слов, при написании которых люди час- то допускают ошибки: acummulate, definate и др. n Избегайте смешения естественных языков: если в проекте участвуют программисты разных националь - ностей, обяжите их именовать все элементы программы, используя один естественный язык.
Лекция 6. Именование переменных Имена, которые следует избегать n Избегайте имен стандартных типов, переменных и методов: просматривайте иногда списки зарезерви - рованных слов и имен. n He используйте имена, которые совершенно не связаны с тем, что представляют переменные: использование имен вроде margaret и pookie гарантиру - ет, что никто их не поймет. Не называйте переменные в честь девушки, любимого сорта пива и т. д. , если только они не являются представляемыми “сущностями”. n Избегайте имен, содержащих символы, которые можно спутать с другими символами: если два имени различаются только одним таким символом. Пример: eye. Chart 1, eye. Chart. I, eye. Chartl.
Лекция 6. Основные типы данных Числовые данные n Избегайте “ магических чисел ” : это обычные числа (например: 100 ; 47524 ) , которые появляются в программе без объяснений. Вместо них используйте именованные константы или глобальные переменные. n Ошибка деления на ноль. Если используется операция деления , напишите код, предупреждающий появление ошибки деления на 0. n Выполняйте преобразования типов понятно. Пример: у = х + (float) i;
Лекция 6. Основные типы данных Числовые данные n Применяйте жестко заданные нули и единицы по необходимости : значения 0 и 1 используются для инкремента, декремента, а также в начале циклов при нумерации первого элемента массива. n Избегайте сравнений разных типов : делайте преобразования вручную, так чтобы компилятор мог сравнить два числа одного и того же типа.
Лекция 6. Основные типы данных Целые числа n Проверяйте целочисленность операций деления. Пример: 10 * (7/10) = (10*7) / 10 = 7. В целочисленной арифметики: 10 * (7/10) = 0 (т. к. деление (7/10) равно 0). n Проверяйте переполнение целых чисел. Пример: 250 * 300 = 75 000. Если максимальное целое = 65 535, то из-за переполнения 250 * 300 = 9464 (т. к. 75 000 - 65 536 = 9464). n Проверяйте на переполнение промежуточные результаты: Число, получаемое в конце вычислений, – не единственное, о котором следует беспокоиться.
Лекция 6. Основные типы данных Числа с плавающей точкой n Избегайте сложения и вычитания слишком разных по размеру чисел. Пример: Для 32 -битной переменной с плавающей запятой: 1 000, 00 + 0, 1 = 1 000, 00 , так как в 32 битах недостаточно значимых цифр, чтобы охватить интервал между 1 000 и 0, 1. n Избегайте сравнений на равенство : числа с плавающей точкой, которые должны быть равны, на самом деле равны не всегда.
Лекция 6. Основные типы данных Числа с плавающей точкой Пример неправильного сравнения чисел с плавающей точкой (Java): //Переменная nominal – 64 -битное вещественное число. double nominal =1. 0; double sum = 0. 0; for ( int i = 0; i < 10; i++ ) { sum += 0. 1; //sum вычисляется как 10*0, 1. Она должна быть равна 1, 0. } //Здесь неправильное сравнение. if ( nominal == sum ) { … } Пример метода для сравнения чисел с плавающей запятой (Java): final double ACCEPTABLE_DELTA = 0. 00001; boolean Equals( double Term 1, double Term 2 ) { if ( Math. abs( Term 1 - Term 2 ) < ACCEPTABLE_DELTA ) { return true; } else { return false; } }
Лекция 6. Основные типы данных Числа с плавающей точкой n Предупреждайте ошибки округления. Способы решения проблем округления: • Измените тип переменной на тип с большей точностью. Если вы используете числа с одинарной точностью, замените их числами с двойной точностью и т. д. • Используйте двоично-десятичные переменные (binary coded decimal, BCD). • Измените тип с плавающей запятой на целые значения.
Лекция 6. Основные типы данных Символы и строки n Избегайте магических символов и строк: это литеральные строки (символы), которые разбросаны по всей программе. Следует применять именованные константы или глобальные переменные. n Узнайте, как ваш язык и система поддерживают Unicode. Преобразование между Unicode и другими наборами символов часто необходимо для взаимодейст - вия со стандартными библиотеками сторонних производителей. n Если вам необходимо поддерживать несколько языков, используйте Unicode: он обеспечивает более полную поддержку международных наборов символов, чем ISO 8859 или другие стандарты.
Лекция 6. Основные типы данных Символы и строки n Разработайте стратегию интернационализации/ локализации в ранний период жизни программы. Необходимо решить, будут ли все строки храниться во внешних ресурсах и будет ли создаваться отдельный вариант программы для каждого языка или конкретный язык будет определяться во время выполнения. n Если нужно поддерживать только один алфавит, используйте набор символов ISO 8859. n Выберите целостную стратегию преобразования строковых типов. Если вы используете несколько строковых типов, хорошим подходом будет хранение всех строк программы в одном формате и преобразо - вание их в другой формат как можно ближе к операциям ввода и вывода.
Лекция 6. Основные типы данных Логические переменные n Используйте логические перемен- ные для документирования прог - раммы. Вместо простой проверки логического выражения вы можете прис воить его значение переменной, кото - рая сделает смысл теста очевидным. Пример логического условия, чье назначение неочевидно (Java): if ( ( element. Index < 0 ) || ( MAX_ELEMENTS < element. Index ) || ( element. Index == last. Element. Index ) ) { } Пример логического условия, чье назначение понятно (Java): finished = ( ( element. Index < 0 ) || ( MAX_ELEMENTS < element. Index )); repeated. Entry = ( element. Index == last. Element. Index ); if ( finished || repeated. Entry ) { … }
Лекция 6. Основные типы данных Логические переменные n Используйте логические переменные для упроще - ния сложных условий. Пример сложного условия (Visual Basic): If ( ( document. At. End. Of. Stream() ) And ( Not input. Error ) ) And_ ( ( MIN_LINES <= line. Count ) And ( line. Count <= MAX_LINES ) ) And _ (Not Error. Processing() ) Then ' делаем что-то End If Пример упрощенного условия (Visual Basic): all. Data. Read = ( document. At. End. Of. Stream() ) And ( Not input. Error ) legal. Line. Count = ( MIN_LINES <= line. Count ) And ( line. Count <= MAX_LINES ) If ( all. Data. Read ) And ( legal. Line. Count ) And ( Not error. Processing()) {…}
Лекция 6. Основные типы данных Перечисления Перечислимым называется тип данных, который позволяет описать на естественном языке каждый элемент класса или объекта. n Используйте перечислимые типы для читабельности. Вместо выражений вида if chosen. Color = 1 лучше написать: if chosen. Color = Color_Red n Используйте перечислимые типы для надежности. При объявлении переменной типа перечисление компи - лятор позволит присвоить ей значени только из этого типа.
Лекция 6. Основные типы данных Перечисления n Проверяйте некорректные значения : При использовании перечислимого типа в условиях if или case, проверяйте появление недопустимых значений. Правильный пример проверки некорректных значений в перечис- лимом типе (Visual Basic): Select Case screen. Color Case Color_Red Case Color_Blue Case Color_Green //Здесь выполняется проверка неправильного значения. Case Else Display. Internal. Error( False, "Internal Error 752: Invalid color. " ) End Select
Лекция 6. Основные типы данных Массивы Массивы – простейшие и часто используемые типы структурирован - ных данных ; состоят из группы элементов одинакового типа, доступ к которым осуществляется напрямую по индексу. n Убедитесь, что все значения индексов массива не выходят за его границы: все проблемы с массивами так или иначе связаны с тем, что доступ к их элементам может осуществляться произвольно. n Обдумайте применение контейнеров вместо массивов: рассмотрите вопрос использования контей - нерных классов с последовательным доступом (наборов, стеков, очередей и др. ) – как альтернативу.
Лекция 6. Основные типы данных Массивы n Проверяйте конечные точки массивов: правильно ли выполняется доступ к первому и последнему элементу массива или случайно используется элемент перед ними либо после них. n В многомерном массиве убедитесь, что его индексы используются в правильном порядке: остерегайтесь ошибок вида Array [ i ][ j ], имея в виду Array[j][i].
Лекция 6. Основные типы данных Создание собственных типов данных n Создавайте типы с именами, отра - жающими их функциональность: из- бегайте имен типов, которые ссылаются на данные, лежащие в основе этих типов. Используйте имена, которые отражают те элементы реальной задачи, которые этот тип представляет. n Избегайте предопределенных типов: если есть возможность, что тип может измениться, избегайте применения предопределенных типов везде, кроме определений typedef ИЛИ type.
Лекция 6. Основные типы данных Создание собственных типов данных n Не переопределяйте предопределен- ные типы : изменение определения стандартного типа может вызвать путани- цу : если в языке есть предопределенный тип Integer , не создавайте свой тип с таким же именем. n Рассмотрите вопрос создания класса вместо использования typedef : простые операторы typedef позволяют проделать большой путь в сторону сокрытия информации об исходном типе переменной , однако иногда может потребоваться дополнительная гибкость и управляе- мость, которой позволяют добиться классы.
Лекция 6. Нестандартные типы данных Структуры Термин "структура" относится к типу данных, построенному на основе других типов. Чаще всего предпочитают создавать классы, а не структуры, чтобы использовать преимущества закрытости и функциональности, предлагаемой классами, в дополнение к открытым данным, поддерживаемым структурами.
Лекция 6. Нестандартные типы данных Правила использование структур n Используйте структуры для прояснения взаимо - отношений между данными : Структуры объединяют группы взаимосвязанных элементов. Пример неструктурированных Пример структурированных перемен- переменных (Visual Basic): name = input. Name employee. name = input. Name address = input. Address employee. address = input. Address phone = input. Phone employee. phone = input. Phone title = input. Title supervisor. title = input. Title department = input. Department supervisor. department = input. Department bonus = input. Bonus supervisor. bonus = input. Bonus n Используйте структуры для упрощения операций с блоками данных : Проще обрабатывать структуру цели - ком, чем выполнять те же действия над каждым элементом: это надежней и требует меньше строк кода.
Лекция 6. Нестандартные типы данных Правила использование структур n Используйте структуры для упрощения списка параметров : вместо того чтобы передавать параметры по одному, можно объединить взаимосвязанные элементы в структуру и передать все вместе. Пример вызова метода, не использую- Пример вызова метода, ис - щего структуру (Visual Basic): пользующего структуру (Visual Hard. Way. Routine( name, address, phone, ssn, Basic): gender, salary ) Easy. Way. Routine( employee ) n Используйте структуры для упрощения сопровож - дения : применяя их, вы группируете взаимосвязанные данные, требующие минимальных исправлений в программе.
Лекция 6. Нестандартные типы данных Указатели n Использование указателей – одна из наиболее подверженных ошибкам об - ластей программирования. Это привело к тому, что современные языки (Java, C# и Visual Basic) непредос тавляют указатель в качестве типа данных. n Правильное применение требует отличного понимания того, каr компилятор управляет распределением памяти. n Многие общие проблемы с безопасностью, особенно случаи переполнения буфера, могут быть сведены к ошибочному использованию указателей.
Лекция 6. Нестандартные типы данных Понимание указателей n Концептуально каждый указатель состоит из двух частей: области памяти и знания, как следует интерпретировать содержимое этой области. n Область памяти – это адрес, часто представлен- ный в шестнадцатеричном виде. В 32 -разрядном процессоре адрес будет 32 -битным числом, например 0 х0001 ЕА 40. n Сам по себе указатель содержит только этот адрес. n Чтобы обратиться к данным, на которые этот указатель указывает, надо пойти по этому адресу и как-то интерпретировать содержимое памяти в этой области. Сам по себе этот участок памяти – просто набор битов.
Лекция 6. Нестандартные типы данных Пример объема памяти для типов данных 0 A 61 62 63 64 65 66 67 68 69 6 A Рассматривается как: Сплошной участок памяти для последующих примеров Интерпретируется как: Интерпретация не возможна без соответствующей переменной- указатеря 0 A 61 62 63 64 65 66 67 68 69 6 A Рассматривается как: String[10] (в формате Visual Basic с длиной строки в начале) Интерпретируется как: abcdefghij
Лекция 6. Нестандартные типы данных Пример объема памяти для типов данных 0 A 61 62 63 64 65 66 67 68 69 6 A Рассматривается как: 2 -байтное целое Интерпретируется как: 24842 0 A 61 62 63 64 65 66 67 68 69 6 A Рассматривается как: 4 -байтное целое с плавающей запятой Интерпретируется как: 4. 17595656202980 E+0021
Лекция 6. Нестандартные типы данных Пример объема памяти для типов данных 0 A 61 62 63 64 65 66 67 68 69 6 A Рассматривается как: 4 -байтное целое Интерпретируется как: 16679391754 0 A 61 62 63 64 65 66 67 68 69 6 A Рассматривается как: char Интерпретируется как: Символ перевода строки (код ASCII 0 A шестнадцатиричное или 10 десятирич- ное)
Лекция 6. Нестандартные типы данных Советы по использованию указателей Ошибка в указателе – чаще всего результат того, что он указывает не туда, куда должен. “Повреждение памяти” – ошибка при присваении значения некорректной пере- менной-указателю. n Изолируйте операции с указателями в методах или классах: это уменьшает вероятность неосторожных ошибок по всей программе ; такой код становится независимым от деталей представления данных и возможно его повторное использования. n Выполняйте объявление и определение указателей одновременно.
Лекция 6. Нестандартные типы данных Советы по использованию указателей n Удаляйте указатели в той же области действия, где они были созданы : при использовании указателя в единственном блоке кода вызывайте new для выделения памяти и delete для ее освобождения в том же блоке ; при распределении памяти в конструкторе объекта – освобож - дайте в деструкторе этого объекта. n Проверяйте указатели перед их применением : перед использованием указателя в критической части програм- мы проверьте, что он указывает на осмысленную область памяти (выполняйте автоматически через методы доступа). n Проверяйте переменную, на которую ссылается ука - затель , перед ее использованием : выполняйте коррект - ную проверку значения, на которое ссылается указатель (через методы доступа).
Лекция 6. Нестандартные типы данных Советы по использованию указателей n Используйте закрепленные признаки для проверки повреждения памяти : "Поле-тэг" ( tag field ) или "закрепленный признак" ( dog tag ) – это поле, которое добавляется к структуре исключительно с целью проверки ошибок. • Когда выделяется память для переменной, поместите в поле закрепленного признака значение, которое должно остаться неизменным. • Используя структуру, проверяйте значение закреплен- ного признака: если оно не содержит ожидаемого значения, то данные были повреждены. • Удаляя указатель, измените значение этого поля : так можно выявить ошибку, если случайно попытаетесь освободить этот указатель еще раз.
Лекция 6. Нестандартные типы данных Советы по использованию указателей 1. Выделите 104 байта - на 4 байта больше, чем требуется. но 2. Укажите в первых четырех байтах значение обязательного признака, а затем верните указатель на область, следующую за этими четырьмя байтами. Установите указатель сюда Признак но 3. Когда понадобится удалить указатель, проверьте значение признака. Проверьте этот признак Признак но
Лекция 6. Нестандартные типы данных Советы по использованию указателей 4. Если значение признака корректно, присвойте ему 0 или другое значение, которое ваша программа будет считать недопустимым. Главное, чтобы его ошибочно не посчитали корректным после освобождения памяти. С той же целью заполните всю область памяти 0, 0 х. СС или любым другим неслучайным значением. 5. В заключение удалите указатель. Освободите все 104 байта
Лекция 6. Нестандартные типы данных Советы по использованию указателей n Используйте для ясности дополнительные переменные указателей : Не нужно экономить на переменных-указателях. Одну и ту же переменную нельзя вызвать для разных целей. n Добавьте явную избыточность : альтернативой полю признака является использование двух таких полей : если данные в избыточных полях не совпа - дают, то память была повреждена.
Лекция 6. Нестандартные типы данных Советы по использованию указателей Пример кода вставки в список нового узла (C++): void Insert. Link( Node *current. Node, Node *insert. Node) { // добавляем “insert. Node” после “current. Node” insert. Node->next = current. Node->next; insert. Node->previous = current. Node; if ( current. Node->next != NULL ) { //Эта строка излишне сложна. current. Node->next->previous = insert. Node; } current. Node->next = insert. Node; }
Лекция 6. Нестандартные типы данных Советы по использованию указателей Пример более читабельного кода для вставки узла (C++): void Insert. Link( Node *start. Node, Node *new. Middle. Node) { // вставляем "new. Middle. Node" между "start. Node" и "following. Node" Node *following. Node = start. Node->next; new. Middle. Node->next = following. Node; new. Middle. Node->previous = start. Node; if ( following. Node != NULL ) { following. Node->previous = new. Middle. Node; } start. Node->next = new. Middle. Node; }
Лекция 6. Нестандартные типы данных Советы по использованию указателей n Упрощайте сложные выражения с указателями Сложные выражения с использованием указателей тяжело читать. Пример: p->q->r->s. data. n Удаляйте указатели в связных списках в пра - вильном порядке: перед удалением текущего элемента убедитесь, что у вас есть указатель на следующий элемент списка. n Выделите “ запасной парашют ” памяти: резерви - руйте часть динамической памяти в начале работы программы как запасной парашют (для сохранения работы, освобождения ресурсов и аккуратного заверше- ния ). Когда памяти станет не хватать, раскройте резервный парашют – освободите эту память и завершите работу программы.
Лекция 6. Нестандартные типы данных Советы по использованию указателей n Уничтожайте мусор : избегайте ошибок с освобожденными указателями, записывая мусор в блоки памяти прямо перед их освобождением. n Устанавливайте указатели null при их удалении или освобождении : известный тип ошибок указателей – это “ висячий указатель ” ( dangling pointer ), т. е. обращение к нему после вызова функций delete или free. Записывая в указатели пустое значение после их освобождения, что запись данных по этому адресу приведет к ошибке , которую легко можно отловить.
Лекция 6. Нестандартные типы данных Советы по использованию указателей n Напишите методы-оболочки, чтобы централизо - вать стратегию борьбы с ошибками в указателях. • SAFE _ NEW: вызывает new для выделения памяти, добавляет указатель в список задействованных указателей и возвращает вновь созданный указатель вызывающей стороне. Он может также проверить, что оператор new не вернул null (ошибка нехватки памяти). • SAFE _ DELETE: проверяет, находится ли переданный ему указатель в списке действительных указателей. Если он там есть, метод записывает мусор в адресуемую им память, удаляет указатель из списка, вызывает оператор delete для освобождения памяти и устанавливает указатель в null, если нет – SAFE_DELETE выводит диагностическое сообщение и прерывает программу.
Лекция 6. Нестандартные типы данных Советы по использованию указателей Пример оболочки для кода удаления указателя (C++): "define SAFE_DELETE( pointer ) { ASSERT( pointer != NULL, "Attempting to delete null pointer. "); if ( Is. Pointer. In. List( pointer ) ) { memset( pointer, GARBAGE_DATA, Memory. Block. Size(pointer ) ); Remove. Pointer. From. List( pointer ); delete pointer; pointer = NULL; } else { ASSERT( FALSE, "Attempting to delete unallocated pointer. " ); } }
Лекция 6. Нестандартные типы данных Указатели в С++ n В C ++ и указатели (*), и ссылки (&) косвенно ссылаются на объект. n Отличия: ссылка обязана всегда ссылаться на объект, а указатель может быть равен null ; после инициализации ссылки нельзя изменить то, куда она ссылается. n По умолчанию в методах аргументы передаются по значению : С++ создает копию объекта, и при передаче объекта вызывающей программе вновь создается копия. n Для передачи параметра по ссылке необходимо применять указатели , а для передачи по значению – “константных ссылок”.
Лекция 6. Нестандартные типы данных Указатели в С++ n Используйте автоматические указатели auto _ ptr : удаляя занятую память автоматически при выходе auto _ ptr из области видимости, указатели решают множество проблем с утечками памяти, присущих обычным указателям. n Изучите интеллектуальные указатели : интеллек - туальные указатели – это замена обычных или “ тупых ” указателей. Они действуют аналогично обычным, но предоставляют дополнительные возможности по управ - лению ресурсами, операциям копирования, присваива - ния, создания и удаления объектов.
Лекция 6. Нестандартные типы данных Что такое глобальные данные? n Глобальные переменные доступны из любого места программы. n Наиболее опытные программисты пришли к вы - воду, что применять глобальные переменные рискованней, чем локальные; полезней осущест- влять доступ к данным с помощью методов. n Даже если применение глобальных переменных не всегда ведет к ошибкам, оно не представляет собой хороший способ программирования.
Лекция 6. Нестандартные типы данных Проблемы с глобальными данными n Проблемы при использовании псевдонимов для гло - бальных данных : использование псевдонима означает обращение к переменной по двум и более именам. Это происходит, когда глобальная переменная передается в метод, где используется и в качестве глобальной переменной, и в качестве параметра. n Непреднамеренные изменения глобальных данных: вы можете изменить значение глобальной переменной в одном месте и ошибочно думать, что оно осталось прежним где-то в другом. Пример метода с псевдонимами (Visual Basic): Sub Write. Global( By. Ref input. Var As Integer ) Результат: input. Var = 0 Input Variable: 5 global. Var = input. Var + 5 Global Variable: 5 Msg. Box( "Input Variable: " & Str( input. Var ) ) Msg. Box( "Global Variable: " & Str( global. Var ) ) End Sub
Лекция 6. Нестандартные типы данных Проблемы с глобальными данными n Проблемы реентерабельности гло - бальных данных: в случае использова- ния многопоточного программирования должна быть уверенность, что глобаль - ные данные сохранят свои значения даже при запуске несколько копий программы. n Затруднение повторного использования кода, выз- ванное глобальными данными : для использования кода из одной программы в другой глобальные данные усложняют его перенос. Рещение: модификация старого класса, чтобы он не использовал глобальные данные.
Лекция 6. Нестандартные типы данных Проблемы с глобальными данными n Проблемы с неопределенным порядком инициа - лизации глобальных данных : если при инициали - зации глобальной переменной из одного файла используется глобальная переменная из другого файла, значение второй переменной предсказать сложно, если нет специальных действий для их инициализации в правильном порядке. n Нарушение модульности и интеллектуальной управляемости, привносимое глобальными дан- ными : программа с глобальные данные сложна для разбиение на отдельные методы и модули.
Лекция 6. Нестандартные типы данных Причины использования глобальных переменных n Хранение глобальных значений : иногда данные концептуально относятся к целой программе: переменная, отражающая состоя- ние программы, таблица с данными, исполь - зуемая всеми методами программы. n Эмуляция именованных констант : можно использо - вать глобальные переменные как подстановки для именованных констант, если ваш язык их не поддер - живает. Так, вы можете заменить константные значения 1 и 0 глобальными переменными TRUE и FALSE , установ - ленными в 1 и 0. n Эмуляция перечислимых типов в таких языках, как Python, которые напрямую такие типы не поддерживают.
Лекция 6. Нестандартные типы данных Причины использования глобальных переменных n Оптимизация обращений к часто используемым данным : Иногда перемен - ная так часто вызывается, что упоминается в списке параметров каждого метода. Чтобы не включать переменную в каждый список параметров методов, следует сделать ее глобальной. n Исключение бродячих данных : данные , передаю - щиеся методу или классу только для того, чтобы передать в другой метод или класс , называются бродячими. Пример для исключения таких данных : объект-обработчик ошибок, применяемый в каждом методе.
Лекция 6. Нестандартные типы данных Используйте глобальные данные только как последнее средство! n Начните с объявления всех переменных локальны- ми и делайте их глобальными только по необходи- мости : если выяснится, что они нужны еще в других частях, сделайте их сначала закрытыми или защищен- ными переменными класса , в крайнем случае – делайте их глобальными. n Различайте глобальные переменные и переменные- члены класса : некоторые переменные действительно глобальны потому, что к ним обращаются из любого места программы ; классовые переменные интенсивно используются только некоторым набором методов. n Используйте методы доступа : это основной подход для решения проблем с глобальными данными.
Лекция 6. Нестандартные типы данных Преимущества методов доступа n Централизованный контроль над данными. n Все ссылки на переменную изолированы. n Преимущества сокрытия информации : можно изменять содержимое метода доступа, не затрагивая остальную часть программы. n Методы доступа легко преобразуются в абстрактные типы данных.
Лекция 6. Нестандартные типы данных Как использовать методы доступа? n Требуйте, чтобы весь код обращался к данным через методы доступа : хорошим соглашением является именование глобальных переменных с префиксом g_; никакой код не должен обращаться к переменным с префиксом g _ напрямую, кроме методов доступа к этим переменным. n Не валите все глобальные данные в одну кучу: в противном случае утра- чиваются преимущества абстрактных типов данных и сокрытия информации.
Лекция 6. Нестандартные типы данных Как использовать методы доступа? n Управляйте доступом к глобаль ным переменным с помощью блокировок: • блокировка требует, чтобы перед вызовом или обновлением значения глобальной переменной ее помечали для изменений (check out); • после использования переменную нужно освобо - дить (check in). • До тех пор, пока она занята и другая часть програм- мы попытается к ней обратиться , процедура блокировки должна генериривать исключение.
Лекция 6. Нестандартные типы данных Как использовать методы доступа? n Встройте уровень абстракции в методы доступа : разрабатывайте методы доступа в области определения задачи, а не на уровне деталей реализации. n Выполняйте доступ к данным на одном и том же уровне абстракции : Если вы используете метод доступа для выполнения какого-то действия со структурой, все остальные действия должны производиться с помощью таких методов.
Лекция 6. Нестандартные типы данных Уменьшение рисков использования глобальных данных n Разработайте соглашения по именованию, кото - рые сделают глобальные переменные очевид - ными. Если вы используете глобальные переменные для нескольких целей (например, как переменные и как замену именованных констант), убедитесь, что ваши соглашения по именованию делают различия между этими типами использования. n Создайте хорошо аннотированный список всех глобальных переменных: если соглашение по имено- ванию указывает, что данная переменная является глобальной, будет полезно показать, что эта переменная делает.
6 переменные исправл.ppt
- Количество слайдов: 100

