Pointer arithmetic, I imagine. Elementary math after all.
With unsigned integers, type aliases and pointer types this would have been a lot easier to keep track of. The limited range of primitive integers also means static analysis is basically impossible. Assing a pointer to an integer or vice versa and that's a code smell. Assign an integer to an integer and the compiler is none the wiser.
You say that the lack of unsigned types in Java is in part responsible for some of your wasted time. But C has notoriously bad integer types and promotion rules, especially for interactions between signed and unsigned! If you had found this same bug in C code (a compiler bug), your first instinct as a C programmer might have been "it's probably some signed/unsigned interaction issue". And you'd also have wasted time trying to find a non-existent issue in your code.
(also, not entirely sure what you meant. There isn't a canonical C compiler, or toolset, and the language itself really doesn't do anything to prevent you from shooting yourself in the foot)
Well, that or JNI/FFI in some C code I guess.
Also, Kotlin has unsigned integers. So you could use that for the parts of your code that benefit from it.
What I'm building is what Lucene does (i.e. document indexing) and then the rest of the search engine as well including crawling and serving traffic.
I previously worked at a search engine which did its index building and querying in C, with its higher-level stuff (web-apps, scheduling, tooling, etc.) in Java.
Later when I built my own version, I started with C for the low-level and Haskell for the high-level. I made a few iterations in C, but eventually rewrote it in Rust, and I was pretty happy with that choice.
I was more familiar with C, and it was a really good fit for what I was writing. Terms and Documents just become termIds and docIds (numbers) sitting in indexes (arrays). Memory-mapping is a really comfortable way to do things: files are just arrays; let the OS sort out when to page things in and out of disk.
But where C fell down for me was in the changing the code. The meaning of the data&code were lost in nested for-loops and void-function-pointers, etc. Rust gave me a better shot at both writing and rewriting the code.
Java for the low-level was a non-starter for me for a few reasons, but the biggest two were startup time and difficulty of the mmap api (31-bits of address-space for a file? c'mon!).
Neither of these are issues anymore though. Especially not with graal's native images.