Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Основы программирования. Учебное пособие-1

.pdf
Скачиваний:
0
Добавлен:
15.08.2026
Размер:
951 Кб
Скачать

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

Длина функции не должна превышать одной страницы. Еще лучше, чтобы функция не превышала по длине половины страницы. Небольшие функции способствуют повторному использованию программного кода.

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

имодификацию.

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

Прототип функции, заголовок функции и вызов функции должны иметь взаимное соответствие по числу, типу и порядку следования аргументов и параметров, а также по типу возвращаемого значения.

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

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

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

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

51

для выбора рекурсивного решения является то, что итеративное решение может не быть очевидным.

Разбиение программы на функции аккуратным, иерархическим образом способствует систематизации конструирования программного обеспечения. Но за все приходится платить.

Массивы

Распространенные ошибки

Важно обратить внимание на различие между «седьмым элементом массива» и «элементом массива номер семь». Поскольку индексы массива начинаются с 0, «седьмой элемент массива» имеет индекс 6, в то время как «элемент массива номер семь» имеет индекс 7 и фактически является восьмым элементом массива. Это источник так называе-

мых ошибок смещения индекса.

Отсутствие инициализации массива, элементы которого должны быть инициализированы.

Указание в списке инициализации бо´льшего числа значений, чем количество элементов в массиве, является синтаксической ошибкой.

Завершение директив препроцессора #define или #include точкой с запятой. Не забывайте, что директивы препроцессора не являются операторами языка C,

Присвоение значения символической константе в выполняемом оператореявляетсясинтаксическойошибкой.Символическаяконстанта не является переменной. Компилятор не резервирует для нее места в памяти, как в случае переменных, содержащих значения во время выполнения программы.

Ссылка на элемент за пределами массива.

Передача функции scanf символьного массива, недостаточно большого для хранения вводимой с клавиатуры строки, может привести

кпотере данных в программе и другим ошибкам времени выполнения.

Предположение, что элементы локального массива, объявленного

как static, инициализируются нулями всякий раз при вызове функции, где этот массив объявлен.

Неправильная ссылка на элемент двумерного массива а[х][у] в виде а[х, у].

52

Хороший стиль

Используйте для имен символических констант только символы верхнего регистра. Это выделяет эти константы в тексте программы и напоминает программисту, что символические константы не являются переменными.

Стремитеськясностипрограммы.Иногдаимеетсмыслотказаться от наиболее эффективного использования памяти или процессорного времени в пользу написания более понятного кода.

При выполнении цикла над элементами массива его индекс никогда не должен быть меньше 0 и должен быть строго меньше общего количества элементов, т. е. не больше, чем (размер массива – 1). Убедитесь, что условие завершения цикла не допускает обращения к элементам вне этого диапазона.

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

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

Некоторые программисты включают имена переменных в прототипы функций для того, чтобы сделать программы более понятными. Компилятор игнорирует эти имена.

Повышение эффективности

Иногда соображения эффективности намного перевешивают соображения понятности.

Последствия (обычно серьезные) ссылок на элементы за пределами массива являются системно зависимыми.

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

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

Часто наиболее простые алгоритмы работают плохо. Их достоинством является простота написания, тестирования и отладки. Однако

53

для достижения максимальной эффективности часто требуются более сложные алгоритмы.

Методические замечания

Определение размера всех массивов с помощью символических констант повышает общность программ (делает их масштабируемыми).

К параметру-массиву в определении функции может применяться модификатор const, чтобы не допустить изменения в теле функции ис-

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

Указатели

Распространенные ошибки

Знак операции косвенной адресации * не распространяется на все переменные в строке объявления. Символ * должен предшествовать имени каждого указателя.

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

Не разыменовывайте указатель, кроме случая, когда вам нужно получить значение, на которое он ссылается.

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

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

Вычитание или сравнение двух указателей, ссылающихся не на один и тот же массив.

Выход за начало или конец массива при арифметических операциях с указателем.

54

Присвоение значения указателя одного типа указателю другого типа, когда ни один из них не является указателем типа void *, приводит к синтаксической ошибке.

