4 ms·
Sorry to disappoint, but the compilers for nearly 100% of the server, desktop, and mobile computers in the real world do not allow arbitrary pointer arithmetic.
by hvdijk 4y ago
Sorry to disappoint, but the compilers for nearly 100% of the server, desktop, and mobile computers in the real world do not allow arbitrary pointer arithmetic. They cannot issue an error message for it because that is provably impossible to detect in the general case, but they do optimise on the basis that it does not happen, even if it breaks programs that try to make use of it. Consider this example for GCC:
int a[2], b[2];
int offset(void) { return b - a; }
int check(void) { return &a[1] + offset() == &b[1]; }
The function check() is optimised by GCC to return zero at -O1 optimisation level or higher, because it reasons that no matter how offset() is implemented, either the addition is undefined, or the comparison results in false.
(Note that GCC does this even in a few cases where it is unclear whether the optimisation is valid. The example I provided is slightly more complicated than I would have liked to avoid that issue; the optimisation is definitely valid in this example.)
- deleted 4y ago[deleted]
- xscott 4y agoI originally responded to your code, and you're right about it. That's not what I expected or would want to happen. However, your code doesn't actually address my comment about conversions and arithmetic. Where does this code code fail? int a[2], b[2]; uintptr_t x = (uintptr_t)a; uintptr_t y = (uintptr_t)b; uintptr_t offset = y - x; int* p = (int*)(x + offset); printf("b == p: %d\n", b==p); I didn't try _every_ compiler on Godbolt, but I didn't see it misbehave anywhere I did try. edit: changed to uintptr_t
- spc476 4y agoThere's nothing in the C standard that I could find that dictates that b[] has to follow directly after a[] in memory. That it works is just happenstance (or an implementation detail). The only place were order is maintained are fields in a structure definition.
- xscott 4y agoThere's nothing in that example that assumes they are adjacent. It only assumes they are in the same (flat) address space. Put a gigabyte between them if you want. If I had used `uintptr_t` instead of `ssize_t`, I think it's even compliant as far as wraparound goes. edit: Note I changed the code to use uintptr_t
- hvdijk 4y agoThat is integer arithmetic, not pointer arithmetic. My understanding of the C memory model that is mentioned in the article (look for PNVI-ae-udi) is that what you are doing is or will be well-defined. I suspect there may still be a few implementations around where the offset you get from integer subtractions has no obvious relation to the offset you would get from pointer subtractions (for two pointers where subtraction is well-defined), but for your example that makes no difference.
- xscott 4y ago> That is integer arithmetic, not pointer arithmetic. Yeah, but I did say: "which operation could any C compiler disallow in practice: converting from pointer to integer, arithmetic on integers, or converting from integer to pointer?" If C/C++ compilers keep breaking pointer arithmetic in the game of exploiting undefined behavior for optimizations, people are going to start doing pointer arithmetic with integers when they need it. And they do need it sometimes, for debuggers, profilers, memory checkers, JITs, garbage collectors, shared memory, and so on.
- hvdijk 4y agoThat may be good. If the pointer arithmetic with integers, or other constructs that have the effect of disabling optimisations, is kept to the code that has additional requirements beyond what the standard guarantees, that means the code out there that does not have those additional requirements, which I suspect is the majority of code, can continue to be aggressively optimised.
- xscott 4y agoSame compiler and optimization level provides different behavior when you ask it to compile as C vs C++. Gross.