Потребовалось переписать код с одного языка на другой, и возникла странная ситуация. Одинаковый с виду листинг ведет себя совершенно по-разному. В виду того, что кода действительно много, ошибку очень сложно заметить. На примере данной ситуации порассуждаем, насколько разные языки программирования?!
Вот пример кода на языке Structured Text, который применяется для программирования промышленных контроллеров. Программы на этом языке могут выполнять различные действия, от управления светофором до контроля процессов на атомной электростанции. Специфика языка и платформы соблюдена, так что код может выглядеть достаточно непривычно.
VAR
i: INT;
OUT: INT;
IN: ARRAY [0..4] of INT:= 1, 2, 3, 4, 5;
END_VAR
OUT := 0;
FOR i:= 0 TO 4 DO
OUT := OUT + IN[i];
END_FOR;
Что делает эта программа? Читает данные с входных сигналов и записывает управляющее воздействие в переменную OUT. Значение этой переменной будет 15. Теперь внесем некоторую ошибку в программу, а конкретно взятие элемента по несуществующему индексу в массиве и посмотрим на результат.
VAR
i: INT;
OUT: INT;
IN: ARRAY [0..4] of INT:= 1, 2, 3, 4, 5;
END_VAR
OUT := 0;
FOR i:= -1 TO 4 DO
OUT := OUT + IN[i];
END_FOR;
Значение в переменной OUT = 15
То есть такой же, как и до ошибки. Программа не бросила исключение, а просто проигнорировала несуществующий элемент. Такое поведение для нас вполне приемлемо, если мы случайно не обработали исключение и управляем аварийными стержнями ядерного реактора. Управляющее воздействие переменной OUT будет таким же, как и до ошибочного изменения индекса. Если это кратковременный сбой, то система не отреагирует на ошибку и будет дальше стабильно продолжать работать. Теперь представим, что эту же задачу выполняют разные программы на других языках программирования и сравним результаты.
Код без ошибочного индекса выполняется одинаково на всех языках программирования и в переменной OUT всегда значение 15. Будем рассматривать только тот код, где закралась ошибка со значением стартового индекса равном -1.
JavaScript
var IN = [ 1, 2, 3, 4, 5 ];
var OUT = 0;
for (var i = -1; i <= 4; i++) {
OUT += IN[i];
}
console.log('OUT = '+ OUT);
OUT = NaN
Go
package main
import "fmt"
func main() {
IN := [5]int{ 1, 2, 3, 4, 5 }
OUT := 0
for i := -1; i <= 4; i++ {
OUT += IN[i]
}
fmt.Printf("OUT = %d", OUT)
}
panic: runtime error: index out of range
Java
public class MyClass {
public static void main(String args[]) {
int[] IN = {1, 2, 3, 4, 5};
int OUT = 0;
for (int i = -1; i <= 4; i++)
OUT += IN[i];
System.out.printf("OUT = %d", OUT);
}
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
PHP
<?php
$IN = [1, 2, 3, 4, 5];
$OUT = 0;
for ($i=-1; $i<=4; $i++) {
$OUT += $IN[$i];
}
echo('OUT = '.$OUT);
OUT = 15
PHP Notice: Undefined offset: -1
Python 3
IN = [ 1, 2, 3, 4, 5 ];
OUT = 0;
for i in range(-1, 5):
OUT += IN[i];
print('OUT = {0:1d}'.format(OUT));
OUT = 20
C/C++
#include<stdio.h>
int main() {
int IN[]= {1,2,3,4,5};
int OUT=0;
int i;
for (i=-1; i<=4; i++) {
OUT += IN[i];
}
printf("OUT = %i", OUT);
return 0;
}
С языком С++ отдельная история. Если вы захотите проверить данный пример на популярных сайтах, то получите вот такие результаты:
OUT = -143484461
https://ideone.com/ и др.
OUT = 15
OUT = 14
В рамках этой статьи я не буду вдаваться в подробности, какие компиляторы С/С++ на каких платформах используют эти сайты. Буду очень рад, если вы поделитесь своим мнением в комментариях.
C Sharp
using System;
class Program
{
static void Main()
{
int[] IN = new int[] { 1, 2, 3, 4, 5 };
int OUT = 0;
for (int i = -1; i <= 4; i++)
{
OUT += IN[i];
}
Console.Write("OUT of IN + y = "+ OUT);
}
}
Unhandled Exception:
System.IndexOutOfRangeException: Index was outside the bounds of the array.
Выводы
При написании кода на любом языке программирования нужно учитывать особенности работы со структурами данных в языке. Также учесть все места в коде, где программа может "упасть". Часто после исключения программный код уже невозможно вернуть на строку возникновения ошибки, а ниже по коду могут выполняться важные действия. Поэтому не все языки подходят для решения ответственных задач или требуют дополнительных проверок и ветвлений кода.
Автор: all_IT