Разыменование void-указателя.

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

Хороший стиль

Включайте в имена переменных-указателей префикс (или суффикс) ptr; такие имена подскажут вам, что эти переменные являются указателями и должны обрабатываться соответствующим образом.

Чтобы избежать неожиданных результатов, всегда инициализируйте указатели.

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

Прежде чем использовать функцию, посмотрите на ее прототип, чтобы узнать, может ли функция изменять переданные ей значения.

Ссылайтесь на элементы массива при помощи индексов. Пусть ваша программа будет компилироваться немного дольше, но зато текст программы будет более понятен.

Повышение эффективности

При передаче больших объектов данных, например структур, используйте указатель на константные данные. Этот подход позволит вам совместить производительность передачи параметра по ссылке и защиту данных как при передаче параметра по значению.

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

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

Переносимость программ

Хотя модификатор const определен в стандарте ANSI C, некоторые системы его не поддерживают.

55

Размервбайтаходногоитогожетипа данныхможетбытьразным

вразличных системах. При написании программ, которые будут работать на различных платформах и при этом небезразличны к представле-

нию данных, используйте операцию sizeof для определения размера типа данных.

Большинство компьютеров сегодня поддерживают 2-байтовые и 4-байтовые целые. Некоторые из последних машин имеют еще и 8-бай- товые целые. Поскольку результат выполнения арифметических операций над указателем зависит от размера объектов, на которые ссылается указатель, то результат арифметических выражений с указателями является машинно зависимым.

Методические замечания

Употребление модификатора const можно рассматривать в рам-

ках соблюдения принципа минимальных привилегий. Следование этому принципу позволяет существенно сократить время отладки программы

иколичество побочных нежелательных эффектов, а также делает программу проще для понимания и сопровождения.

Если передаваемое функции значение не изменяется (или не должно быть изменено) в теле функции, оно должно объявляться с мо-

дификатором const, чтобы гарантировать невозможность даже случайного ее изменения.

В результате вызова по значению в вызывающей функции может быть изменена только одна величина. Этой величине должно быть присвоено возвращаемое вызванной функцией значение. Для изменения сразу нескольких значений в вызывающей функции необходимо использовать вызов по ссылке.

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

мума привилегий.

При передаче в функцию массива следует передавать и его размер. Это сделает функцию более универсальной. Универсальные функции могут использоваться большим количеством программ.

Глобальные переменные нарушают принцип минимальных привилегий и являются примером плохого стиля программирования.

56

Символы и строки

Распространенные ошибки

При определении размера массива символов не выделено место, чтобы запомнить символ '\0', который ограничивает строку.

Попытка напечатать «строку», которая не содержит ограничивающего символа '\0'.

Обработка одиночного символа в качестве строки. Строка – это указатель, который, возможно, представляется данными типа long int. Символ является небольшим целым числом (значения кодовой таблицы ASCII находятся в диапазоне 0 255). На многих системах такая замена вызовет ошибку, поскольку младшие адреса памяти резервируются для специального использования (например, для адресации программ обработкипрерыванийоперационнойсистемы),такчтопроизойдет«несанкционированный доступ».

Передача символа в качестве аргумента функции, которая опери-

рует со строками.

Передача строки в качестве аргумента функции, которая оперирует с символами.

Отсутствие ограничивающего символа '\0' в первом аргументе

функцииstrncpy,когдавеличинатретьегоаргументаменьшеилиравна длине строки во втором аргументе.

Предположение, что strcmp и strncmp возвращают 1 в случае равенства строк. На самом деле функции strcmp и strncmp возвращают в этом случае 0 (значение false в C), и, следовательно, при проверке двух строк на равенство результат должен сравниваться с 0.

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

Хороший стиль

Когда вы объявляете массив символов для хранения строки, убедитесь, что размер его достаточен для хранения самой большой строки, с которой предполагается работать. Язык C позволяет запоминать строку любой длины. Если строка оказалась длиннее массива, в котором она должна сохраняться, то символы, выходящие за размер массива, перепишут данные в области памяти, следующей сразу за массивом.

