По-перше, зазначу, що хоча я тут згадую лише "C", те ж саме стосується C ++.
Коментар, що згадував Годеля, був частково (але лише частково) суттєвим.
Коли ви переходите до цього, невизначена поведінка у стандартах С значною мірою лише вказує на межу між тим, що стандарт намагається визначити, і тим, що він не робить.
Теореми Годеля (їх дві) в основному говорять, що неможливо визначити математичну систему, яка може бути доведена (за власними правилами) як повною, так і послідовною. Ви можете зробити свої правила таким чином, щоб вони були повноцінними (справа, якою він займався, - це "звичайні" правила для натуральних чисел), інакше ви зможете довести свою послідовність, але ви не можете мати обох.
У випадку чогось типу C, це не застосовується безпосередньо - здебільшого "доцільність" повноти чи послідовності системи не є першочерговим завданням для більшості мовних дизайнерів. У той же час, так, на них, мабуть, вплинули (хоча б певною мірою), усвідомлюючи, що визначити "ідеальну" систему - таку, яка доказів є повною та послідовною, неможливо. Знаючи, що таке неможливо, можливо, це полегшило крок назад, трохи подихніть і визначитесь із межею того, що вони спробують визначити.
Загрожуючи звинуватити в зарозумілості (ще раз), я б охарактеризував стандарт С таким, що керується (частково) двома основними ідеями:
- Мова повинна підтримувати якомога більше різноманітних апаратних засобів (в ідеалі, все "розумне" обладнання до нижньої межі).
- Мова повинна підтримувати написання якомога більшого різноманіття програмного забезпечення для даного середовища.
Перший означає, що якщо хтось визначає новий процесор, для цього слід забезпечити хорошу, міцну, зручну реалізацію C, якщо дизайн принаймні розумно наближається до кількох простих рекомендацій - в основному, якщо це дотримується чогось із загального порядку моделі Фон Неймана і забезпечує принаймні якийсь розумний мінімальний об'єм пам'яті, якого повинно бути достатньо, щоб дозволити реалізацію C. Для "розміщеної" реалізації (тієї, яка працює на ОС) вам потрібно підтримати певне поняття, яке досить близько відповідає файлам, і мати набір символів з певним мінімальним набором символів (потрібно 91).
Другий означає, що має бути можливість запису коду, який безпосередньо керує обладнанням, щоб ви могли писати такі речі, як завантажувачі, операційні системи, вбудоване програмне забезпечення, яке працює без будь-якої ОС тощо. Зрештою, в цьому відношенні є деякі обмеження, тому майже будь-які практична операційна система, завантажувач і т.д., швидше за все, містить , по крайней мере, трохи трохи коду , написаного на мові асемблера. Аналогічно, навіть невелика вбудована система, ймовірно, включає хоча б якусь заздалегідь записану підпрограму бібліотеки, щоб надати доступ до пристроїв хост-системи. Хоча точну межу важко визначити, наміром є те, що залежність від такого коду має бути зведена до мінімуму.
Невизначена поведінка в мові значною мірою обумовлена наміром мови підтримувати ці можливості. Наприклад, мова дозволяє перетворити довільне ціле число в покажчик і отримати доступ до того, що трапиться за цією адресою. Стандарт не намагається сказати, що станеться, коли ви це зробите (наприклад, навіть читання з деяких адрес може мати зовнішні видимі впливи). У той же час, вона не робить спроб заважати вам робити такі речі, тому що вам потрібно для деяких видів програмного забезпечення, яке ви повинні мати змогу писати на C.
Існує певна невизначена поведінка, керована й іншими елементами дизайну. Наприклад, ще одна мета C - підтримувати окрему компіляцію. Це означає (наприклад), що передбачається, що ви можете "зв'язати" частини разом, використовуючи лінкер, який приблизно відповідає тому, що більшість із нас бачить як звичайну модель лінкера. Зокрема, має бути можливість об'єднати окремо складені модулі в повну програму без знання семантики мови.
Існує ще один тип невизначеної поведінки (що набагато частіше зустрічається в C ++, ніж у C), який існує просто через обмеження технології компілятора - речі, які ми в основному знаємо, є помилками, і, ймовірно, хотіли б компілятору діагностувати як помилки, але враховуючи нинішні обмеження на технології компілятора, сумнівно, що їх можна було б діагностувати за будь-яких обставин. Багато з них обумовлені іншими вимогами, такими як окрема компіляція, тому багато в чому полягає в балансуванні суперечливих вимог. У цьому випадку комітет, як правило, вирішив підтримати більші можливості, навіть якщо це означає відсутність діагностики деяких можливих проблем, замість обмеження можливостей для забезпечення діагностики всіх можливих проблем.
Ці відмінності у намірах сприяють більшості відмінностей між C та чимось на кшталт Java або систем на базі CLI на базі Microsoft. Останні досить чітко обмежені роботою зі значно обмеженим набором апаратних засобів або потребують програмного забезпечення для емуляції більш конкретного обладнання, на яке вони націлені. Вони також спеціально мають намір не допустити будь-яких прямих маніпуляцій з обладнанням, замість цього вимагають використовувати щось на зразок JNI або P / Invoke (і код, записаний у чомусь на зразок C), щоб навіть зробити таку спробу.
Повернувшись на мить до теорем Годеля, ми можемо провести щось паралельне: Java та CLI обрали альтернативу "внутрішньо послідовної", тоді як C обрала "альтернативу". Звичайно, це дуже груба аналогія - я сумніваюся, що хтось намагається офіційно підтвердити або внутрішню узгодженість, або повноту в будь-якому випадку. Тим не менше, загальне поняття цілком відповідає тій вибір, яку вони прийняли.