CHAP5 (1018805), страница 3

Файл №1018805 CHAP5 (Сборник литературы - С и С++) 3 страницаCHAP5 (1018805) страница 32017-07-08СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 3)

{

case CIRCLE: print_circle( p );

case LINE: print_line ( p );

case TEXT: print_text ( p );

}

}

на следующий:

typedef struct

{

void (*print)( struct *shape );

union shape_data;

{ // здесь данные для различных фигур.

}

}

shape;

extern void print_circle( shape *p );

extern void print_line ( shape *p );

extern void print_text ( shape *p );

shape a_circle = { print_circle, ... };

print_shape( shape *p )

{

( p->type )( p );

}

Главные преимущества такого подхода заключаются в следующем:

  • Вам больше не нужен перечислитель shape_type.

  • Функцию print_shape() теперь написать гораздо проще.

  • print_shape() будет продолжать работать без модификации, когда вы добавите новые фигуры в эту систему.

60. Избегайте циклов do/while.

Цикл do/while опасен в принципе, так как вы обязательно выполняете его тело хотя бы один раз. Следовательно, вы должны проверить условия завершения до входа в этот цикл. Я часто вижу код, похожий на следующий:

if ( !проверить_нечто )

return ERROR;

do

{

начинка();

} while ( проверить_нечто );

Вам гораздо лучше сделать так:

while ( проверить_нечто )

начинка();

Похожий случай:

if ( некое_условие() )

do

// масса материала

while ( некое_условие() && другой_материал() );

легче трактовать следующим образом:

while ( некое_условие() )

{

// масса материала

if ( !другой_материал() )

break;

}

Я профессионально занимаюсь программированием с 1979 года и за это время использовал цикл do/while всего два раза.

60.1. Никогда не используйте do/while для бесконечного цикла.

Код, похожий на следующий:

do

{

// здесь следует несколько страниц кода

while ( 1 );

просто заставляет сопровождающего программиста шарить по листингу взглядом, ища while, вместо того, чтобы найти его сразу в случае расположения оператор while (1) в начале цикла.

61. В цикле со счетчиком его значение должно по возможности уменьшаться.

Циклы являются одним из тех мест, где малое повышение эффективности значительно улучшает выполнение программы, потому что их код выполняется многократно. Так как сравнение с нулем обычно более эффективно, чем сравнение с определенным числом, то цикл с уменьшающимся счетчиком, как правило, выполняется быстрее. Используйте

for ( i = max; --i >= 0; )

;

вместо:

for ( i = 0; i < max; ++i )

;

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

62. Не делайте одно и то же двумя способами одновременно.

В качестве контрапункта к предыдущему правилу рассмотрим следующий фрагмент (содержащий в себе ошибку):

int array[ARRAY_SIZE];

int *p = array;

for ( i = 1; i < ARRAY_SIZE ; ++i )

*p++ = 0;

Проблема состоит в том, что счетчик не совпадает по фазе с указателем (i имеет значение 1, когда указатель указывает на элемент array[0]), и последний элемент массива не будет инициализирован.

Я обычно предпочитаю для простых перемещений по массивам указатели (вместо индексов массива), потому что указатели, как правило, более эффективны, устраняя неявную операцию умножения в выражении a[i], интерпретируемом как:

( a + ( i* sizeof(a[0])))

Я бы переписал это код таким образом:

int array[ARRAY_SIZE];

int *current = array;

int *const end = array + (SIZE-1);

while ( current <= end )

*current++ = 0;

Так же надежно (хотя и менее эффективно) сделать следующее:

int array[ARRAY_SIZE];

int i;

for ( i = 0; i < ARRAY_SIZE ; ++i )

array[i] = 0;

Кстати, если вы используете указатели, то вам придется извлекать индекс при помощи арифметики указателей, а не за счет сохранения второй переменной. У вас могут возникнуть проблемы, если вы передадите i функции в предыдущем примере с ошибкой. Воспользуйтесь подобным кодом:

for ( current = array; current <= end; ++current )

{

// ...

f( current - array ); // передать функции f() текущий индекс массива

}

С другой стороны, обычно нужно избегать кода, подобного следующему, так как такой оператор цикла чрезвычайно неэффективен:

while ( (current - array) < ARRAY_SIZE )

// ...

63. Используйте оператор for, если имеются любые два из инициализурующего, условного или инкрементирующего выражений.

Иначе используйте while. Такой код:

int x = 10;

// далее следует 200 строк кода, в которых переменная 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). Поэтому важно каждый раз при выполнении арифметических вычислений учитывать ограничения компьютерной системы. Если вы производите умножение перед делением, то при этом рискуете выйти за пределы разрядности при сохранении результата; если вы сначала делите, то рискуете случайно округлить результат до нуля; и так далее. Численным методам для компьютеров посвящены целые книги, и вам нужно прочитать хотя бы одну из них, если в ваших программах много математики.

Вы также должны знать мелкие грехи своего языка программирования. В С, например, преобразование типов выполняется по принципу "оператор за оператором". Я однажды потратил утро, пытаясь разобраться, почему следующий код ничего не делает:

long x;

x &= 0xffff; // очистить все, кроме младших 16-ти бит 32-битного типа long.

Компьютер имел 16-битовый тип int и 32-битовый тип long. Константа 0xffff типа int с арифметическим значением -1. Компилятор С при трансляции &= обнаруживал разные типы операндов и поэтому преобразовывал int в long. -1 в типе long представляется как 0xffffffff, поэтому логическая операция И не имела эффекта. Это как раз тот способ, которым и должен работать данный язык программирования. Я просто об этом не подумал.

Заметьте, что вы не можете исправить эту ситуацию приведением типов. Все, что делает следующий код, это заменяет неявное преобразование типа явным. Но тем не менее происходит то же самое:

x &= (long)0xffff;

Единственным методом решения этой проблемы является:

x &= 0xffffUL;

или равноценный ему.

66.1. Рассчитывайте на невозможное.

Оператор switch всегда должен иметь предложение с ключевым словом default для ситуации по умолчанию, особенно если эта ситуация не должна возникать:

f( int i ) // переменная i должна иметь значение 1 или 2.

Характеристики

Тип файла
Документ
Размер
87,5 Kb
Тип материала
Высшее учебное заведение

Список файлов книги

С и С++ - сборник литературы
C++ Бархатный путь - Марченко А
cpp_001.shtml
cpp_002.shtml
cpp_003.shtml
cpp_004.shtml
cpp_005.shtml
cpp_006.shtml
cpp_007.shtml
cpp_008.shtml
cpp_009.shtml
cpp_010.shtml
cpp_011.shtml
cpp_012.shtml
cpp_013.shtml
cpp_014.shtml
cpp_015.shtml
cpp_016.shtml
cpp_017.shtml
cpp_018.shtml
cpp_019.shtml
cpp_020.shtml
cpp_021.shtml
cpp_022.shtml
cpp_023.shtml
cpp_024.shtml
cpp_025.shtml
cpp_026.shtml
cpp_027.shtml
cpp_030.shtml
cpp_034.shtml
Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
7027
Авторов
на СтудИзбе
260
Средний доход
с одного платного файла
Обучение Подробнее