It's simple. Don't write such code.
It's prone to errors just like you want to check for. That's why modern c++ has invented for example std::span. A span represents a contiguous sequence of objects just like your pointer + size. But the pointer and size are not separate values that you can easily mix up in function calls. It's a single object containing both.
Actually if the size is known at compile time it gets encoded in the type itself and the span only contains a pointer making it smaller and allowing for better optimization of the function.
Beyond that you have to trust the users of your function don't lie to you. There is no good way to validate a pointer, a problem that hunts garbage collectors, and it's best to just let the OS tell you when the pointer is total garbage by producing a segfault.
Note: Using span instead of array makes is even simpler than passing pointer + size:
#include <span>
int get(std::span<int> x, size_t i) {
return x[i];
}
int main() {
int x[] = {2, 3, 5, 7, 11};
return get(x, 2);
}
A std::span
can be constructed from an array of known size so the compiler does all the work for you.