100 likes | 341 Views
Типовые ошибки. Ошибок нет лишь в абсолютно не нужной программе. Если программа заработала с первого раза, есть большая доля вероятности, что она не будет работать никогда
E N D
Типовые ошибки • Ошибок нет лишь в абсолютно не нужной программе. Если программа заработала с первого раза, есть большая доля вероятности, что она не будет работать никогда • Даже в профессиональных программных пакетах, время от времени обнаруживаются ошибки логического характера, приводящие либо к непредусмотренному поведению программы, либо к конфликтам с другими программами, либо к конфликтам в оборудовании • Практически невозможно перечислить все виды ошибок, которые допускает человек при написании программы. Сейчас мы поговорим об ошибках, которые часто совершают люди, начинающие программировать на языке Си. Речь пойдет не о синтаксических ошибках, обнаружить которые помогает компилятор, а об логически ошибочных конструкциях, приводящих к неверной работе программы
Использование неинициализированного указателя • Характерный пример: int *pt, t; ... *pt = 13; • Указателю не присвоен адрес, и он содержит случайное число. Запись по случайному адресу может испортить данные или код программы, что приведет к краху операционной системы и потребует ее перезагрузки • Допустимый вариант: int *pt, t; ... pt = &t; *pt = 13;
Отсутствие завершающего нулевого байта в конце строки • Пример ошибочного копирования символьной строки: char *psrs = "Копируем"; char dst[15], *pdst; pdst = dst; while(*psrs !='\0') *pdst++ = *psrs++; • При невнимательном копировании строк (или посимвольном программном заполнении символьного массива) не занесен завершающий нулевой байт как признак конца текста. В результате при чтении из такого массива (dst) процесс будет продолжен за пределами массива, пока случайно не попадется нулевой байт. Результат чтения при этом непредсказуем • Для устранения ошибки достаточно после приведенного текста добавить оператор: *pdst = '\0';
Лишний шаг в цикле • При работе с массивами следует помнить, что диапазон индексов массива простирается от 0 до (n-1), где n - объявленная размерность массива • Пример лишнего повторения цикла: int a[10], i; for(i=0; i<=10; i++) a[i] = i; • Правильный вариант: int a[10], i; for(i=0; i<10; i++) a[i] = i;
Использование знака присваивания(=) вместо тождества(==) • В ряде языков программирования проверка на равенство двух выражений осуществляется так: if (a = b) .... • В Си такая конструкция также допустима, но имеет совершенно другой смысл - оператор присваивания, заключенный в скобки, рассматривается как число, равное присвоенному значению. Поэтому (a = b) всегда истинно, кроме случая b=0 • Правильный Си-фрагмент проверки на тождественное равенство: if (a == b) puts("Равно"); else puts("Не равно");
Некорректное указание пути к файлу • Обратной косой чертой (\) разделяются имена каталогов при записи пути к файлу. Однако в Си с нее начинается запись управляющих символов или 8-ричных и 16-ричных констант. Такое совпадение может породить трудноуловимую ошибку при записи пути к файлу как текстовой строки: fp = fopen("c:\new\text.c", "w"); • Записывая подобное, программист ожидает открыть доступ к файлу text.c. Этого не произойдет. Символы \n будут восприняты как переход на новую строку, а символы \t - как знак табуляции. Именно они войдут в состав имен каталога и файла, что является недопустимым • Правильный вариант задания пути - это удвоение черты: fp = fopen("c:\\new\\text.c", "w");
Пропуск оператора break в переключателе • Оператор break используется в операторе switch для прекращения обработки конкретного варианта. Если в конце варианта он не записан, будут также выполнены все операторы варианта, следующего ниже по тексту
Передача в функцию копии аргумента вместо его адреса • Если прототип вызываемой функции содержит в качестве аргумента указатель, то при обращении к функции фактический параметр должен быть адресом фактического аргумента • Если вместо адреса будет передан сам аргумент, то последствия трудно предугадать, поскольку в любом случае переданное значение будет рассматриваться как некоторый адрес int n; scanf(“%d ",n); • Правильный вариант int n; scanf(“%d ",&n);
Неподключение необходимых header-файлов • Если все библиотечные функции, оперирующие данными с плавающей точкой (sin(), cos(), sqrt() и т.п.), возвращают неверные значения, то скорее всего программист не включил в текст программы строку: #include <math.h> • При динамическом резервировании памяти функциями типа malloc() должен быть подключен файл alloc.h: #include <alloc.h>
Прочие ошибки • Можно привести много примеров трудноуловимых логических ошибок • В частности, открытие файла в текстовом режиме может привести к невозможности последующего чтения двоичных данных • Ошибочным является возврат из функции адреса ее автоматической переменной, поскольку такие переменные прекращают существование после выхода из функции • Программист должен накапливать опыт и постепенно приобретать профессиональное чутье на наличие ошибки