Строго говоря, код в примерах не очень корректный. Увы, подобные перлы действительно перестают работать в результате деятельности оптимизатора, но это проблема кода, а не оптимизатора.Полукорректная проверка переполнения при арифметике с указателями. Да, проверка переполнения, конечно, хорошая вещь, но ТАК это не надо делать. Поведение этого кода при переполнении неизвестно. Соответственно, это код под одну конкретную платформу, включая в понятие платформы и компилятор. Под то окружение, где работает так, как рассчитывал автор кода. И вполне естественно, что с развитием компилятора и появлением в нём более серьёзных оптимизаций ситуация изменилась. Код перестал работать так, как работал. Увы и ах. Ну так просто писать это надо подумавши. Посчитать, например, длину буфера вычитанием указателей и проверить, что индекс в диапазоне. А не заниматься не переносимым сложением с расчётом на переполнение, результат которого неопределён.
Ну и в переводе ошибка. Это не tun->sk не может быть нулевым, это tun не может быть нулевым. Кстати, в этом примере трудно не согласиться с оптимизатором! Если строкой выше идёт доступ к члену структуры, то указатель просто не имеет права быть нулевым. Проверять надо до доступа. Глючный код -- неопределённый результат, в чём удивление? Тот, кто этот код писал, видел за кодом арифметику с указателями на уровне ассемблерных команд. Это ценное умение. Но одновременно -- отвратительная привычка, если сопровождается тем, что программист рассчитывает на определённую низкоуровневую реализацию. Так он просто не сможет писать переносимый код.
Рациональное зерно, правда, в том, что кривоватый код, который ранее работал безупречно, может перестать это делать с включением оптимизации или заменой версии компилятора. Но ведь это ожидаемо и очевидно, нет?