Per ottenere una migliore comprensione di quali lingue come Java ti comprano in termini di sicurezza e gestione della memoria, lavora in C. L'unica cosa che guiderà il punto a casa più in profondità sarà lavorare in assembler¹. Anche se non assomiglia a ciò che Java fornisce, C ++ ti offre troppi strumenti per gestire il tipo di complessità che causa questi problemi, e la tentazione di usarli sarà troppo grande.
La gestione della memoria in C è molto laboriosa rispetto a Java e C ++. Non hai alcun tipo di garbage collection automatico, non hai distruttori espliciti per liberare risorse quando un oggetto esce dall'ambito, quindi devi tracciare manualmente ogni allocazione di memoria e abbinarlo con una deallocazione corrispondente. Gli array non sanno quanto sono grandi e non ci sono limiti di controllo, quindi devi tenere traccia di tutte queste dimensioni tampone e applicare manualmente quei limiti. C non fornisce molto nel modo di gestire le eccezioni (i segnali e setjmp/longjmp
ne sono l'estensione, e questo non è proprio niente), quindi devi stare attento a come gestisci le condizioni di errore.
Se vuoi davvero apprezzare ciò che Java (o anche C ++) ti compra, implementa una libreria C che imita il comportamento della classe String
di Java.
¹ Il meglio dei due mondi sarebbe lavorare in C e fare in modo che il compilatore scriva l'assembly generato in un file di output e lo studi.