Neither C nor C++ allows you to derive pointers pointing outside the original object (as in, memory allocation), it's UB. Yes, the arithmetic operation itself has UB, not merely later derefering an OoB pointer. The only exception is one-past-the-end pointers which are safe to create (but not to dereference of course).
Whether it's safe to "smuggle" pointers around as ints, without doing any arithmetic on them, is "complicated" and honestly there's no one single answer as far as I know.
Pointer provenance is not explicitly present in the C standard. There is a TS, TS 6010, that introduces a pointer provenance model that is fairly close to the effective model of most compilers. Prior to that discussion, compilers have documented a vague model of "pointers need to be based on their original address," but these vague models are usually poorly upheld by the actual compiler [1].
C++ inherits C's failure to discuss pointer provenance, and tacks on an additional complication that it tries to reason about the value of pointers to objects outside their lifetimes. The current thinking on the C++ committee is to give up and go with an "angelic nondeterminism" model, i.e., if there is some possible way to give the program well-defined semantics, that is the semantics of the program. (See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p24... for the current proposal).
[1] In more detail, the principal problem is pointer <-> integer conversions. The compiler is usually thinking of provenance in terms of escaping pointers--there is a use or a source that the compiler cannot definitively track down--and converting a pointer to an integer is an operation that causes the pointer to escape. But the compiler also tends to optimize integers in a way that doesn't preserve provenance (i.e., if x == y, the compiler will freely rewrite a use of x to a use of y or vice versa, even though the two need not have the same provenance!), and also also, a conversion of a pointer to an integer may be dropped if the result is unused. This makes it fairly easy to write programs that are miscompiled under sane provenance models.
Ralf Jung's series (one of the prominent developers behind Rust's memory model and author of RustBelt) about pointers is very recommended:
https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html
https://www.ralfj.de/blog/2020/12/14/provenance.html
https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html
Does C++ pointer arithmetic invalidate this post or is there some other magic there?
Neither C nor C++ allows you to derive pointers pointing outside the original object (as in, memory allocation), it's UB. Yes, the arithmetic operation itself has UB, not merely later derefering an OoB pointer. The only exception is one-past-the-end pointers which are safe to create (but not to dereference of course).
Whether it's safe to "smuggle" pointers around as ints, without doing any arithmetic on them, is "complicated" and honestly there's no one single answer as far as I know.
To make a long story short:
Pointer provenance is not explicitly present in the C standard. There is a TS, TS 6010, that introduces a pointer provenance model that is fairly close to the effective model of most compilers. Prior to that discussion, compilers have documented a vague model of "pointers need to be based on their original address," but these vague models are usually poorly upheld by the actual compiler [1].
C++ inherits C's failure to discuss pointer provenance, and tacks on an additional complication that it tries to reason about the value of pointers to objects outside their lifetimes. The current thinking on the C++ committee is to give up and go with an "angelic nondeterminism" model, i.e., if there is some possible way to give the program well-defined semantics, that is the semantics of the program. (See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p24... for the current proposal).
[1] In more detail, the principal problem is pointer <-> integer conversions. The compiler is usually thinking of provenance in terms of escaping pointers--there is a use or a source that the compiler cannot definitively track down--and converting a pointer to an integer is an operation that causes the pointer to escape. But the compiler also tends to optimize integers in a way that doesn't preserve provenance (i.e., if x == y, the compiler will freely rewrite a use of x to a use of y or vice versa, even though the two need not have the same provenance!), and also also, a conversion of a pointer to an integer may be dropped if the result is unused. This makes it fairly easy to write programs that are miscompiled under sane provenance models.
D does not allow conversion of an int to a pointer in code marked @safe.
C++ memory provenience is very complicated (and possibly unsound) because it lets you do all sorts of complicated things like this.