Анимация
JavaScript
|
Главная Библионтека ... Кроме того, стандарт пересмотрел подход к жизни переменных, объявленн1х в операторе for. - Ред. далее следует 2 00 строк кода, в которых переменная x не используется while(x>0) снова следует 200 строк кода f( x-- ); не очень хорош, даже если вы сэкономили немного времени, соединив операцию декрементирования -- с вызовом функции. Переместите инициализацию и x-- в оператор for. Так как объявление в Си++ может располагаться везде, где можно поместить оператор, то вы даже можете объявить x непосредственно перед for: int x = 10; for (;x>0;--x) следует 200 строк кода f(x); (И хотя вы можете сказать, что в Си++ есть возможность сделать for ( int=0;..., такая практика вводит в заблуждение, потому что на самом деле область видимости у x внешняя, как если бы ее объявление было сделано в строке, предшествующей оператору for. Я не рекомендую этого) . Если три оператора внутри for слишком длинны, чтобы поместиться в одной строке, то вы можете их отформатировать их следующим образом: for( некое длинное имя переменной = f(); некое длинное имя переменной ; некое длинное имя переменной = f() ) ... но часто лучше выделить одно из предложений вот так: int некое длинное имя переменной = f(); for(; некое длинное имя переменной; некое длинное имя переменной = f() ) или в чрезвычайном случае int некое чрезвычайно длинное имя переменной = f(); for(; ; некое чрезвычайно длинное имя переменной = f() ) if(!некое чрезвычайно длинное имя переменной ) break; ... Главное - это сосредоточить инициализацию, проверку и инкрементирование в одном месте. Я никогда не сделаю так: int некое чрезвычайно длинное имя переменной = f(); while( некое чрезвычайно длинное имя переменной ) много строк кода некое чрезвычайно длинное имя переменной = f(); потому что это нарушает контроль над циклом. 64. То, чего нет в условном выражении, не должно появляться и в других частях оператора for Так как оператор for предназначен для того, чтобы собрать инициализирующую, условную и инкрементирующие части цикла в одном месте, так чтобы вы могли, взглянув, понять, что происходит, то вы не должны загромождать оператор for материалом, который не имеет отношения к циклу, иначе вы лишите смысла всю эту конструкцию. Избегайте подобного кода: int *ptr; ... for( ptr = array, i = array size; --i >= 0; f(ptr++) ) который лучше сформулировать так: int *ptr = array; for ( i = array size; -- i >=0;) f( ptr++ ); 65. Допускайте, что ситуация может измениться в худшую сторону Одним из лучших примеров этой проблемы связан со "знаковым расширением". Большинство компьютеров используют так называемую арифметику "двоичного дополнения". Крайний левый бит отрицательного числа в этой арифметике всегда содержит 1. Например, восьмибитовый тип char со знаком, содержащий число -10, будет представлен в машине с двоичным дополнением как 11110110 (или 0xf6). То же самое число в 16-битовом типе int представлено как 0xfff6. Если вы преобразуете 8-битовый char в int явно посредством оператора приведения типов или неявно, просто используя его в арифметическом выражении, где второй операнд имеет тип int, то компилятор преобразует char в int, добавляя второй байт и дублируя знаковый бит (крайний слева) char в каждом бите добавленного байта. Это и есть знаковое расширение. Существуют два вида операций сдвига вправо на уровне языка ассемблера: "арифметический сдвиг" дает вам знаковое расширение (то значение, что было в крайнем левом бите до сдвига, будет в нем и после сдвига); "логический сдвиг" заполняет левый бит нулем. Данное правило, независимо от того, арифметический или логический сдвиг у вас, когда вы используете оператор сдвига Си/Си++, звучит очень просто: если вам требуется знаковое расширение, то допустите, что у вас получится заполнение нулями. А если вам нужно заполнение нулями, то представьте, что у вас получилось знаковое расширение. Другим хорошим примером являются возвращаемые коды ошибок. Удивительное количество программистов не заботится о проверке того, не вернула ли функция malloc() значение NULL при недостатке свободной памяти. Быть может, они полагают, что имеется бесконечный объем виртуальной памяти, но известно, что ошибка может с легкостью вызвать резервирование всей имеющейся памяти, и вы никогда не обнаружите этого, если не будете проверять возвращаемые коды ошибок. Если функция может индицировать состояние ошибки, то вы должны допустить, что ошибка произойдет, по меньшей мере, однажды за время жизни программы. 66. Компьютеры не знают математики Компьютеры - это арифметические инструменты, славные счетные машины. Они не знают математики. Поэтому даже такие простые выражения, как следующее, могут добавить вам хлопот: int x = 32767; x=(x*2)/ 2; (На 16-разрядной машине x получится равным -1.32767 - это 0x7fff. Умножение на 2 - на самом деле сдвиг влево на один бит, дает в результате 0xfffe - отрицательное число. Деление на два является арифметическим сдвигом вправо с гарантированным знаковым расширением, и так вы получаете теперь 0xffff или -1). Поэтому важно каждый раз при выполнении арифметических вычислений учитывать 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [ 25 ] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |