More seriously, the core issue is that [Obj-]C[++] conflates a pointer to an object (in the memory sense) with a pointer to an array of many of those objects. As a result they have to transfer counts/sizes independently, null terminate, or be hopeful, etc. Sensible languages chose to actually distinguish address of object from array of object, and as a result they can have a different structure. Technically C++ does this (new T[...] gives you a pointer to memory that has a preceding count) but because it interoperates with C transparently you don't know if a given T* is a C array (no length), a C++ array (has a length), or an individual object (no length). On the plus side that means you can pass a tuple of "address of a local variable" and '1' to a function without heap allocating an array, on the down side gestures at the software security landscape.
Similarly the problem with other previous attempts to add bounds checking to C is that they didn't consider "must be able to be adopted by system frameworks without breaking existing ABI", which is what this attempts to do. The caveat is that it does have to include `unsafe` annotations and builtins to deal with APIs that don't have any actual bounds information (a classic example might be something like `gets(char*)`).
Everyone does basically agree with you: the key safety of the modern "safe" system languages is that "memory is just a bag of bits" is not safe. Hence the strict typing, strict maintenance of lifetime (GC, refcounting, lifetime's as part of the type system), weird concepts like bounds checking (:D). Doing this at the actual hardware level (as you're implying with registers) doesn't really work - there's too much variation in how languages work, how languages work changes over time, and it's also extremely expensive - in cpu time, memory overhead, and silicon area. Organizations have tried it in the past and I don't recall any actually being successful, except as examples of why "CISC is bad" :D