Алексеев Е.Р., Чеснокова О.В., Кучер Т.В. Самоучитель по программированию на Free Pascal и Lazarus |
121 |
{Если значение переменной m выходит за пределы области допустимых значений, то выдается сообщение об ошибке.}
else
writeln(‘ОШИБКА ПРИ ВВОДЕ!!!’);
end
end.
Понятно, что чем меньше в программе ошибок, тем она лучше. В очень хорошей программе ошибок нет вообще. А это значит, что программист должен не только основательно продумать алгоритм, поставленной задачи, но и предугадать ошибки, которые может допустить пользователь, работая с программой.
Если пользователь допустил ошибку, например, при вводе данных, его необходимо проинформировать об этом. Для этого можно воспользоваться функцией MessageDlg, которая выводит сообще-
ние в отдельном окне. В общем виде функцию записывают так:
MessageDlg(сообщение, тип_сообщения, [список_кнопок], справка);
где
•сообщение – текст, который будет отображен в окне сообщения;
•тип_ сообщения – определяет внешний вид окна (табл. 3.2);
•список_кнопок – константы (перечисляются через запятую), определяющие тип кнопок окна сообщения (табл. 3.3);
•справка – номер окна справочной системы, которое будет вы-
ведено на экран, если нажать F1, параметр равен нулю, если использование справки не предусмотрено.
Таблица. 3.2. Тип окна сообщения.
Параметр |
Тип окна сообщения |
mtInformation |
информационное |
mtWarning |
предупредительное |
mtError |
сообщение об ошибке |
mtConfirmation |
запрос на подтверждение |
mtCustom |
обычное |
Алексеев Е.Р., Чеснокова О.В., Кучер Т.В. Самоучитель по программированию на Free Pascal и Lazarus |
122 |
Таблица. 3.3. Тип кнопки в окне сообщения.
Константа |
Кнопка в окне сообщения |
mbYes |
Да |
mbNo |
Нет |
mbOk |
Ок |
mbCancel |
Отмена |
mbAbort |
Прервать |
mbRetry |
Повторить |
mbIgnore |
Пропустить |
mbHelp |
Помощь |
Вернемся к задаче решения квадратного уравнения (задача 3.3). Нами был рассмотрен алгоритм решения этой задачи и написана программа на языке программирования Free Pascal. Реализуем эту задачу в среде Lazarus. Создадим новый проект41 (рис. 3.20).
Для организации ввода коэффициентов уравнения внедрим на форму четыре объекта типа надпись (Label1, Label2,
Label3, Label4) и три поля ввода (Edit1, Edit2, Edit3).
Корни уравнения или сообщение Рисунок 3.20: Форма для ре- об их отсутствии будем выводить
шения квадратного уравнения в надпись Label542.
Все действия по вычислению корней квадратного уравнения будут выполняться при нажатии кнопки Button1.
При вводе данных в программе могут возникнуть следующие ошибки:
•в поле ввода оказалась строка, которую невозможно преобразовать в число;
•значение коэффициента a равно нулю43.
Для того чтобы не допустить подобных ошибок необходимо контролировать данные, вводимые пользователем. Применим для этой цели встроенную процедуру Val(S,X,Kod), которая преоб-
разовывает строку S в целое или вещественное число X. Если преоб-
41 Подробно о создании проекта см. в первой главе.
42 На этапе конструирования формы Label5.Visible:=false.
43 В этом случае при вычислении корней произойдет деление на ноль.
Алексеев Е.Р., Чеснокова О.В., Кучер Т.В. Самоучитель по программированию на Free Pascal и Lazarus |
123 |
разование прошло успешно, то параметр Kod принимает значение,
равное нулю, а результат преобразования записывается в переменную X. В противном случае Kod содержит номер позиции в строке S, где
произошла ошибка, и содержимое переменной X не меняется. Далее
приведен фрагмент программы с подробными комментариями: procedure TForm1.Button1Click(Sender: TObject);
var
a,b,c,d,x1,x2: real; kod1,kod2,kod3:integer; begin
//Ввод значений коэффициентов уравнения. //Из поля ввода Edit1 считывается строка //символов и преобразовывается в вещественное //число, если преобразование прошло успешно, //то kod1=0 и полученное число записывается //в переменную a.
val(Edit1.Text,a,kod1);
val(Edit2.Text,b,kod2);
val(Edit3.Text,c,kod3);
//Если преобразования прошли успешно, то if (kod1=0) and (kod2=0) and (kod3=0) then
//проверить чему равен первый коэффициент. //Если значение первого коэффициента //равно нулю, то
if a=0 then
//выдать соответствующее сообщение. MessageDlg(‘Введите не нулевое значение а’, mtInformation,[mbOk],0)
else
//иначе перейти к решению уравнения begin
d:=b*b-4*a*c; Label5.Visible:=true; if d<0 then
Label5.Caption:=’В уравнении’+ chr(13)+’нет действительных корней’
else begin
Алексеев Е.Р., Чеснокова О.В., Кучер Т.В. Самоучитель по программированию на Free Pascal и Lazarus |
124 |
x1:=(-b+sqrt(d))/2/a; x2:=(-b-sqrt(d))/(2*a);
Label5.Caption:=’X1=’+
FloatToStr(x1)+chr(13)+
‘X2=’+FloatToStr(x2);
end;
end else
//Преобразование не выполнено, //выдать сообщение.
MessageDlg(‘Введите числовое значение’, mtInformation,[mbOk],0);
end;
Результаты работы программы показаны на рис. 3.21 — 3.24.
Рисунок 3.22: Обработка |
|
ошибки ввода данных — в поле |
|
Рисунок 3.21: Обработка |
ввода строка, которую невоз- |
ошибки ввода данных — коэффи- |
можно преобразовать в число |
циент a равен 0 |
(коэффициент равен символу B) |
Рисунок 3.23: Решение квад- |
|
ратного уравнения 3x2+4x+5=0 |
Рисунок 3.24: Вычисление |
(корней нет) |
корней квадратного уравнения |
│
English (en) │
suomi (fi) │
Free Pascal supports exceptions. Exceptions are useful for error handling and avoiding resource leaks. However, bear in mind that exceptions have a performance impact.
The official documentation is here: Reference guide chapter 17.
By default exceptions are disabled. You can opt in by using the ObjFPC or DELPHI Compiler Mode, or adding -Sx to the compiler commandline. This enables the try
, raise
, except
, and finally
reserved words for use in your own code, but it doesn’t enable exception generation from the RTL. To enable the RTL to raise exceptions instead of generating runtime errors, use the SysUtils unit in your program.
The base Exception
class is found in the SysUtils unit. All exceptions should preferably use this class or its descendants.
SysUtils automatically sets up a basic exception catcher, so any otherwise uncaught exception is displayed in human-readable form, as the program terminates. To generate readable callstacks from caught exceptions in your own code, without necessarily terminating the program, you can use SysUtils functions ExceptAddr
, ExceptFrames
, ExceptFrameCount
, and BackTraceStrFunc
.
Examples
Note that Pascal uses different keywords than some other languages: raise
instead of throw
, and except
instead of catch
. Also, as a compiler design choice, each try
-block can pair with either an except
or finally
block, but not both at the same time. You’ll need to nest try
-blocks if you need except
and finally
protecting the same code.
Error handling
uses sysutils; begin try // Do something that might go wrong. except begin // Try to recover or show the user an error message. end; end; end.
Cleaning up resources
try // Do something that might go wrong. finally // Clean-up code that is always called even if an exception was raised. end;
Exception leaking
Finally-blocks don’t destroy exception objects. Any exception that reaches the program’s «end.» will trigger a memory leak warning. An extra except block can be used to consume such exceptions. This is Delphi-compatible.
begin try // Your main program, where an exception object is created. finally try // Clean-up code. except end; end; end.
Signalling problems
raise Exception.Create('Helpful description of what went wrong');
Using specialised exception types to signal different problems
type EMyLittleException = Class(Exception); begin try raise EMyLittleException.Create('Foo'); except on E : EMyLittleException do writeln(E.Message); on E : Exception do writeln('This is not my exception!'); else writeln('This is not an Exception-descendant at all!'); end; end;
Re-raising exceptions
try // Do something that might go wrong. except // Try to recover or show the user an error message. if recoveredSuccessfully = FALSE then raise; end;
Exception classes
SysUtils defines and raises many specific exception classes.
With SysUtils included in your program, and exceptions enabled, various runtime errors are changed into exceptions. Processor-level interrupts like SIGSEGV or SIGFPU, which normally translate to run-time errors, are also changed to exceptions. For example, run-time error 200 (division by zero) becomes EDivByZero or EZeroDivide, while run-time error 216 (a general protection fault) becomes EAccessViolation.
Best practice
- Raise exceptions to signal that an operation could not be completed, where it normally should have succeeded.
- Do not use exceptions as part of expected control flow. Instead, add checks for common error conditions and return error values the old-fashioned way. For example, input parsing problems or file existence fails are usually not truly exceptional.
- But do raise an exception if it’s critical that the error is noticed; programmers may forget to check for returned error values.
- Naming convention: Prefix exception class names with a capital E.
- Re-raise exceptions in
except
-blocks usingraise;
if you were unable to recover from the problem; this preserves the original exception’s callstack. - Be careful about using the catch-all base
Exception
class, since the underlying code or OS/filesystem might produce an unanticipated exception that slips through your exception handler, potentially corrupting the program state. - When writing a unit or library, if exceptions are used at all, they should be documented clearly up front. This way the unit user knows what to expect.
- Keep exception handling away from code that needs to run as fast as possible.
Performance
Compiler optimisation levels don’t make much difference. All exception blocks use a small amount of wrapper code that can’t be optimised away. There are different ways for a compiler to produce exception code, with varying performance implications. As of FPC 3.0.4, the default exception mechanism uses the standard setjmp/longjmp style. FPC also supports OS-provided Structured Exception Handling; this is the default on Win64, and can be enabled on Win32 (recompile the compiler with $define TEST_WIN32_SEH
). Other mechanisms may be added to FPC in the future.
To get a better feel for what’s going on, try writing a small test program and compiling it with the -a
switch. This leaves behind a human-readable assembly file.
Notes on the setjmp/longjmp method:
Try
-statements insert some extra address calculations, a stack push and pop, some direct jumps, and a conditional jump. This is the setup cost of an exception frame, even if no exception is raised.Except
-statements insert the above, plus a push and pop, more direct jumps, and another conditional jump.Finally
-statements add a pop and a conditional jump.Raise
-statements create a string and pass control to FPC’s exception raiser. The string creation itself can spawn an implicit exception frame for the function/method, due to dynamic string allocations, even if theraise
is not executed. This is why in e.g. container types one often sees theraise
moved to a separate local (private) procedure. This localises the exception frame to that procedure, so the performance hit is only taken if the procedure is called.
Furthermore, generating a human-readable callstack from a raised exception involves time-consuming stack traversal and string manipulation.
With all that said, outside of heavy processing code, the convenience of exceptions usually outweighs their performance impact. Don’t feel bad about using them if it makes your code better.
Further reading
Logging exceptions
Avoiding implicit try finally section
Exceptions vs Error codes — a situation where exceptions might be a danger in some code (see middle of this page)
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Grids, StdCtrls, Spin; type { TForm1 } TForm1 = class(TForm) Save_B: TButton; Load_B: TButton; P_E: TEdit; I_L: TLabel; Z_L: TLabel; R_L: TLabel; P_L: TLabel; Count_SE: TSpinEdit; I_SG: TStringGrid; Z_SG: TStringGrid; R_SG: TStringGrid; procedure Count_SEChange(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Load_BClick(Sender: TObject); procedure Save_BClick(Sender: TObject); private { private declarations } public { public declarations } end; var Form1: TForm1; implementation type TPeople = record Name, Family : ShortString; Rost, Ves : Double; end; {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin I_SG.RowCount:=Count_SE.Value+1; I_SG.Cells[0,0]:='Фамилия'; I_SG.Cells[1,0]:='Имя'; I_SG.Cells[2,0]:='Рост'; I_SG.Cells[3,0]:='Вес'; Z_SG.Cells[0,0]:='Фамилия'; Z_SG.Cells[1,0]:='Имя'; Z_SG.Cells[2,0]:='Рост'; Z_SG.Cells[3,0]:='Вес'; R_SG.Cells[0,0]:='Фамилия'; R_SG.Cells[1,0]:='Имя'; R_SG.Cells[2,0]:='Рост'; R_SG.Cells[3,0]:='Вес'; end; procedure TForm1.Save_BClick(Sender: TObject); var people : TPeople; f: File; i: Integer; begin AssignFile(f,'data.dat'); Rewrite(f,1); for i:=1 to I_SG.RowCount-1 do begin people.Family:=I_SG.Cells[0,i]; people.Name:=I_SG.Cells[1,i]; people.Rost:= StrToFloat(I_SG.Cells[2,i]); people.Ves:= StrToFloat(I_SG.Cells[3,i]); BlockWrite(f,people.Name,SizeOf(people.Name)); BlockWrite(f,people.Family,SizeOf(people.Family)); BlockWrite(f,people.Rost,SizeOf(people.Rost)); BlockWrite(f,people.Ves,SizeOf(people.Ves)); end; CloseFile(f); end; procedure TForm1.Load_BClick(Sender: TObject); var people : Array[1..100] of TPeople; f : file; i, count, j : Integer; b : String; begin AssignFile(f,'data.dat'); Reset(f,1); count := 0; while not EOF(f) do begin inc(count); BlockRead(f,people[count].Name,SizeOf(people[count].Name)); BlockRead(f,people[count].Family,SizeOf(people[count].Family)); BlockRead(f,people[count].Rost,SizeOf(people[count].Rost)); BlockRead(f,people[count].Ves,SizeOf(people[count].Ves)); end; CloseFile(f); Z_SG.RowCount:=count+1; for i := 1 to count do begin Z_SG.Cells[1,i]:=people[i].Name; Z_SG.Cells[0,i]:=people[i].Family; Z_SG.cells[2,i]:=FloatToStr(people[i].Rost); Z_SG.cells[3,i]:=FloatToStr(people[i].Ves); j:=0; b:=P_E.Text; if (pos(b,people[i].Name)=1) then if (people[i].Ves = people[i].Rost - 100) then begin inc(j); R_SG.RowCount:=j+1; R_SG.Cells[1,i]:=people[i].Name; R_SG.Cells[0,i]:=people[i].Family; R_SG.Cells[2,i]:=FloatToStr(people[i].Rost); R_SG.Cells[3,i]:=FloatToStr(people[i].Ves); end; end; if j = 0 then begin R_SG.RowCount:=2; R_SG.Cells[1,i]:='Заданным'; R_SG.Cells[0,i]:='Человека по'; R_SG.Cells[2,i]:='Параметрам'; R_SG.Cells[3,i]:='Не найдено'; end; end; procedure TForm1.Count_SEChange(Sender: TObject); begin I_SG.RowCount:=Count_SE.Value+1; end; end. |
Lazarus — это среда программирования на языке Pascal, предназначенная для создания кроссплатформенных приложений. Как и любая другая программа, Lazarus может встретиться с ошибками. В этой статье мы рассмотрим проверенные методы и рекомендации по исправлению ошибок в Lazarus.
Ошибка компиляции
Одной из основных ошибок при работе с Lazarus является ошибка компиляции. Это означает, что компилятор не может преобразовать исходный код в исполняемый файл из-за ошибок в коде.
Существует несколько причин, по которым может возникнуть ошибка компиляции:
- Синтаксическая ошибка в исходном коде
- Отсутствие необходимых компонентов или библиотек
- Некорректные параметры проекта
Чтобы исправить ошибку компиляции, необходимо:
- Проверить синтаксис исходного кода на наличие ошибок
- Проверить настройки проекта и убедиться, что все необходимые компоненты и библиотеки установлены
- Убедиться, что параметры проекта заданы правильно
- Попробовать перекомпилировать проект
Ошибка запуска
Если Lazarus успешно скомпилирован, но не запускается, то в этом случае возникает ошибка запуска. Ошибка запуска может быть вызвана множеством факторов, включая неправильные настройки окружения, несовместимые версии библиотек и компонентов, а также проблемы с операционной системой.
Для исправления ошибки запуска, рекомендуется:
- Проверить настройки окружения и установить необходимые пакеты и библиотеки
- Убедиться, что используемые версии библиотек и компонентов совместимы
- Попробовать запустить Lazarus с правами администратора
- Проверить журналы системы на наличие ошибок, которые могут быть связаны с запуском Lazarus
Ошибка открытия проекта
Если Lazarus не может открыть проект, то возникает ошибка открытия проекта. Это может быть вызвано неправильными настройками, поврежденным проектом или проблемами с файловой системой.
Для исправления ошибки открытия проекта, рекомендуется:
- Проверить настройки путей к файлам и убедиться, что они корректны
- Проверить файлы проекта на наличие повреждений
- Попробовать скопировать проект в другую папку и открыть его там
Ошибка совместимости
Если Lazarus не может открыть или скомпилировать проект, то может быть проблема совместимости. Это может быть вызвано различными факторами, включая несовместимые версии библиотек, компонентов или ОС.
Для исправления ошибки совместимости, рекомендуется:
- Проверить требования к версии Lazarus и установить соответствующую версию
- Установить все необходимые пакеты и библиотеки, которые требуются для компиляции проекта
- Проверить настройки проекта и убедиться, что они совместимы с Lazarus
Заключение
Исправление ошибок в Lazarus может быть сложным и трудоемким процессом. Однако, следуя рекомендациям, описанным в этой статье, можно существенно упростить этот процесс. Помните, что самая важная часть исправления ошибок — это внимательное и методичное их поиск и их осознанное исправление.
Зато, если выкинет в середине цикла, который должен обработать все элементы (и после него ещё должна быть обработка), программа вообще никак не будет работать. Зависнет, например. А так, элемент будет пропущен и обработка продолжится. К тому же, если исключения выбрасываются библиотекой, что мне искать все классы исключений, которые она может выбросить? Но я не спорю — это действительно неправильно. Вопрос в том как с ними работать по-человечески? Или, exceptions — это, из серии «гладко было на бумаге, да забыли про овраги»?
Добавлено спустя 1 минуту 53 секунды:
2MageSlayer:
Такое же, как и всё остальное. Один из случаев, когда я не понимаю, как с ними правильно работать.
Ещё, например, меня интересует тонна проверок на nil. Ведь, по-идее, в коде не должно быть таких проверок?