Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 880
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
ГЛАВА 11 Сила имен переменных
281
Ключевые моменты
쐽
Выбор хороших имен переменных — одно из главных условий понятности программы. С отдельными типами переменных — например, с индексами цик- лов и переменными статуса — связаны свои принципы именования.
쐽
Имена должны быть максимально конкретны. Имена, которые из-за невыра- зительности или обобщенности можно использовать более чем с одной целью,
обычно являются плохими.
쐽
Конвенции именования позволяют провести различие между локальными дан- ными, данными класса и глобальными данными, а также между именами ти- пов, именованных констант, перечислений и переменных.
쐽
Над каким бы проектом вы ни работали, вам следует принять конвенцию име- нования переменных. При выборе типа конвенции следует учитывать размер программы и число работающих над ней программистов.
쐽
Современные языки программирования позволяют отказаться от сокращения имен. Если вы все-таки сокращаете имена, регистрируйте аббревиатуры в сло- варе проекта или используйте стандартизированные префиксы.
쐽
За чтением кода программисты проводят гораздо больше времени, чем за его написанием. Выбирайте имена так, чтобы они облегчали чтение кода, пусть даже за счет удобства его написания.
282
ЧАСТЬ III Переменные
Г Л А В А 1 2
Основные типы данных
Содержание
쐽
12.1. Числа в общем
쐽
12.2. Целые числа
쐽
12.3. Числа с плавающей запятой
쐽
12.4. Символы и строки
쐽
12.5. Логические переменные
쐽
12.6. Перечислимые типы
쐽
12.7. Именованные константы
쐽
12.8. Массивы
쐽
12.9. Создание собственных типов данных (псевдонимы)
Связанные темы
쐽
Присвоение имен данным: глава 11
쐽
Нестандартные типы данных: глава 13
쐽
Общие вопросы использования переменных: глава 10
쐽
Описание форматов данных: подраздел «Размещение объявлений данных»
раздела 31.5
쐽
Документирование переменных: подраздел «Комментирование объявлений дан- ных» раздела 32.5
쐽
Создание классов: глава 6
Основные типы данных являются базовыми блоками для построения остальных типов данных. Эта глава содержит советы по применению чисел вообще, целых чисел, чисел с плавающей запятой, символов и строк, логических переменных,
перечислимых типов, именованных констант и массивов. В заключительном раз- деле этой главы рассказано о создании собственных типов данных.
Эта глава содержит рекомендации по предупреждению главных ошибок в приме- нении основных типов данных. Если вы уже ознакомились с основами, перехо- дите к концу главы, к обзору перечня допускаемых ошибок, а также к обсужде- нию нестандартных типов данных в главе 13.
http://cc2e.com/1278
ГЛАВА 12 Основные типы данных
283
12.1. Числа в общем
Далее дано несколько рекомендаций, позволяющих сократить число ошибок при использовании чисел.
Избегайте «магических чисел» Магические числа — это обычные числа, такие как
100 или 47524, которые появля- ются в программе без объяснений. Если вы программируе- те на языке, поддерживающем именованные константы, ис- пользуйте их вместо магических чисел. Если вы не можете применить именованные константы, применяйте глобальные переменные, когда это возможно.
Исключение магических чисел дает три преимущества.
쐽
Изменения можно сделать более надежно. Если вы используете именованные константы, нет нужды искать каждое из чисел
100, и вы не измените по ошиб- ке те из этих чисел, что ссылаются на что-либо иное.
쐽
Изменения сделать проще. Когда максимальное число элементов меняется со
100 на 200, используя магические числа, вы должны найти каждое число 100
и изменить его на
200. Если вы используете 100+1 или 100-1, вы также долж- ны найти и изменить все числа
101 и 99 на 201 и 199 соответственно. При использовании именованных констант вы просто меняете определение кон- станты со
100 на 200 в одном месте.
쐽
Ваша программа лучше читается. Конечно, в выражении:
for i = 0 to 99 do ...
можно предположить, что
99 определяет максимальное число элементов. А вот выражение:
for i = 0 to MAX_ENTRIES-1 do ...
не оставляет на этот счет сомнений. Даже если вы уверены, что это число ни- когда не изменится, применяя именованные константы, вы получите более читабельную программу.
Применяйте жестко заданные нули и единицы по необходимости Зна- чения
0 и 1 используются для инкремента, декремента, а также в начале циклов при нумерации первого элемента массива.
0 в конструкции:
for i = 0 to CONSTANT do ...
вполне приемлем, так же как
1 в выражении:
total = total + 1
Вот хорошее правило: используйте в программе как константы только
0 и 1, а любые другие числа определите как литералы с понятными именами.
Ошибки деления на ноль Каждый раз, когда вы пользуетесь символом деления
(
/ в большинстве языков), думайте о том, может ли в знаменателе оказаться 0. Если такая возможность существует, напишите код, предупреждающий появление ошиб- ки деления на 0.
Перекрестная ссылка О приме- нении именованных констант вместо магических чисел см.
раздел 12.7.
284
ЧАСТЬ III Переменные
Выполняйте преобразования типов понятно Убедитесь, что кто-нибудь, чи- тая вашу программу, поймет преобразования между разными типами данных,
которые в ней встречаются. На языке C++ вы могли бы написать:
y = x + (float) i а на Microsoft Visual Basic:
y = x + CSng( i )
Эта практика поможет обеспечить однозначность ваших преобразований — раз- ные компиляторы по-разному конвертируют, а при таком подходе вы гарантиро- ванно получите то, что ожидали.
Избегайте сравнений разных типов Если x — число с плавающей запятой, а
i — целое, проверка:
if ( i = x ) then ...
почти гарантированно не сработает. К тому времени, когда компилятор опреде- лит каждый тип, который он хочет задействовать для сравнения, преобразует один из типов в другой, произведет ряд округлений и вычислит ответ, вы будете рады,
если ваша программа вообще работает. Сделайте преобразования вручную, так чтобы компилятор мог сравнить два числа одного и того же типа, и точно знай- те, что нужно сравнивать.
Обращайте внимание на предупреждения вашего компилято-
ра Многие современные компиляторы сообщают о наличии разных типов чисел в одном выражении. Обращайте на это внимание! Каждый программист рано или поздно просит кого-нибудь помочь выследить надоедли- вую ошибку, а выясняется, что о ней все время предупреждал компилятор. Про- фессионалы высокого класса пишут свои программы так, чтобы исключить все предупреждения компилятора. Легче предоставить работу компилятору, чем вы- полнять ее самому.
12.2. Целые числа
Учитывайте следующие рекомендации при применении целых чисел.
Проверяйте целочисленность операций деления Когда используются целые числа, выражение 7/10 не равно 0,7. Оно обычно равно 0 или минус бесконечно- сти, или ближайшему целому, или… ну, вы понимаете. Результат зависит от выб- ранного языка. Это же относится и к промежуточным результатам. В реальном мире
10 * (7/10) = (10*7) / 10 = 7. Но не в мире целочисленной арифметики. 10 * (7/10)
равно 0, потому что целочисленное деление (7/10) равно 0. Простейший способ исправить положение — преобразовать его так, чтобы операции деления выпол- нялись последними: (10*7) / 10.
Проверяйте переполнение целых чисел При выполнении умножения или сло- жения необходимо принимать во внимание наибольшие возможные значения це- лых чисел. Для целого числа без знака это обычно 2 32
–1, а иногда и 2 16
–1, или 65 535.
Проблема возникает, когда вы умножаете два числа, в результате чего получается
Перекрестная ссылка Вариант этого примера см. в разделе 12.3.
1 ... 32 33 34 35 36 37 38 39 ... 104
ГЛАВА 12 Основные типы данных
285
число большее, чем максимально возможное целое. Скажем, если вы умножаете
250 * 300, правильным ответом будет 75 000. Но если максимальное целое — 65 535,
то, возможно, из-за переполнения вы получите 9464 (75 000 – 65 536 = 9464). Вот интервалы значений для часто встречающихся целых типов (табл. 12-1):
Табл. 12-1. Интервалы значений некоторых целых типов
Целый тип
Интервал
8-битный со знаком
От –128 до 127 8-битный без знака
От 0 до 255 16-битный со знаком
От –32 768 до 32 767 16-битный без знака
От 0 до 65 535 32-битный со знаком
От –2 147 483,648 до 2 147 483 647 32-битный без знака
От 0 до 4 294 967 295 64-битный со знаком
От –9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 64-битный без знака
От 0 до 18 446 744 073 709 551 615
Простейший способ предотвращения целочисленного переполнения — просмотр каждого члена арифметического выражения с целью представить наибольшее возможное значение, которое он может принимать. Так, если в целочисленном выражении
m = j * k, наибольшим значением для j будет 200, а для k — 25, то мак- симальным значением для
m будет 200 * 25 = 5 000. Для 32-разрядных машин это вполне допустимо, так как максимальным целым является 2 147 483 647. С другой стороны, если максимально возможное значение для
j — это 200 000, а для k —
100 000, то значение
m может достигать 200 000 * 100 000 = 20 000 000 000. Это уже неприемлемо, так как 20 000 000 000 больше, чем 2 147 483 647. В этом слу- чае для размещения наибольшего возможного значения
m вам придется исполь- зовать 64-битные целые или числа с плавающей запятой.
Кроме того, учитывайте будущее развитие программы. Если
m никогда не будет больше 5 000 — отлично. Но если ожидается, что
m будет постоянно расти на протяжении нескольких лет, примите это во внимание.
Проверяйте на переполнение промежуточные результаты Число, полу- чаемое в конце вычислений, — не единственное, о котором следует беспокоить- ся. Представьте, что у вас есть такой код:
Пример переполнения промежуточных результатов (Java)
int termA = 1000000;
int termB = 1000000;
int product = termA * termB / 1000000;
System.out.println( “( “ + termA + “ * “ + termB + “ ) / 1000000 = “ + product );
Вы можете подумать, что значение
Product вычисляется как (100 000*100 000) /
100 000 и поэтому равно 100 000. Но программе приходится вычислять проме- жуточное значение
100 000*100 000 до того, как будет выполнено деление на
100 000, а это значит, что нужно хранить такое большое число, как
1 000 000 000 000
. Угадайте, что получится? Вот результат:
( 1000000 * 1000000 ) / 1000000 = -727
286
ЧАСТЬ III Переменные
Если значение целых чисел в вашей системе не превышает 2 147 483 647, проме- жуточный результат слишком велик для целого типа данных. В такой ситуации промежуточный результат, который должен быть равен
1 000 000 000 000, на са- мом деле равен
727 379 968, поэтому, когда вы делите его на 100 000, вы получа- ете -
727 вместо 100 000.
Вы можете решить проблему переполнения промежуточных результатов так же,
как и в случае целочисленного переполнения: изменив тип на длинное целое или число с плавающей запятой.
12.3. Числа с плавающей запятой
Главная особенность применения чисел с плавающей запятой в том, что многие дробные десятичные числа не могут быть точно представлены с помощью нулей и единиц, используемых в цифровом компьютере.
В бесконечных десятичных дробях, таких как 1/3 или 1/7, обычно сохраняется только 7 или 15 цифр после запятой. В моей версии Microsoft Visual Basic 32-бит- ное представление дроби 1/3 в виде числа с плавающей запятой равно 0,33333330.
То есть точность ограничена 7 цифрами. Такая точность достаточна для большин- ства случаев, но все же способна иногда вводить в заблуждение.
Вот несколько рекомендаций по использованию чисел с плавающей запятой.
Избегайте сложения и вычитания слишком разных по
размеру чисел Для 32-битной переменной с плавающей запятой сумма 1 000 000,00 + 0,1, вероятно, будет равна
1 000 000,00, так как в 32 битах недостаточно значимых цифр, чтобы охватить интервал между 1 000 000 и 0,1. Ана- логично 5 000 000,02 – 5 000 000,01, вероятно, равно 0,0.
Решение? Если вам нужно складывать настолько разные по величине числа, сна- чала отсортируйте их, а затем складывайте, начиная с самых маленьких значений.
Аналогично, если вам надо сложить бесконечный ряд значений, начните с наи- меньшего члена, т. е. суммируйте члены в обратном порядке. Это не решит про- блемы округления, но минимизирует их. Многие алгоритмические книги предла- гают решения для таких случаев.
Избегайте сравнений на равенство Числа с плавающей запятой, которые должны быть равны, на самом деле рав- ны не всегда. Главная проблема в том, что два разных спо- соба получить одно и то же число не всегда приводят к одинаковому результату. Так, если 10 раз сложить 0,1, то 1,0 получается только в редких случаях. Следующий пример содержит две переменных (
nominal и sum),
которые должны быть равны, но это не так.
Пример неправильного сравнения чисел с плавающей точкой (Java)
Переменная nominal — 64-битное вещественное число.
double nominal = 1.0;
double sum = 0.0;
Перекрестная ссылка Книги,
содержащие алгоритмы реше- ния этих проблем, см. в подраз- деле «Дополнительные ресур- сы» раздела 10.1.
1 равен 2 для достаточно боль- ших значений 1.
Аноним
>