Во-первых, я отмечу, что хотя я упоминаю здесь только «C», то же самое в равной степени относится и к C ++.
Комментарий, в котором упоминался Годель, был частично (но только частично) точным.
Когда вы приступаете к этому, неопределенное поведение в стандартах C в значительной степени просто указывает на границу между тем, что стандарт пытается определить, и тем, что он не делает.
Теоремы Гёделя (их две) в основном говорят о том, что невозможно определить математическую систему, которая может быть доказана (по своим собственным правилам) как полной и последовательной. Вы можете сделать свои правила так, чтобы они были полными (дело, с которым он имел дело, были «нормальными» правилами для натуральных чисел), или вы можете сделать возможным доказать их непротиворечивость, но вы не можете иметь и то и другое.
В случае чего-то вроде C, это не относится напрямую - по большей части «доказуемость» полноты или согласованности системы не является первоочередной задачей для большинства разработчиков языков. В то же время, да, они, вероятно, находились под влиянием (по крайней мере, в некоторой степени), зная, что невозможно доказать «идеальную» систему - ту, которая доказуемо полная и последовательная. Знание того, что такое невозможно, возможно, сделало бы немного легче отступить назад, немного вздохнуть и принять решение о границах того, что они будут пытаться определить.
Рискуя (опять же) быть обвиненным в высокомерии, я бы охарактеризовал стандарт C как управляемый (частично) двумя основными идеями:
- Язык должен поддерживать как можно более широкое разнообразие аппаратного обеспечения (в идеале, все «вменяемое» оборудование до некоторого разумного нижнего предела).
- Язык должен поддерживать написание как можно более широкого спектра программного обеспечения для данной среды.
Первое означает, что если кто-то определит новый ЦП, то должна быть возможность обеспечить хорошую, надежную и пригодную для использования реализацию C для этого, если дизайн хотя бы достаточно близок к нескольким простым рекомендациям - в основном, если он следует за общим порядком модели фон Неймана и обеспечивает по крайней мере некоторый разумный минимальный объем памяти, которого должно быть достаточно для реализации на языке Си. Для «размещенной» реализации (той, которая выполняется в ОС) вам необходимо поддерживать некоторое понятие, которое достаточно близко соответствует файлам, и иметь набор символов с определенным минимальным набором символов (требуется 91).
Второе означает, что должна быть возможность писать код, который напрямую манипулирует аппаратным обеспечением, поэтому вы можете писать такие вещи, как загрузчики, операционные системы, встроенное программное обеспечение, которое работает без какой-либо ОС и т. Д. В конечном счете, в этом отношении существуют некоторые ограничения, так что почти любой Практическая операционная система, загрузчик и т. д., скорее всего, содержат хотя бы немного кода, написанного на ассемблере. Аналогично, даже небольшая встроенная система, вероятно, будет включать в себя, по крайней мере, какие-то предварительно написанные библиотечные процедуры для предоставления доступа к устройствам в хост-системе. Хотя точную границу трудно определить, цель состоит в том, чтобы зависимость от такого кода была сведена к минимуму.
Неопределенное поведение в языке в значительной степени обусловлено намерением языка поддерживать эти возможности. Например, язык позволяет вам конвертировать произвольное целое число в указатель и получать доступ к тому, что происходит по этому адресу. Стандарт не пытается сказать, что произойдет, когда вы это сделаете (например, даже чтение с некоторых адресов может иметь внешне видимые эффекты). В то же время, он не пытается помешать вам делать такие вещи, потому что вам нужно для некоторых видов программного обеспечения, которые вы должны писать на C.
Существует некоторое неопределенное поведение, обусловленное и другими элементами дизайна. Например, еще одна цель C - поддерживать отдельную компиляцию. Это означает (например), что предполагается, что вы можете «связать» части вместе, используя линкер, который примерно соответствует тому, что большинство из нас считает обычной моделью линкера. В частности, должна быть возможность объединить отдельно скомпилированные модули в единую программу без знания семантики языка.
Существует еще один тип неопределенного поведения (это гораздо чаще встречается в C ++, чем в C), который присутствует просто из-за ограничений технологии компилятора - вещи, которые мы в основном знаем, являются ошибками и, вероятно, хотели бы, чтобы компилятор диагностировал их как ошибки, но учитывая текущие ограничения на технологию компиляции, сомнительно, что они могут быть диагностированы при любых обстоятельствах. Многие из них основаны на других требованиях, таких как отдельная компиляция, так что это в значительной степени вопрос балансировки противоречивых требований, и в этом случае комитет обычно предпочитает поддерживать более широкие возможности, даже если это означает отсутствие диагностики некоторых возможных проблем, вместо того, чтобы ограничивать возможности, чтобы гарантировать, что все возможные проблемы диагностированы.
Эти различия в намерениях определяют большинство различий между C и чем-то вроде Java или системами на основе CLI от Microsoft. Последние довольно явно ограничены работой с гораздо более ограниченным набором оборудования или требованием программного обеспечения для эмуляции более конкретного оборудования, на которое они нацелены. Они также специально намерены предотвратить любые прямые манипуляции с оборудованием, вместо этого требуя, чтобы вы использовали что-то вроде JNI или P / Invoke (и код, написанный на чем-то вроде C), чтобы даже предпринять такую попытку.
Возвращаясь к теоремам Годеля на минуту, мы можем провести нечто вроде параллели: Java и CLI выбрали «внутренне согласованную» альтернативу, а C - «полную» альтернативу. Конечно, это очень грубая аналогия - я сомневаюсь , что кто -то пытается - х формальное доказательство либо внутренней согласованности или полноты в любом случае. Тем не менее, общее понятие довольно близко соответствует выбору, который они приняли.