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

Вот пример кода на языке 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
В Python поддерживаются отрицательные индексы и нумерация идет с конца массива.
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.
Выводы
При написании кода на любом языке программирования нужно учитывать особенности работы со структурами данных в языке. Также учесть все места в коде, где программа может "упасть". Часто после исключения программный код уже невозможно вернуть на строку возникновения ошибки, а ниже по коду могут выполняться важные действия. Поэтому не все языки подходят для решения определенных задач или требуют дополнительных проверок и ветвлений кода. В языках стандарта МЭК есть защита от примитивных программных ошибок, так как зачастую перегрузить программу контроллера при сбое некому и эта операция также может оказаться фатальной в некоторых ситуациях.