Podstawowa różnica między C i Javą polega na tym, że jeśli uniknie się pewnych łatwych do zidentyfikowania cech Javy (np. W Unsafe
przestrzeni nazw), każda możliwa akcja, którą można podjąć - w tym „błędne” - będzie miała ograniczony zakres możliwych wyników . Wprawdzie ogranicza to to, co można zrobić w Javie - przynajmniej bez korzystania z Unsafe
przestrzeni nazw, ale umożliwia także ograniczenie szkód, które mogą być spowodowane przez błędny program lub - co ważniejsze - przez program, który poprawnie przetworzyłby prawidłowe pliki, ale nie są szczególnie chronione przed błędami.
Tradycyjnie kompilatory C przetwarzałyby wiele akcji w sposób zdefiniowany w standardzie w „normalnych” przypadkach, podczas gdy przetwarzałyby wiele przypadków narożnych „w sposób charakterystyczny dla środowiska”. Jeśli ktoś używałby procesora, który zwarłby się i zapaliłby, gdyby nastąpiło przepełnienie numeryczne, i chciałby uniknąć zapłonu procesora, musiałby napisać kod, aby uniknąć przepełnienia numerycznego. Gdyby jednak ktoś używał procesora, który idealnie szczęśliwie obcinałby wartości w uzupełnieniu do dwóch, nie trzeba unikać przepełnień w przypadkach, w których takie obcięcie skutkowałoby akceptowalnym zachowaniem.
Nowoczesne C idzie o krok dalej: nawet jeśli ktoś celuje w platformę, która w naturalny sposób zdefiniowałaby zachowanie czegoś takiego jak przepełnienie numeryczne, w którym Standard nie nakładałby żadnych wymagań, przepełnienie jednej części programu może wpłynąć na zachowanie innych części programować w sposób arbitralny, niezwiązany prawami czasu i przyczynowości. Rozważmy na przykład coś takiego:
uint32_t test(uint16_t x)
{
if (x < 50000) foo(x);
return x*x; // Note x will promote to "int" if that type is >16 bits.
}
„Nowoczesny” kompilator języka C otrzymujący coś takiego jak powyższy może dojść do wniosku, że ponieważ obliczenia x * x przepełniłyby się, gdyby x był większy niż 46340, może bezwarunkowo wykonać wywołanie „foo”. Zauważ, że nawet jeśli akceptowalne byłoby nienormalne zakończenie działania programu, jeśli x jest poza zakresem, lub funkcja zwraca jakąkolwiek wartość w takich przypadkach, wywołanie foo () z poza zakresem x może spowodować szkody daleko poza jedna z tych możliwości. Tradycyjne C nie zapewniałoby żadnego sprzętu zabezpieczającego poza tym, co dostarczał programista i platforma, ale pozwoliłoby, aby sprzęt zabezpieczający ograniczył szkody w nieoczekiwanych sytuacjach. Nowoczesne C ominie każdy sprzęt bezpieczeństwa, który nie jest w 100% skuteczny w utrzymywaniu wszystkiego pod kontrolą.