Если вы вызываете функции библиотеки обработки символов, подключите заголовочный файл <ctype.h>.

57

Если вы вызываете функции из библиотеки утилит общего назначения, подключите заголовочный файл <stlib.h>.

Если вы вызываете функции из стандартной библиотеки ввода/ вывода, подключите заголовочный файл <stdio.h>.

Если вы вызываете функции из библиотеки обработки строк, подключите заголовочный файл <string.h>.

Переносимость программ

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

Внутренние числовые коды, использующиеся для представления символов, могут отличаться на различных компьютерах.

Тип size_t является системно зависимым синонимом либо для типа unsigned long, либо для типа unsigned int.

Содержание сообщения, генерируемого strerror, является системно зависимым.

Форматированный ввод/вывод

Распространенные ошибки

Строка управления форматом не заключена в кавычки.

Печать отрицательного значения со спецификатором, который предполагает наличие значения без знака.

Использование для вывода первого символа строки. Специфи- кацияпреобразованияпредполагаетаргументтипаchar.Строка–это указатель на char, т. е. char *.

Использование %s для вывода аргумента типа char. Спецификацияпреобразования%s предполагаетпередачувкачествеаргументауказателя на char. На некоторых системах это приведет к фатальной ошибке при выполнении программы, называемой «несанкционированным доступом».

Использование одинарных кавычек, заключающих строки символов, есть синтаксическая ошибка. Строка символов должна быть заключена в двойные кавычки.

58

Использование двойных кавычек для обозначения символьной константы. Такая запись фактически создает строку, содержащую два символа, вторым из которых является символ '\0', ограничивающий строку. Символьная константа содержит один символ и должна заключаться в одинарные кавычки.

Попытка напечатать символ процента как литерал при использовании в строке управления форматом %, а не %%. Если в строке управления форматом появляется символ %, то за ним должен следовать спецификатор преобразования.

Задание недостаточной ширины поля для вывода текущего значения.Этоможетсместитьдругиеданные,спутаввсерезультаты.Следите за своими данными!

Попыткавывестивоператореprintf вкачествелитеральныхданных символ двойной кавычки, вопросительного знака или обратной косой черты, не предваряя этот символ обратной косой чертой.

Хороший стиль

Аккуратно форматируйте представление данных при выводе. Это делает результаты программы понятнее и уменьшает число ошибок пользователя.

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

При вводе данных просите пользователя ввести один элемент или небольшое число элементов данных за один раз. Не предлагайте ему вводить в ответ на один запрос значительный объем данных.

Переносимость программ

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

59

Структуры, объединения, операции с битами и перечисления

Распространенные ошибки

Забывают о точке с занятой, завершающей определение структуры.

Присваивают структуру одного типа структуре другого типа.

Сравнение структур является синтаксической ошибкой, потому что на различных системах существуют отличающиеся друг от друга

правила выравнивания.

Вставляютпробелмеждусоставнымичастями и> операцииука-

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

Пытаются сослаться на элемент структуры, используя только имя элемента.

Не используют скобки, когда ссылаются на элемент структуры, применяя указатель и операцию элемента структуры (иными словами,

запись *aPtr.suit является синтаксической ошибкой).

Полагая, что структуры,как и массивы, вызываются по ссылке автоматически, пытаются изменить значение структуры вызывающей функции в вызываемой.

Забывают индекс массива при ссылке на отдельные структуры в массиве структур.

Обращаются к данным, хранящимся в объединении, как к данным одноготипа,втовремякакхранящиесявданныймоментвобъединении

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

Сравнение объединений является синтаксической ошибкой, поскольку в разных системах существуют различные требования на выравнивание.

Инициализация объединения в объявлении значением, которое имеет тип, отличный от типа первого элемента объединения.

Использование логической операции И ( && ) вместо поразрядной

операции ( & ) и наоборот.

Использование логической операции ИЛИ ( || ) вместо поразрядной операции ( | ) и наоборот.

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

60

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]