Виды ошибок в программах
Я учусь на своих ошибках. Ругаю себя за это, но продолжаю ошибаться. С другой стороны — это всё-таки лучше, чем не учиться совсем, и наступать на одни и те же грабли бесконечно.
При создании программ, даже простых, ошибки неизбежны. Поэтому для поиска ошибок во всех средствах разработки имеются особые инструменты для отладки. Но сегодня не об отладке и не о поиске ошибок. Сегодня о видах ошибок, которые встречаются в программах.
Итак, основных вида всего три:
- Синтаксические ошибки
- Логические ошибки
- Ошибки выполнения программы
Синтаксические ошибки в программах
Эти ошибки довольно распространены, особенно среди начинающих. Но эти ошибки — самые безобидные. Потому что компиляторы легко находят ошибки синтаксиса и указывают место в исходном коде, где обнаружена такая ошибка. Программисту остаётся только исправить её.
Синтаксические ошибки — это ошибки синтаксиса (а то бы вы не догадались))). То есть ошибки правил языка. Например, для Паскаля это будет синтаксической ошибкой:
WriteLn('Hello, World !!!') ReadLn;
Потому что после первой строки нет точки с запятой.
Подобные ошибки очень часто совершают новички. И это вгоняет их в ступор — они пугаются и не могут понять, что же не так с их кодом. Хотя если бы они внимательно его посмотрели и прочитали сообщение об ошибке, то легко могли бы исправить её:
Потому что в сообщении чётко сказано:
Syntax error, ";" expected but "identifier READLN" found
что можно перевести как
синтаксическая ошибка, ";" ожидалось, но "READLN" найден
То есть компилятор говорит нам: я ожидал увидеть точку с запятой, а нашёл идентификатор READLN
.
Логические ошибки в программах
Это самые противные и самые труднонаходимые ошибки. Программа может быть написана совершенно правильно с точки зрения синтаксиса языка, и при этом она будет неправильно работать. Потому что программист допустил где-то логическую ошибку.
И компилятор вам ничего об этой ошибке не расскажет, потому что правила языка не нарушены.
Поиски таких ошибок могут занять много времени и отнять у вас немало здоровья. Поэтому при разработке программ лучше не торопиться и стараться не допускать логических ошибок.
Пример логической ошибки:
for i := 1 to 10 do if i = 15 then WriteLn('i = 15');
Здесь мы сравниваем значение i
с числом 15, и выводим сообщение, если i = 15
.
Но фишка в том, что в данном цикле i
не будет равно 15 НИКОГДА, потому что в цикле переменной i
присваиваются значения от 1 до 10.
Эта ошибка довольно безобидная. Здесь мы имеем просто бессмысленный код, который не причинит никакого вреда.
Однако представьте, что программа должна выдавать какой-то сигнал тревоги, если i = 15
. Тогда получится, что никакого сигнала пользователь никогда не услышит, даже если случилось что-то страшное. А всё потому, что программист немного ошибся. Вот так вот и падают ракеты и самолёты…
Распространённые логические ошибки в С++ вы можете посмотреть здесь.
Ошибки времени выполнения программы
Даже если исходный код не содержит ни логических, не синтаксических ошибок, это ещё не означает, что ваша программа безупречна. Потому что ошибки могут возникнуть в ходе выполнения программы. Например, случайно будет удалён файл, который должна читать программа, и она не сможет его найти. Если не принять мер, то программа может завершиться аварийно. А пользователям такое поведение программ очень не нравится.
Одна из самых рапространённых ошибок времени выполнения — это неожиданное деление на ноль. Пример:
Write('y = '); ReadLn(y); x := 100 / y; WriteLn('100 / ', y, ' = ', x);
Что здесь такого? Всё правильно и с точки зрения логики, и с точки зрения синтаксиса. И в большинстве случаев программа отработает без каких-либо неожиданностей.
Но представьте, что пользователь введёт ноль. Что тогда будет? Правильно — попытка деления на ноль. А на ноль делить нельзя. Поэтому во время выполнения этой программы произойдёт ошибка, которая очень расстроит пользователя. Потому что в случае, например, с консольным приложением программа просто закроется, и пользователь не поймёт, что это было. Но зато поймёт, что программа — говно, и программы от этого разработчика лучше больше никогда не использовать.
В данном случае, если вы не уверены на 100%, что y будет отличаться от нуля, надо всегда делать проверку на ноль. И хороший код должен быть хотя бы таким:
Write('y = '); ReadLn(y); if y = 0 then WriteLn('ERROR: y = 0') else begin x := 100 / y; WriteLn('100 / ', y, ' = ', x); end;
Ну что же. На этом с видами ошибок пока всё. Изучайте программирование и поменьше ошибайтесь.
|
Основы программирования Каждый профессионал когда-то был чайником. Наверняка вам знакомо состояние, когда “не знаешь как начать думать, чтобы до такого додуматься”. Наверняка вы сталкивались с ситуацией, когда вы просто не знаете, с чего начать. Эта книга ориентирована как раз на таких людей, кто хотел бы стать программистом, но совершенно не знает, как начать этот путь. Подробнее… |
The following compiles fine in my Eclipse:
final int j = 1/0;
// compiles fine!!!
// throws ArithmeticException: / by zero at run-time
Java prevents many «dumb code» from even compiling in the first place (e.g. "Five" instanceof Number
doesn’t compile!), so the fact this didn’t even generate as much as a warning was very surprising to me. The intrigue deepens when you consider the fact that constant expressions are allowed to be optimized at compile time:
public class Div0 {
public static void main(String[] args) {
final int i = 2+3;
final int j = 1/0;
final int k = 9/2;
}
}
Compiled in Eclipse, the above snippet generates the following bytecode (javap -c Div0
)
Compiled from "Div0.java"
public class Div0 extends java.lang.Object{
public Div0();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_5
1: istore_1 // "i = 5;"
2: iconst_1
3: iconst_0
4: idiv
5: istore_2 // "j = 1/0;"
6: iconst_4
7: istore_3 // "k = 4;"
8: return
}
As you can see, the i
and k
assignments are optimized as compile-time constants, but the division by 0
(which must’ve been detectable at compile-time) is simply compiled as is.
javac 1.6.0_17
behaves even more strangely, compiling silently but excising the assignments to i
and k
completely out of the bytecode (probably because it determined that they’re not used anywhere) but leaving the 1/0
intact (since removing it would cause an entirely different program semantics).
So the questions are:
- Is
1/0
actually a legal Java expression that should compile anytime anywhere?- What does JLS say about it?
- If this is legal, is there a good reason for it?
- What good could this possibly serve?
Aziz Shaikh
16.1k11 gold badges61 silver badges79 bronze badges
asked May 29, 2010 at 6:22
7
Is
1/0
actually a legal Java expression that should compile anytime anywhere?
Yes.
What does JLS say about it?
Nothing specific … apart from saying that division by zero will result in a runtime exception. However, the JLS acknowledges that possibility of runtime exceptions in the following definition:
«A compile-time constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following: …»
(Emphasis added.) So the following would NOT compile:
switch(i) {
case 1:
case 1 + 1:
case 1 / 0: // compilation error.
}
If this is legal, is there a good reason for it?
Good question. I suppose that it is a way to throw ArithmeticException
though that is hardly a plausible reason. A more likely reason for specifying Java this way is to avoid unnecessary complexity in the JLS and compilers to deal with an edge case that is rarely going to bite people.
But this is all by the by. The fact is that 1/0
is valid Java code, and no Java compiler should ever flag this as a compilation error. (It would be reasonable for a Java compiler to issue a warning, provided that there was a compiler switch to turn it off.)
answered May 29, 2010 at 6:28
Stephen CStephen C
688k94 gold badges790 silver badges1200 bronze badges
5
I did some digging into the Bug Database, and discovered some interesting information.
Bug ID 4178182: JLS doesnt specify behavior for 1/0 as a constant expression
The following code is illegal:
class X { static final int i = 1 / 0; }
The value of this compile-time constant is undefined, therefore this
has to be a compile-time error. Guy Steele confirmed about 18 months
ago that this was indeed the intended behaviour.A compile-time constant has to have its value available statically (that’s
what makes it a compile-time constant For example, the value of other
constants whose values are determined by a constant that contains a division
by zero are undefined. This affects the semantics ofswitch
statements,
definite assigment and unassignment, etc.
Bug ID 4089107: javac treats integer division by (constant) zero as an error
public class zero { public static void main(String[] args) { System.out.println(1/0); } }
Running the above yields:
zero.java:3: Arithmetic exception. System.out.println(1/0); ^ 1 error
Bug ID 4154563: javac accepts division by zero constant expressions in case expressions.
Java compiler crashes while trying to compile next test. This test also crashes all 1.2beta4 compiler versions, but bug is absent in 12.beta3. An example and compiler diagnostics follow:
public class B { public static void main(String argv[]) { switch(0){ case 0/0: } } }
Evaluation: The compiler used to report all attempts to divide by the constant zero as compile-time errors. This was fixed in beta3 so that code would be generated for division by constant zero. Unfortunately this bug was introduced. The compiler should handle a division by zero in a case expression gracefully.
Conclusion
So the question of whether or not 1/0
should compile was a contested topic of discussion, with some people quoting Guy Steele claiming that this should be a compile time error, and others saying that it shouldn’t. It seems that ultimately it’s decided that it’s neither a compile-time error nor a compile-time constant.
answered May 29, 2010 at 13:28
Well, if you look into the Double class, you will see the following:
/**
* A constant holding the positive infinity of type
* <code>double</code>. It is equal to the value returned by
* <code>Double.longBitsToDouble(0x7ff0000000000000L)</code>.
*/
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
The same calculation is made in the Float class, except with floats instead of doubles. Basically, 1/0 returns a really, really big number, larger than Double.MAX_VALUE.
This following code:
public static void main(String[] args) {
System.out.println(Double.POSITIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY > Double.MAX_VALUE);
}
Outputs:
Infinity
true
Note the special case in printing out Double.POSITIVE_INFINITY
. It prints out a string, though it’s regarded as a double.
To answer the question, yes it is legal in Java, but 1/0 resolves to «infinity» and is treated differently from standard Doubles (or floats, or so on and so forth).
I should note that I do not have the slightest clue how or why it was implemented this way. When I see the above output, it all seems like black magic to me.
answered May 29, 2010 at 10:18
3
Java explicitly requires integer division by zero to trigger an ArithmeticException
. The assignment to j
can’t be elided because that would violate the spec.
answered May 29, 2010 at 6:30
Marcelo CantosMarcelo Cantos
179k38 gold badges324 silver badges363 bronze badges
1
It’s legal because no where is it a given that the compiler is supposed to fold constant expressions at compile time.
A «smart» compiler might compile:
a = 1 + 2
as
a = 3
But there’s nothing that says the compiler HAS to do that. Other than that, 1/0 is a legal expression just like:
int a;
int b;
a = a/b;
is a legal expression.
At RUNTIME it throws an exception, but that’s a runtime error for a reason.
answered May 29, 2010 at 6:27
Will HartungWill Hartung
114k19 gold badges128 silver badges202 bronze badges
6
Why bother catching this at compile-time, when you’re going to need a run-time variant anyway?
For example, if you were loading and parsing «0» from a text file, then tried to divide by it, Java would have no idea what you were doing at compile-time because it doesn’t know the contents of that external file.
Also if you were to set any variable to 0 and divide by the variable, Java would have to keep track of every possible value of every variable at every point in the script in order to catch a divide by 0 at compile time.
Might as well keep things consistent and make it a runtime-only exception.
answered May 29, 2010 at 6:42
DanielDaniel
6776 silver badges11 bronze badges
2
It is legal in compiling point of view, but it would throw an exception if executed!
the reason… well programming must allow flexibility therefore all the expressions and every single code you type is a variable for the compiler, thus in the mathematical expression X/Y the compiler does not care if the Y variable value is (Y==0) or any other number for the compiler this is a variable… if the compiler would have to look at values also, that would be considered runtime, wouldn’t it.
answered May 29, 2010 at 6:39
TacB0sSTacB0sS
10k12 gold badges72 silver badges117 bronze badges
4
Since others already answered the legality of 1/0
, let’s move to the second question:
- If this is legal, is there a good reason for it?
- What good could this possibly serve?
An answer could be:
To tease a colleague of yours. ;o)
When the colleague leaves the room with his computer left unlocked, sneak by and burry
1/0
somewhere deep into a static initializer of some class that is used early in the application. This way he will find out soon enough after (or even during) the deployment of the application by encountering the unusualArithmeticException
and he will probably scratch his head for a while. Using this fail-fast way you can ensure it’s a relatively harmless joke.
P.S.: It worked. ;o)
answered Jan 8, 2016 at 9:38
charliecharlie
1,4588 silver badges20 bronze badges
The following compiles fine in my Eclipse:
final int j = 1/0;
// compiles fine!!!
// throws ArithmeticException: / by zero at run-time
Java prevents many «dumb code» from even compiling in the first place (e.g. "Five" instanceof Number
doesn’t compile!), so the fact this didn’t even generate as much as a warning was very surprising to me. The intrigue deepens when you consider the fact that constant expressions are allowed to be optimized at compile time:
public class Div0 {
public static void main(String[] args) {
final int i = 2+3;
final int j = 1/0;
final int k = 9/2;
}
}
Compiled in Eclipse, the above snippet generates the following bytecode (javap -c Div0
)
Compiled from "Div0.java"
public class Div0 extends java.lang.Object{
public Div0();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_5
1: istore_1 // "i = 5;"
2: iconst_1
3: iconst_0
4: idiv
5: istore_2 // "j = 1/0;"
6: iconst_4
7: istore_3 // "k = 4;"
8: return
}
As you can see, the i
and k
assignments are optimized as compile-time constants, but the division by 0
(which must’ve been detectable at compile-time) is simply compiled as is.
javac 1.6.0_17
behaves even more strangely, compiling silently but excising the assignments to i
and k
completely out of the bytecode (probably because it determined that they’re not used anywhere) but leaving the 1/0
intact (since removing it would cause an entirely different program semantics).
So the questions are:
- Is
1/0
actually a legal Java expression that should compile anytime anywhere?- What does JLS say about it?
- If this is legal, is there a good reason for it?
- What good could this possibly serve?
Aziz Shaikh
16.1k11 gold badges61 silver badges79 bronze badges
asked May 29, 2010 at 6:22
7
Is
1/0
actually a legal Java expression that should compile anytime anywhere?
Yes.
What does JLS say about it?
Nothing specific … apart from saying that division by zero will result in a runtime exception. However, the JLS acknowledges that possibility of runtime exceptions in the following definition:
«A compile-time constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following: …»
(Emphasis added.) So the following would NOT compile:
switch(i) {
case 1:
case 1 + 1:
case 1 / 0: // compilation error.
}
If this is legal, is there a good reason for it?
Good question. I suppose that it is a way to throw ArithmeticException
though that is hardly a plausible reason. A more likely reason for specifying Java this way is to avoid unnecessary complexity in the JLS and compilers to deal with an edge case that is rarely going to bite people.
But this is all by the by. The fact is that 1/0
is valid Java code, and no Java compiler should ever flag this as a compilation error. (It would be reasonable for a Java compiler to issue a warning, provided that there was a compiler switch to turn it off.)
answered May 29, 2010 at 6:28
Stephen CStephen C
688k94 gold badges790 silver badges1200 bronze badges
5
I did some digging into the Bug Database, and discovered some interesting information.
Bug ID 4178182: JLS doesnt specify behavior for 1/0 as a constant expression
The following code is illegal:
class X { static final int i = 1 / 0; }
The value of this compile-time constant is undefined, therefore this
has to be a compile-time error. Guy Steele confirmed about 18 months
ago that this was indeed the intended behaviour.A compile-time constant has to have its value available statically (that’s
what makes it a compile-time constant For example, the value of other
constants whose values are determined by a constant that contains a division
by zero are undefined. This affects the semantics ofswitch
statements,
definite assigment and unassignment, etc.
Bug ID 4089107: javac treats integer division by (constant) zero as an error
public class zero { public static void main(String[] args) { System.out.println(1/0); } }
Running the above yields:
zero.java:3: Arithmetic exception. System.out.println(1/0); ^ 1 error
Bug ID 4154563: javac accepts division by zero constant expressions in case expressions.
Java compiler crashes while trying to compile next test. This test also crashes all 1.2beta4 compiler versions, but bug is absent in 12.beta3. An example and compiler diagnostics follow:
public class B { public static void main(String argv[]) { switch(0){ case 0/0: } } }
Evaluation: The compiler used to report all attempts to divide by the constant zero as compile-time errors. This was fixed in beta3 so that code would be generated for division by constant zero. Unfortunately this bug was introduced. The compiler should handle a division by zero in a case expression gracefully.
Conclusion
So the question of whether or not 1/0
should compile was a contested topic of discussion, with some people quoting Guy Steele claiming that this should be a compile time error, and others saying that it shouldn’t. It seems that ultimately it’s decided that it’s neither a compile-time error nor a compile-time constant.
answered May 29, 2010 at 13:28
Well, if you look into the Double class, you will see the following:
/**
* A constant holding the positive infinity of type
* <code>double</code>. It is equal to the value returned by
* <code>Double.longBitsToDouble(0x7ff0000000000000L)</code>.
*/
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
The same calculation is made in the Float class, except with floats instead of doubles. Basically, 1/0 returns a really, really big number, larger than Double.MAX_VALUE.
This following code:
public static void main(String[] args) {
System.out.println(Double.POSITIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY > Double.MAX_VALUE);
}
Outputs:
Infinity
true
Note the special case in printing out Double.POSITIVE_INFINITY
. It prints out a string, though it’s regarded as a double.
To answer the question, yes it is legal in Java, but 1/0 resolves to «infinity» and is treated differently from standard Doubles (or floats, or so on and so forth).
I should note that I do not have the slightest clue how or why it was implemented this way. When I see the above output, it all seems like black magic to me.
answered May 29, 2010 at 10:18
3
Java explicitly requires integer division by zero to trigger an ArithmeticException
. The assignment to j
can’t be elided because that would violate the spec.
answered May 29, 2010 at 6:30
Marcelo CantosMarcelo Cantos
179k38 gold badges324 silver badges363 bronze badges
1
It’s legal because no where is it a given that the compiler is supposed to fold constant expressions at compile time.
A «smart» compiler might compile:
a = 1 + 2
as
a = 3
But there’s nothing that says the compiler HAS to do that. Other than that, 1/0 is a legal expression just like:
int a;
int b;
a = a/b;
is a legal expression.
At RUNTIME it throws an exception, but that’s a runtime error for a reason.
answered May 29, 2010 at 6:27
Will HartungWill Hartung
114k19 gold badges128 silver badges202 bronze badges
6
Why bother catching this at compile-time, when you’re going to need a run-time variant anyway?
For example, if you were loading and parsing «0» from a text file, then tried to divide by it, Java would have no idea what you were doing at compile-time because it doesn’t know the contents of that external file.
Also if you were to set any variable to 0 and divide by the variable, Java would have to keep track of every possible value of every variable at every point in the script in order to catch a divide by 0 at compile time.
Might as well keep things consistent and make it a runtime-only exception.
answered May 29, 2010 at 6:42
DanielDaniel
6776 silver badges11 bronze badges
2
It is legal in compiling point of view, but it would throw an exception if executed!
the reason… well programming must allow flexibility therefore all the expressions and every single code you type is a variable for the compiler, thus in the mathematical expression X/Y the compiler does not care if the Y variable value is (Y==0) or any other number for the compiler this is a variable… if the compiler would have to look at values also, that would be considered runtime, wouldn’t it.
answered May 29, 2010 at 6:39
TacB0sSTacB0sS
10k12 gold badges72 silver badges117 bronze badges
4
Since others already answered the legality of 1/0
, let’s move to the second question:
- If this is legal, is there a good reason for it?
- What good could this possibly serve?
An answer could be:
To tease a colleague of yours. ;o)
When the colleague leaves the room with his computer left unlocked, sneak by and burry
1/0
somewhere deep into a static initializer of some class that is used early in the application. This way he will find out soon enough after (or even during) the deployment of the application by encountering the unusualArithmeticException
and he will probably scratch his head for a while. Using this fail-fast way you can ensure it’s a relatively harmless joke.
P.S.: It worked. ;o)
answered Jan 8, 2016 at 9:38
charliecharlie
1,4588 silver badges20 bronze badges
30.12.2019C, C++
Ошибка — недопустимая операция, выполняемая пользователем, которая приводит к ненормальной работе программы.
Ошибки программирования часто остаются незамеченными, пока программа не будет скомпилирована или выполнена. Некоторые ошибки не позволяют программе скомпилироваться или выполнить. Таким образом, ошибки должны быть удалены перед компиляцией и выполнением.
Наиболее распространенные ошибки можно широко классифицировать следующим образом.
Тип ошибок
- Синтаксические ошибки: ошибки, возникающие при нарушении правил написания синтаксиса C / C ++, называются синтаксическими ошибками. Эта ошибка компилятора указывает на то, что должно быть исправлено, прежде чем код может быть скомпилирован. Все эти ошибки обнаруживаются компилятором и, таким образом, известны как ошибки времени компиляции.
Наиболее частые синтаксические ошибки:- Отсутствует скобка ( } )
- Печать значения переменной без ее объявления
- Отсутствует точка с запятой, как это:
#include<stdio.h>
void
main()
{
int
x = 10;
int
y = 15;
printf
(
"%d"
, (x, y))
}
Ошибка:
error: expected ';' before '}' token
- Синтаксис базовой конструкции написан неправильно. Например: цикл
#include<stdio.h>
int
main(
void
)
{
while
(.)
{
printf
(
"hello"
);
}
return
0;
}
Ошибка:
error: expected expression before '.' token while(.)
В данном примере синтаксис цикла while неверен. Это вызывает синтаксическую ошибку.
- Ошибки во время выполнения : ошибки, возникающие во время выполнения программы (во время выполнения) после успешной компиляции, называются ошибками во время выполнения. Одной из наиболее распространенных ошибок времени выполнения является деление на ноль, также известное как ошибка деления. Эти типы ошибок трудно найти, так как компилятор не указывает на строку, в которой происходит ошибка.
Для большего понимания запустите пример, приведенный ниже.#include<stdio.h>
void
main()
{
int
n = 9,
div
= 0;
div
= n/0;
printf
(
"resut = %d"
,
div
);
}
Ошибка:
warning: division by zero [-Wdiv-by-zero] div = n/0;
В приведенном примере есть деление на ноль ошибок. Это пример ошибки во время выполнения, то есть ошибки, возникающие при запуске программы.
- Ошибки компоновщика: эта ошибка возникает, когда после компиляции мы связываем различные объектные файлы с объектом main, используя клавиши Ctrl + F9 (RUN). Это ошибки, возникающие, когда невозможно сгенерировать исполняемый файл программы. Это может быть связано с неправильным прототипированием функций, неправильными заголовочными файлами. Одна из самых распространенных ошибок компоновщика — запись Main () вместо main () .
#include<stdio.h>
void
Main()
{
int
a = 10;
printf
(
"%d"
, a);
}
Ошибка:
(.text+0x20): undefined reference to `main'
- Логические ошибки: При компиляции и выполнении программы желаемый вывод не получается, когда заданы определенные входные значения. Эти типы ошибок, которые обеспечивают неправильный вывод, но, по-видимому, не содержат ошибок, называются логическими ошибками. Это одна из самых распространенных ошибок начинающих программистов.
Эти ошибки зависят исключительно от логического мышления программиста и их легко обнаружить, если мы следуем линии выполнения и определяем, почему программа выбирает этот путь выполнения.int
main()
{
int
i = 0;
for
(i = 0; i < 3; i++);
{
printf
(
"loop "
);
continue
;
}
getchar
();
return
0;
}
Нет выхода
- Семантические ошибки: эта ошибка возникает, когда операторы, написанные в программе, не имеют значения для компилятора.
void
main()
{
int
a, b, c;
a + b = c;
}
ошибка
error: lvalue required as left operand of assignment a + b = c; //semantic error
Эта статья предоставлена Кришна Бхатия . Если вы как GeeksforGeeks и хотели бы внести свой вклад, вы также можете написать статью с помощью contribute.geeksforgeeks.org или по почте статьи contribute@geeksforgeeks.org. Смотрите свою статью, появляющуюся на главной странице GeeksforGeeks, и помогите другим вундеркиндам.
Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.
Рекомендуемые посты:
- Предопределенные макросы в C с примерами
- Как создать графический интерфейс в программировании на C, используя GTK Toolkit
- C ++ программа для печати всех четных и нечетных чисел от 1 до N
- Переверните содержимое файла и сохраните его в другом
- Библиотека ctype.h (<cctype>) в C / C ++ с примерами
- Минимум ячеек, которые нужно перевернуть, чтобы получить подматрицу 2 * 2 с равными элементами
- Вложенные циклы в C ++ с примерами
- Слабые байты в структурах: объяснение на примере
- Функция _Find_first () в C ++ bitset с примерами
- Функция _Find_next () в C ++ bitset с примерами
- Обход всех уровней N-арного дерева влево-вправо
- Разница между итераторами и указателями в C / C ++ с примерами
- ostream :: seekp (pos) метод в C ++ с Exmaples
- Методы по умолчанию в C ++ с примерами
Ошибки в C / C ++
0.00 (0%) 0 votes
1. Синтаксические ошибки.
Причиной
таких ошибок могут быть неправильно
написанные ключевые слова, неверно
примененные разделители или недопустимые
комбинации операторов. Такие ошибки
VisualBasicраспознает сразу после того, как курсор
покидает только что написанную строку.
Строка с синтаксической ошибкой
выделяется красным цветом. После
устранения ошибки выделение цветом
снимается.
VisualBasicимеет средства,
позволяющие не только обнаружить
синтаксическую ошибку, но и избежать
ее в процессе написания кода. К таким
средствам относятся
– механизм
контекстной подсказки (после написании
ключевого слова появляется окно, в
котором отображается полный синтаксис
вводимого оператора или список аргументов
используемой процедуры);
– автоматическое
отображение списка элементов (например,
после имени элемента управления
появляется список всех свойств и
методов, из которого можно выбрать
требуемое);
– дополнение
слова (при вводе нескольких начальных
символов, достаточных для распознавания
ключевого слова, недостающие символы
автоматически добавляются).
2. Ошибки в структуре программы.
Ошибки
такого рода появляется в результате
неправильного написания многострочного
оператора (например, в операторе цикла
Forнет последней
строки со словомNext
или в уловном оператореIfнетEnd If).
Такие
ошибки VisualBasicраспознает не в процессе ввода символов,
а на этапе компиляции. Сообщение об
ошибке выводится в специальном окне и
начинается оно словамиCompileerror(«ошибка компиляции»)
и содержит указание на ошибку (например,BlockIfwithoutEndIf– «блокIfбезEndIf»).
3. Ошибки, возникающие во время выполнения программы.
Это
ошибки, возникающие во время работы
программы (например, при выполнении
деления на ноль или при попытки чтения
из несуществующего на диске файла). В
таких случаях выводится сообщение в
специальном окне, в котором указывается
причина прерывания программы и номер
ошибки. На этом окне есть четыре кнопки
«Continue», «End»,
«Debug» и «Help».
В
качестве примера на рис.33 показано окно
с сообщением об ошибке деления на ноль.
Рис.33.
Вид окна с сообщением об ошибке на этапе
выполнения
Из
рисунка видно, что надпись на кнопке
«Continue» бледнее остальных.
Это означает, что при такой ошибке
дальнейшее продолжение программы
невозможно. Контур кнопки «Debug»
выделен жирной линией, это означает,
что для перехода в режим отладки эту
кнопку можно «нажать» не только с
помощью манипулятора «мышь», но и путем
нажатия кнопки «Enter»
клавиатуры. Нажатие кнопки «End»
приведет к завершению программы, а
кнопки «Help» – к появлению
окна справки с информацией о типе ошибки
и возможности ее устранения.
При
переходе в режим отладки открывается
окно с текстом программы, в которой
выделена строка с командой, выполнение
которой привело к прерыванию. При этом
появляется возможность определить
значения переменных на момент выполнения
прерывания. Для этого достаточно
подвести курсор к имени переменной, в
появившемся окошке появится либо ее
значение, либо слово «Empty»
(«пустая»), если на момент выполнения
команды переменная не получила никакого
значения.
На
этапе разработки программы можно
предусмотреть перехват возможных
ошибок. Это делается с помощью специальной
процедуры – обработчика ошибок.
Для
перехвата возможной ошибки в исполняемой
процедуре используется оператор On
Error. В нем указывается
метка, которая должна находиться в той
же процедуре и помечать тот фрагмент
кода, куда будет осуществлен переход
при возникновении ошибки выполнения.
Обычно этот фрагмент находится в конце
процедуры, а перед меткой помещается
операторExit, благодаря
которому процедура завершается, если
ошибка не возникла.
Обработка
ошибки начинается с установления типа
ошибки. Для этого используется объект
Err, свойство которогоNumber содержит код
последней возникшей ошибки.
После
обработки ошибки программа должна
продолжить свое исполнение. Для того,
чтобы программа продолжала выполняться
в строке, в которой возникла ошибка, в
обработчике указывается оператор
Resume. Если нужно
продолжить программу не с этой, а со
следующей строки, используется операторResume Next.
В
качестве примера рассмотрим процедуру,
которая запускается при нажатии кнопки
со знаком «/» в проекте «Простой
калькулятор» (Лекция №№). При этом
число, введенное в текстовое окноText1делится на число, введенное в окноText2, результат
заносится в окноText3.
Возможная ошибка – деление на ноль.
Обработка ошибки может выглядеть
следующим образом.
Private
Sub Command3_Click()
On
Error GoTo ошибка
X=Val(
Text1.Text)
Y=Val(
Text2.Text)
Z=X/Y
Text3.Text=Z
Exit
Sub
ошибка:
Z=
Val
(InputBox(“Введите
число, не равное нулю”))
Resume
End
Sub
Если
в программе возможно появление нескольких
ошибок, их можно обработать, предварительно
определив их код и в зависимости от
кода применить тот или иной метод. В
табл.11 приведены описания основных
ошибок этапа выполнения программы.
Таблица
11. Коды основных ошибок
-
Код
ошибкиОшибка
выполнения6
Переполнение
7
Мало
памяти11
Деление
на ноль13
Несовпадение
типов35
Подпрограмма
или функция не заданны53
Файл
не найден55
Файл
уже открыт57
Ошибка
устройств ввода-вывода75
Ошибка
доступа к файлу
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
Перейти к контенту
Прочитайте эти заметки после появления у вас некоторого опыта программирования на C#. Вспомните, пришлось ли вам справиться с ошибками, описанными ниже.
Простейшая классификация ошибок (errors):
- Синтаксические (ошибки в операторах) – компилятор их обнаруживает и указывает возможную причину. Пока вы их не устраните, программа выполняться не будет.
- Логические (нарушение логики алгоритма решения задачи) – компилятор их пропускает, так как проверяет только синтаксис программы.
- Ошибки, возникающие во время выполнения программы (Run-time error). Такая ошибка генерирует исключение (exception), которое без обработки приводит к остановке программы с указанием возможной причины.
Вот типичные ошибки, которые делает почти каждый программист.
Синтаксические ошибки:
- Если вы забыли вставить или случайно закомментировали оператор подключения библиотеки (например, using System;), то для вас будут недоступны поля и методы класса Console и всех других классов. Хорошо, что компилятор подскажет вам эту ошибку: «Элемент «Console» не существует в текущем контексте».
- Пренебрежение точкой с запятой в конце оператора является синтаксической ошибкой, которая возникает тогда, когда компилятор не может распознать оператор. Обычно выдается сообщение «Требуется “;”».
- Если операции ==, !=, >=, <= содержат между своими символами пробелы, это ошибка.
- Смешивание операций проверки на равенство == с операцией присваивания приводит к ошибкам, как синтаксическим, так и логическим.
- Использование ключевого слова в качестве идентификатора переменной.
- Пропуск одной или обеих фигурных скобок, ограничивающих составной оператор.
- Запись ключевого слова while как While с символом W в верхнем регистре приведет к синтаксической ошибке. Заметим, что все ключевые слова содержат только символы нижнего регистра.
- Попытка использовать в операции инкремента не имя простой переменной, а выражение, например, “++(d+2)”.
- Использование запятых вместо точек с запятой в заголовке цикла for.
- Вызов математических функций по имени (например, Sin(x)) без указания в качестве префикса имени класса Math. Нужно вызывать так: Math.Sin(x). То же касается и методов других классов: Console, Convert и тому подобных.
- Забываем вставить оператор break в структуре оператора switch.
Логические ошибки:
- Постановка точки с запятой после правой круглой скобки, завершающей условие в операторе if, приводит к тому, что тело структуры if становится пустым. Подлинный оператор тела структуры if превращается в оператор, следующий за структурой if, и выполняется всегда.
- В теле структуры while не предусматривается действие, которое со временем приведет к тому, что условие станет ложным. Возникнет «зацикливание» программы, которое может продолжаться бесконечно долго.
- Если для счетчика или переменной суммы не заданы начальные значения, то результат работы вашей программ будет вероятно неправильным.
- Использование чисел с плавающей запятой в предположении, что они совершенно точные, может приводить к некорректным результатам.
- Использование неправильной операции отношения или использование неправильной конечной величины счетчика цикла в условиях структур for или while может привести к ошибке занижения (завышения) на единицу.
- Размещение точки с запятой сразу после правой закрывающей скобки заголовка for делает оператор цикла бессмысленным.
- Пропуск пробела между ключевым словом case и целым значением, которое проверяется в цикле switch. Запись case5: (вместо case 5:) просто создаст неиспользуемую метку, но не будут совершены действия, когда управляющее выражение switch будет равно 5.
Примеры Run-time ошибок:
- Ошибка деления на нуль возникает во время выполнения программы. Приводит к остановке программы с сообщением «Попытка деления на нуль. DivideByZeroException не обработано». В этом случае ошибка считается «неисправимой» (необходимо изменение программы!).
- Пример «исправимой» ошибки: результатом умножения двух положительных целых чисел 1000000 и 1100 будет целое отрицательное число, равное -094967296 (исключение — «переполнение»), при этом программа продолжит свое выполнение после оператора умножения.
Так что подумайте, какой вид ошибок хуже, и как их не делать.
Прочитайте заметки про хороший стиль программирования.
NEW: Наш Чат, в котором вы можете обсудить любые вопросы, идеи, поделиться опытом или связаться с администраторами.
Помощь проекту:
Ключевое различие между Синтаксической и Логической ошибкой состоит в том, что Синтаксическая ошибка возникает из-за ошибки в синтаксисе последовательности символов или знаков, которые предназначены для записи на определенном языке программирования, тогда как Логическая ошибка — это ошибка, возникающая из-за ошибки в алгоритме или логике программы.
При программировании часто возможны ошибки. Ошибка — это непредвиденное выполнение программы. Ошибки влияют на правильное выполнение программы. Таким образом, необходимо удалять все ошибки из программы. Процесс выявления ошибок, а также их исправление — называется отладкой. Любой язык программирования имеет строго определенный синтаксис. Каждый программист обязан следовать правильному синтаксису при написании программы. При возникновении ошибки в синтаксисе программы, эта ошибка называется синтаксической ошибкой. В этом случае во время компиляции появляется синтаксическая ошибка. Ошибки, возникающие во время выполнения программы, называются ошибками среды выполнения (run time errors). Выход за пределы массива, деление на ноль, попытка доступа к недоступной памяти, это некоторые примеры ошибок среды выполнения. При написании программы, существует определённая последовательность шагов, для получения в итоге правильной программы. Эта методология для решения поставленной задачи в программе называется алгоритмом. Если логика работы программы будет неверная, то программа выдаст неверный результат. Такого рода ошибка называется логической ошибкой.
Содержание
- Обзор и основные отличия
- Что такое Синтаксическая ошибка
- Что такое Логическая ошибка
- Сходство между Синтаксической и Логической ошибкой
- Разница между Синтаксической и Логической ошибкой
- Заключение
Что такое Синтаксическая ошибка?
Как правило, программы пишутся с использованием языков программирования высокого уровня. Примерами языков программирования высокого уровня являются: C, Python, Java. Их исходный код легко читается и понятен программистам. Но для компьютера эти программы не являются понятными. Компьютер может понять только машинный код. Таким образом, программа высокого уровня преобразуется в машинный код с помощью специальной программы, называемой компилятором.
Для написания программы каждый язык программирования имеет свой собственный набор синтаксиса. Программисты пишут программы в соответствии с синтаксисом используемого языка программирования. Если нарушить этот синтаксис, то это вызовет ошибку. Этот тип ошибки называется — синтаксической ошибкой и она возникает во время компиляции программы.
Синтаксические ошибки легко определяются и удаляются, так как компилятор отображает расположение её и тип этой ошибки. Во время компиляции, при наличии синтаксических ошибок исходный код не будет переведен в машинный код. И для успешного выполнения компиляции программист должен исправить имеющуюся синтаксическую ошибку, указанную компилятором. Некоторыми распространенными примерами синтаксических ошибок являются: пропущенные точки с запятой, пропущенные фигурные скобки, необъявленные переменные или неправильно написанные ключевые слова и идентификаторы. Если программист только напишет «int x» без точки с запятой, то это будет синтаксической ошибкой. Неправильное написание «int» является синтаксической ошибкой. Поэтому, при написании программы необходимо следовать синтаксису, соответствующему языку программирования. Программа не будет компилироваться до тех пор, пока не будет исправлена в ней синтаксическая ошибка.
Синтаксическая ошибка в интерпретируемом языке обнаруживается во время выполнения программы, из-за этого синтаксические ошибки легче отличить чем другие ошибки.
Что такое Логическая ошибка?
Логическими ошибками называются такие ошибки, которые возникают из-за ошибки алгоритма. Программа с логической ошибкой не приводит к тому, что она завершает выполнение, но сгенерированный результат будет неверным. При появлении синтаксической ошибки, ее можно легко обнаружить, так как компиляция указывает о типе ошибки и строке, в которой происходит эта ошибка.
Но идентифицировать логическую ошибку трудно, так как нет никакого сообщения от компилятора. Вывод результата при этом будет неверен, но сама программа будет выполнена. Поэтому программисту необходимо прочитать каждый оператор и определить ошибку самостоятельно. Примером логической ошибки является неправильное использование операторов. Если программист использовал оператор деления (/) вместо оператора умножения (*), то это логическая ошибка.
Сходство между Синтаксической и Логической ошибкой?
- Синтаксическая ошибка, а так же Логическая ошибка являются категориями ошибок в программировании.
Разница между Синтаксической и Логической ошибкой?
Синтаксические ошибки против Логических ошибок |
|
Синтаксические ошибки — это ошибки в синтаксисе последовательности символов или знаков предназначенных для написания на определенном языке программирования | Логические ошибки — это ошибки в программе, которые заставляют ее работать некорректно, но выполнение программы при этом не прерывается |
Возникновение |
|
Синтаксические ошибки возникают из-за ошибок в синтаксисе программы | Логические ошибки возникают из-за ошибок в алгоритме программы |
Обнаружение |
|
Компилятор сам указывает расположение и тип синтаксической ошибки | Программист сам обнаруживает ошибку |
Простота выявления |
|
Проще выявить синтаксическую ошибку | Труднее выявить логическую ошибку |
Заключение — Синтаксическая ошибка против Логической ошибки
При программировании часто возникают ошибки. Существуют различные типы ошибок. При выполнении программы может возникнуть — ошибка среды выполнения. Примеры ошибок среды выполнения программы — это деление на ноль, попытка доступа к недоступной памяти. Из-за ошибок в синтаксисе возникают — Синтаксические ошибки, тогда как из-за сбоя в логике работы программы возникают Логические ошибки. Разница между Синтаксическими и Логическими ошибками заключается в том, что Синтаксические ошибки возникают из-за ошибки в синтаксисе последовательности символов или знаков, которые предназначены для записи на определенном языке программирования, тогда как Логическая ошибка — это ошибка, которая возникает из-за ошибки в алгоритме программы.
Как искать и исправлять ошибки в коде
Искать ошибки в программах — непростая задача. Здесь нет никаких готовых методик или рецептов успеха. Можно даже сказать, что это — искусство. Тем не менее есть общие советы, которые помогут вам при поиске. В статье описаны основные шаги, которые стоит предпринять, если ваша программа работает некорректно.
Шаг 1: Занесите ошибку в трекер
После выполнения всех описанных ниже шагов может так случиться, что вы будете рвать на себе волосы от безысходности, все еще сидя на работе, когда поймете, что:
- Вы забыли какую-то важную деталь об ошибке, например, в чем она заключалась.
- Вы могли делегировать ее кому-то более опытному.
Трекер поможет вам не потерять нить размышлений и о текущей проблеме, и о той, которую вы временно отложили. А если вы работаете в команде, это поможет делегировать исправление коллеге и держать все обсуждение в одном месте.
Вы должны записать в трекер следующую информацию:
- Что делал пользователь.
- Что он ожидал увидеть.
- Что случилось на самом деле.
Это должно подсказать, как воспроизвести ошибку. Если вы не сможете воспроизвести ее в любое время, ваши шансы исправить ошибку стремятся к нулю.
Шаг 2: Поищите сообщение об ошибке в сети
Если у вас есть сообщение об ошибке, то вам повезло. Или оно будет достаточно информативным, чтобы вы поняли, где и в чем заключается ошибка, или у вас будет готовый запрос для поиска в сети. Не повезло? Тогда переходите к следующему шагу.
Шаг 3: Найдите строку, в которой проявляется ошибка
Если ошибка вызывает падение программы, попробуйте запустить её в IDE под отладчиком и посмотрите, на какой строчке кода она остановится. Совершенно необязательно, что ошибка будет именно в этой строке (см. следующий шаг), но, по крайней мере, это может дать вам информацию о природе бага.
Шаг 4: Найдите точную строку, в которой появилась ошибка
Как только вы найдете строку, в которой проявляется ошибка, вы можете пройти назад по коду, чтобы найти, где она содержится. Иногда это может быть одна и та же строка. Но чаще всего вы обнаружите, что строка, на которой упала программа, ни при чем, а причина ошибки — в неправильных данных, которые появились ранее.
Если вы отслеживаете выполнение программы в отладчике, то вы можете пройтись назад по стектрейсу, чтобы найти ошибку. Если вы находитесь внутри функции, вызванной внутри другой функции, вызванной внутри другой функции, то стектрейс покажет список функций до самой точки входа в программу (функции main() ). Если ошибка случилась где-то в подключаемой библиотеке, предположите, что ошибка все-таки в вашей программе — это случается гораздо чаще. Найдите по стектрейсу, откуда в вашем коде вызывается библиотечная функция, и продолжайте искать.
Шаг 5: Выясните природу ошибки
Ошибки могут проявлять себя по-разному, но большинство из них можно отнести к той или иной категории. Вот наиболее частые.
- Вы начали цикл for с единицы вместо нуля или наоборот. Или, например, подумали, что метод .count() или .length() вернул индекс последнего элемента. Проверьте документацию к языку, чтобы убедиться, что нумерация массивов начинается с нуля или с единицы. Эта ошибка иногда проявляется в виде исключения Index out of range .
- Неправильные настройки или константы
Проверьте ваши конфигурационные файлы и константы. Я однажды потратил ужасные 16 часов, пытаясь понять, почему корзина на сайте с покупками виснет на стадии отправки заказа. Причина оказалась в неправильном значении в /etc/hosts , которое не позволяло приложению найти ip-адрес почтового сервера, что вызывало бесконечный цикл в попытке отправить счет заказчику. - Неожиданный null
Бьюсь об заклад, вы не раз получали ошибку с неинициализированной переменной. Убедитесь, что вы проверяете ссылки на null , особенно при обращении к свойствам по цепочке. Также проверьте случаи, когда возвращаемое из базы данных значение NULL представлено особым типом. - Некорректные входные данные
Вы проверяете вводимые данные? Вы точно не пытаетесь провести арифметические операции с введенными пользователем строками? - Присваивание вместо сравнения
Убедитесь, что вы не написали = вместо == , особенно в C-подобных языках. - Ошибка округления
Это случается, когда вы используете целое вместо Decimal , или float для денежных сумм, или слишком короткое целое (например, пытаетесь записать число большее, чем 2147483647, в 32-битное целое). Кроме того, может случиться так, что ошибка округления проявляется не сразу, а накапливается со временем (т. н. Эффект бабочки). - Переполнение буфера и выход за пределы массива
Проблема номер один в компьютерной безопасности. Вы выделяете память меньшего объема, чем записываемые туда данные. Или пытаетесь обратиться к элементу за пределами массива. - Программисты не умеют считать
Вы используете некорректную формулу. Проверьте, что вы не используете целочисленное деление вместо взятия остатка, или знаете, как перевести рациональную дробь в десятичную и т. д. - Конкатенация строки и числа
Вы ожидаете конкатенации двух строк, но одно из значений — число, и компилятор пытается произвести арифметические вычисления. Попробуйте явно приводить каждое значение к строке. - 33 символа в varchar(32)
Проверяйте данные, передаваемые в INSERT , на совпадение типов. Некоторые БД выбрасывают исключения (как и должны делать), некоторые просто обрезают строку (как MySQL). Недавно я столкнулся с такой ошибкой: программист забыл убрать кавычки из строки перед вставкой в базу данных, и длина строки превысила допустимую как раз на два символа. На поиск бага ушло много времени, потому что заметить две маленькие кавычки было сложно. - Некорректное состояние
Вы пытаетесь выполнить запрос при закрытом соединении или пытаетесь вставить запись в таблицу прежде, чем обновили таблицы, от которых она зависит. - Особенности вашей системы, которых нет у пользователя
Например: в тестовой БД между ID заказа и адресом отношение 1:1, и вы программировали, исходя из этого предположения. Но в работе выясняется, что заказы могут отправляться на один и тот же адрес, и, таким образом, у вас отношение 1:многим.
Ваш процесс или поток пытается использовать результат выполнения дочернего до того, как тот завершил свою работу. Ищите использование sleep() в коде. Возможно, на мощной машине дочерний поток выполняется за миллисекунду, а на менее производительной системе происходят задержки. Используйте правильные способы синхронизации многопоточного кода: мьютексы, семафоры, события и т. д.
Если ваша ошибка не похожа на описанные выше, или вы не можете найти строку, в которой она появилась, переходите к следующему шагу.
Шаг 6: Метод исключения
Если вы не можете найти строку с ошибкой, попробуйте или отключать (комментировать) блоки кода до тех пор, пока ошибка не пропадет, или, используя фреймворк для юнит-тестов, изолируйте отдельные методы и вызывайте их с теми же параметрами, что и в реальном коде.
Попробуйте отключать компоненты системы один за другим, пока не найдете минимальную конфигурацию, которая будет работать. Затем подключайте их обратно по одному, пока ошибка не вернется. Таким образом вы вернетесь на шаг 3.
Шаг 7: Логгируйте все подряд и анализируйте журнал
Пройдитесь по каждому модулю или компоненту и добавьте больше сообщений. Начинайте постепенно, по одному модулю. Анализируйте лог до тех пор, пока не проявится неисправность. Если этого не случилось, добавьте еще сообщений.
Ваша задача состоит в том, чтобы вернуться к шагу 3, обнаружив, где проявляется ошибка. Также это именно тот случай, когда стоит использовать сторонние библиотеки для более тщательного логгирования.
Шаг 8: Исключите влияние железа или платформы
Замените оперативную память, жесткие диски, поменяйте сервер или рабочую станцию. Установите обновления, удалите обновления. Если ошибка пропадет, то причиной было железо, ОС или среда. Вы можете по желанию попробовать этот шаг раньше, так как неполадки в железе часто маскируют ошибки в ПО.
Если ваша программа работает по сети, проверьте свитч, замените кабель или запустите программу в другой сети.
Ради интереса, переключите кабель питания в другую розетку или к другому ИБП. Безумно? Почему бы не попробовать?
Если у вас возникает одна и та же ошибка вне зависимости от среды, то она в вашем коде.
Шаг 9: Обратите внимание на совпадения
- Ошибка появляется всегда в одно и то же время? Проверьте задачи, выполняющиеся по расписанию.
- Ошибка всегда проявляется вместе с чем-то еще, насколько абсурдной ни была бы эта связь? Обращайте внимание на каждую деталь. На каждую. Например, проявляется ли ошибка, когда включен кондиционер? Возможно, из-за этого падает напряжение в сети, что вызывает странные эффекты в железе.
- Есть ли что-то общее у пользователей программы, даже не связанное с ПО? Например, географическое положение (так был найден легендарный баг с письмом за 500 миль).
- Ошибка проявляется, когда другой процесс забирает достаточно большое количество памяти или ресурсов процессора? (Я однажды нашел в этом причину раздражающей проблемы «no trusted connection» с SQL-сервером).
Шаг 10: Обратитесь в техподдержку
Наконец, пора попросить помощи у того, кто знает больше, чем вы. Для этого у вас должно быть хотя бы примерное понимание того, где находится ошибка — в железе, базе данных, компиляторе. Прежде чем писать письмо разработчикам, попробуйте задать вопрос на профильном форуме.
Ошибки есть в операционных системах, компиляторах, фреймворках и библиотеках, и ваша программа может быть действительно корректна. Но шансы привлечь внимание разработчика к этим ошибкам невелики, если вы не сможете предоставить подробный алгоритм их воспроизведения. Дружелюбный разработчик может помочь вам в этом, но чаще всего, если проблему сложно воспроизвести вас просто проигнорируют. К сожалению, это значит, что нужно приложить больше усилий при составлении багрепорта.
Полезные советы (когда ничего не помогает)
- Позовите кого-нибудь еще.
Попросите коллегу поискать ошибку вместе с вами. Возможно, он заметит что-то, что вы упустили. Это можно сделать на любом этапе. - Внимательно просмотрите код.
Я часто нахожу ошибку, просто спокойно просматривая код с начала и прокручивая его в голове. - Рассмотрите случаи, когда код работает, и сравните их с неработающими.
Недавно я обнаружил ошибку, заключавшуюся в том, что когда вводимые данные в XML-формате содержали строку xsi:type=’xs:string’ , все ломалось, но если этой строки не было, все работало корректно. Оказалось, что дополнительный атрибут ломал механизм десериализации. - Идите спать.
Не бойтесь идти домой до того, как исправите ошибку. Ваши способности обратно пропорциональны вашей усталости. Вы просто потратите время и измотаете себя. - Сделайте творческий перерыв.
Творческий перерыв — это когда вы отвлекаетесь от задачи и переключаете внимание на другие вещи. Вы, возможно, замечали, что лучшие идеи приходят в голову в душе или по пути домой. Смена контекста иногда помогает. Сходите пообедать, посмотрите фильм, полистайте интернет или займитесь другой проблемой. - Закройте глаза на некоторые симптомы и сообщения и попробуйте сначала.
Некоторые баги могут влиять друг на друга. Драйвер для dial-up соединения в Windows 95 мог сообщать, что канал занят, при том что вы могли отчетливо слышать звук соединяющегося модема. Если вам приходится держать в голове слишком много симптомов, попробуйте сконцентрироваться только на одном. Исправьте или найдите его причину и переходите к следующему. - Поиграйте в доктора Хауса (только без Викодина).
Соберите всех коллег, ходите по кабинету с тростью, пишите симптомы на доске и бросайте язвительные комментарии. Раз это работает в сериалах, почему бы не попробовать?
Что вам точно не поможет
- Паника
Не надо сразу палить из пушки по воробьям. Некоторые менеджеры начинают паниковать и сразу откатываться, перезагружать сервера и т. п. в надежде, что что-нибудь из этого исправит проблему. Это никогда не работает. Кроме того, это создает еще больше хаоса и увеличивает время, необходимое для поиска ошибки. Делайте только один шаг за раз. Изучите результат. Обдумайте его, а затем переходите к следующей гипотезе. - «Хелп, плиииз!»
Когда вы обращаетесь на форум за советом, вы как минимум должны уже выполнить шаг 3. Никто не захочет или не сможет вам помочь, если вы не предоставите подробное описание проблемы, включая информацию об ОС, железе и участок проблемного кода. Создавайте тему только тогда, когда можете все подробно описать, и придумайте информативное название для нее. - Переход на личности
Если вы думаете, что в ошибке виноват кто-то другой, постарайтесь по крайней мере говорить с ним вежливо. Оскорбления, крики и паника не помогут человеку решить проблему. Даже если у вас в команде не в почете демократия, крики и применение грубой силы не заставят исправления магическим образом появиться.
Ошибка, которую я недавно исправил
Это была загадочная проблема с дублирующимися именами генерируемых файлов. Дальнейшая проверка показала, что у файлов различное содержание. Это было странно, поскольку имена файлов включали дату и время создания в формате yyMMddhhmmss . Шаг 9, совпадения: первый файл был создан в полпятого утра, дубликат генерировался в полпятого вечера того же дня. Совпадение? Нет, поскольку hh в строке формата — это 12-часовой формат времени. Вот оно что! Поменял формат на yyMMddHHmmss , и ошибка исчезла.
Отладка
Отладка, или debugging, — это поиск (локализация), анализ и устранение ошибок в программном обеспечении, которые были найдены во время тестирования.
Виды ошибок
Ошибки компиляции
Это простые ошибки, которые в компилируемых языках программирования выявляет компилятор (программа, которая преобразует текст на языке программирования в набор машинных кодов). Если компилятор показывает несколько ошибок, отладку кода начинают с исправления самой первой, так как она может быть причиной других.
В интерпретируемых языках (например Python) текст программы команда за командой переводится в машинный код и сразу исполняется. К моменту обнаружения ошибки часть программы уже может исполниться.
Ошибки компоновки
Ошибки связаны с разрешением внешних ссылок. Выявляет компоновщик (редактор связей) при объединении модулей программы. Простой пример — ситуация, когда требуется обращение к подпрограмме другого модуля, но при компоновке она не найдена. Ошибки также просто найти и устранить.
Ошибки выполнения (RUNTIME Error)
Ошибки, которые обнаруживают операционная система, аппаратные средства или пользователи при выполнении программы. Они считаются непредсказуемыми и проявляются после успешной компиляции и компоновки. Можно выделить четыре вида проявления таких ошибок:
- сообщение об ошибке, которую зафиксировали схемы контроля машинных команд. Это может быть переполнение разрядной сетки (когда старшие разряды результата операции не помещаются в выделенной области памяти), «деление на ноль», нарушение адресации и другие;
- сообщение об ошибке, которую зафиксировала операционная система. Она же, как правило, и документирует ошибку. Это нарушение защиты памяти, отсутствие файла с заданным именем, попытка записи на устройство, защищенное от записи;
- прекращение работы компьютера или зависание. Это и простые ошибки, которые не требуют перезагрузки компьютера, и более сложные, когда нужно выключать ПК;
- получение результатов, которые отличаются от ожидаемых. Программа работает стабильно, но выдает некорректный результат, который пользователь воспринимает за истину.
Ошибки выполнения можно разделить на три большие группы.
Ошибки определения данных или неверное определение исходных данных. Они могут появиться во время выполнения операций ввода-вывода.
К ним относятся:
- ошибки преобразования;
- ошибки данных;
- ошибки перезаписи.
Как правило, использование специальных технических средств для отладки (API-логгеров, логов операционной системы, профилировщиков и пр.) и программирование с защитой от ошибок помогает обнаружить и решить лишь часть из них.
Логические ошибки. Они могут возникать из ошибок, которые были допущены при выборе методов, разработке алгоритмов, определении структуры данных, кодировании модуля.
В эту группу входят:
- ошибки некорректного использования переменных. Сюда относятся неправильный выбор типов данных, использование индексов, выходящих за пределы определения массивов, использование переменных до присвоения переменной начального значения, нарушения соответствия типов данных;
- ошибки вычислений. Это некорректная работа с переменными, неправильное преобразование типов данных в процессе вычислений;
- ошибки взаимодействия модулей или межмодульного интерфейса. Это нарушение типов и последовательности при передаче параметров, области действия локальных и глобальных переменных, несоблюдение единства единиц измерения формальных и фактических параметров;
- неправильная реализация логики при программировании.
Ошибки накопления погрешностей. Могут возникать при неправильном округлении, игнорировании ограничений разрядной сетки, использовании приближенных методов вычислений и т.д.
Веб-разработчик с нуля
Освойте веб-разработку за 12 месяце и делайте сайты и приложения любой сложности.
Методы отладки программного обеспечения
Метод ручного тестирования
Отладка программы заключается в тестировании вручную с помощью тестового набора, при работе с которым была допущена ошибка. Несмотря на эффективность, метод не получится использовать для больших программ или программ со сложными вычислениями. Ручное тестирование применяется как составная часть других методов отладки.
Метод индукции
В основе отладки системы — тщательный анализ проявлений ошибки. Это могут быть сообщения об ошибке или неверные результаты вычислений. Например, если во время выполнения программы завис компьютер, то, чтобы найти фрагмент проявления ошибки, нужно проанализировать последние действия пользователя. На этапе отладки программы строятся гипотезы, каждая из них проверяется. Если гипотеза подтвердилась, информация об ошибке детализируется, если нет — выдвигаются новые.
Вот как выглядит процесс:
Важно, чтобы выдвинутая гипотеза объясняла все проявления ошибки. Если объясняется только их часть, то либо гипотеза неверна, либо ошибок несколько.
Метод дедукции
Сначала специалисты предлагают множество причин, по которым могла возникнуть ошибка. Затем анализируют их, исключают противоречащие имеющимся данным. Если все причины были исключены, проводят дополнительное тестирование. В обратном случае наиболее вероятную причину пытаются доказать.
Метод обратного прослеживания
Эффективен для небольших программ. Начинается с точки вывода неправильного результата. Для точки выдвигается гипотеза о значениях основных переменных, которые могли привести к ошибке. Далее на основании этой гипотезы строятся предположения о значениях переменных в предыдущей точке. Процесс продолжается до момента, пока не найдут ошибку.
Как выполняется отладка в современных IDE
Ранние отладчики, например gdb, представляли собой отдельные программы с интерфейсами командной строки. Более поздние, например первые версии Turbo Debugger, были автономными, но имели собственный графический интерфейс для облегчения работы. Сейчас большинство IDE имеют встроенный отладчик. Он использует такой же интерфейс, как и редактор кода, поэтому можно выполнять отладку в той же среде, которая используется для написания кода.
Отладчик позволяет разработчику контролировать выполнение и проверять (или изменять) состояние программ. Например, можно использовать отладчик для построчного выполнения программы, проверяя по ходу значения переменных. Сравнение фактических и ожидаемых значений переменных или наблюдение за ходом выполнения кода может помочь в отслеживании логических (семантических) ошибок.
Пошаговое выполнение — это набор связанных функций отладчика, позволяющих поэтапно выполнять код.
Шаг с заходом (step into)
Команда выполняет очередную инструкцию, а потом приостанавливает процесс, чтобы с помощью отладчика было можно проверить состояние программы. Если в выполняемом операторе есть вызов функции, step into заставляет программу переходить в начало вызываемой функции, где она приостанавливается.
Шаг с обходом (step over)
Команда также выполняет очередную инструкцию. Однако когда step into будет входить в вызовы функций и выполнять их строка за строкой, step over выполнит всю функцию, не останавливаясь, и вернет управление после ее выполнения. Команда step over позволяет пропустить функции, если разработчик уверен, что они уже исправлены, или не заинтересован в их отладке в данный момент.
Шаг с выходом (step out)
В отличие от step into и step over, step out выполняет не следующую строку кода, а весь оставшийся код функции, исполняемой в настоящее время. После возврата из функции он возвращает управление разработчику. Эта команда полезна, когда специалист случайно вошел в функцию, которую не нужно отлаживать.
Как правило, при пошаговом выполнении можно идти только вперед. Поэтому легко перешагнуть место, которое нужно проверить. Если это произошло, необходимо перезапустить отладку.
У некоторых отладчиков (таких как GDB 7.0, Visual Studio Enterprise Edition 15.5 и более поздних версий) есть возможность вернуться на шаг назад. Это полезно, если пропущена цель либо нужно повторно проверить выполненную инструкцию.
Веб-разработчик с нуля
За 12 месяцев вы освоите базовую верстку, frontend и backend. В конце обучения у вас будет готовое портфолио из проектов.
Виды ошибок в программах
Я учусь на своих ошибках. Ругаю себя за это, но продолжаю ошибаться. С другой стороны — это всё-таки лучше, чем не учиться совсем, и наступать на одни и те же грабли бесконечно.
При создании программ, даже простых, ошибки неизбежны. Поэтому для поиска ошибок во всех средствах разработки имеются особые инструменты для отладки. Но сегодня не об отладке и не о поиске ошибок. Сегодня о видах ошибок, которые встречаются в программах.
Итак, основных вида всего три:
Синтаксические ошибки в программах
Эти ошибки довольно распространены, особенно среди начинающих. Но эти ошибки — самые безобидные. Потому что компиляторы легко находят ошибки синтаксиса и указывают место в исходном коде, где обнаружена такая ошибка. Программисту остаётся только исправить её.
Синтаксические ошибки — это ошибки синтаксиса (а то бы вы не догадались))). То есть ошибки правил языка. Например, для Паскаля это будет синтаксической ошибкой:
Потому что после первой строки нет точки с запятой.
Подобные ошибки очень часто совершают новички. И это вгоняет их в ступор — они пугаются и не могут понять, что же не так с их кодом. Хотя если бы они внимательно его посмотрели и прочитали сообщение об ошибке, то легко могли бы исправить её:
Потому что в сообщении чётко сказано:
что можно перевести как
То есть компилятор говорит нам: я ожидал увидеть точку с запятой, а нашёл идентификатор READLN .
Логические ошибки в программах
Это самые противные и самые труднонаходимые ошибки. Программа может быть написана совершенно правильно с точки зрения синтаксиса языка, и при этом она будет неправильно работать. Потому что программист допустил где-то логическую ошибку.
И компилятор вам ничего об этой ошибке не расскажет, потому что правила языка не нарушены.
Поиски таких ошибок могут занять много времени и отнять у вас немало здоровья. Поэтому при разработке программ лучше не торопиться и стараться не допускать логических ошибок.
Пример логической ошибки:
Здесь мы сравниваем значение i с числом 15, и выводим сообщение, если i = 15 . Но фишка в том, что в данном цикле i не будет равно 15 НИКОГДА, потому что в цикле переменной i присваиваются значения от 1 до 10.
Эта ошибка довольно безобидная. Здесь мы имеем просто бессмысленный код, который не причинит никакого вреда. Однако представьте, что программа должна выдавать какой-то сигнал тревоги, если i = 15 . Тогда получится, что никакого сигнала пользователь никогда не услышит, даже если случилось что-то страшное. А всё потому, что программист немного ошибся. Вот так вот и падают ракеты и самолёты…
Распространённые логические ошибки в С++ вы можете посмотреть здесь.
Ошибки времени выполнения программы
Даже если исходный код не содержит ни логических, не синтаксических ошибок, это ещё не означает, что ваша программа безупречна. Потому что ошибки могут возникнуть в ходе выполнения программы. Например, случайно будет удалён файл, который должна читать программа, и она не сможет его найти. Если не принять мер, то программа может завершиться аварийно. А пользователям такое поведение программ очень не нравится.
Одна из самых рапространённых ошибок времени выполнения — это неожиданное деление на ноль. Пример:
Что здесь такого? Всё правильно и с точки зрения логики, и с точки зрения синтаксиса. И в большинстве случаев программа отработает без каких-либо неожиданностей.
Но представьте, что пользователь введёт ноль. Что тогда будет? Правильно — попытка деления на ноль. А на ноль делить нельзя. Поэтому во время выполнения этой программы произойдёт ошибка, которая очень расстроит пользователя. Потому что в случае, например, с консольным приложением программа просто закроется, и пользователь не поймёт, что это было. Но зато поймёт, что программа — говно, и программы от этого разработчика лучше больше никогда не использовать.
В данном случае, если вы не уверены на 100%, что y будет отличаться от нуля, надо всегда делать проверку на ноль. И хороший код должен быть хотя бы таким:
Ну что же. На этом с видами ошибок пока всё. Изучайте программирование и поменьше ошибайтесь.