6 ms·
A few years ago I came across this article Pointers Are More Abstract Than You Might Expect In C [1]. I followed the article which attempted to interpret the C
by commandersaki 7y ago
A few years ago I came across this article Pointers Are More Abstract Than You Might Expect In C [1].
I followed the article which attempted to interpret the C standard and come to a conclusion. The conclusion is:
> The takeaway message is that pointer arithmetic is only defined for pointers pointing into array objects or one past the last element. Comparing pointers for equality is defined if both pointers are derived from the same (multidimensional) array object. Thus, if two pointers point to different array objects, then these array objects must be subaggregates of the same multidimensional array object in order to compare them. Otherwise this leads to undefined behavior.
Based on the above, I arrived at the conclusion after reading this that comparing two distinct malloc()'d pointers for equality itself is undefined behaviour since malloc() is likely to return pointers to distinct objects that are not part of a sub-aggregate object.
I know this is incorrect, but I don't know why I'm wrong.
[1]: https://stefansf.de/post/pointers-are-more-abstract-than-you-might-expect/ https://stefansf.de/post/pointers-are-more-abstract-than-you...
- pascal_cuoq 7y agoThe only thing that is not defined is comparing a pointer one-past-the-end to a pointer to the very beginning of a toplevel object. Apart from this rule, pointers of course do not need to be derived from the same object in order to be compared with == and !=. &a + 1 == &b is unspecified: it may produce 0 or 1, and it may not produce the same result if you evaluate it several times. Similarly, if both the char pointers p and q were obtained with malloc(10), after they have been tested for NULL, all these operations are valid: p == q (false) p + 1 == q (false) p + 1 == q + 1 (false) p + 10 == q + 1 (false) Only p+10 == q and p == q+10 are unspecified (of the comparisons that can be built without invoking UB during the pointer arithmetic itself). I have no idea what led that person to (apparently) write that &a==&b is undefined. This is plain wrong. I do not see any ambiguity in the relevant clause (https://port70.net/~nsz/c/c11/n1570.html#6.5.9p6 https://port70.net/~nsz/c/c11/n1570.html#6.5.9p6 ). Yes, the standard is in English and natural languages are ambiguous, but you might as well claim that a+b is undefined because the standard does not define what the word “sum” means (https://port70.net/~nsz/c/c11/n1570.html#6.5.6p5 https://port70.net/~nsz/c/c11/n1570.html#6.5.6p5 ).
- azinman2 7y agoWhy is this undefined if it’s all just pointers to addresses in memory, regardless if the memory is valid for that object or not?
- pascal_cuoq 7y agoHere is an example I have at hand that shows that when you are using an optimizing compiler, there is no such thing as “just pointers to addresses in memory”. There are plenty more examples, but I do not have the other ones at hand. https://gcc.godbolt.org/z/Budx3n https://gcc.godbolt.org/z/Budx3n
- pgy 7y agoPlease correct me if I am wrong, but I think here the optimization is possible because "* p = 2" is UB, because the compiler can assume that "p" points to invalid memory. For this assumption, the compiler must know that "realloc" invalidates its first argument. How does it know that? The definition of "realloc" lives in the source of "libc.so", so the compiler should not be able to see into it. Its declaration in "malloc.h" does not have any special attributes. Does the standard and/or the compiler handles "realloc" differently from other functions? edit: It looks like clang inserts a "noalias" attribute to the declaration of "realloc" in the LLVM IR, so it seems it does handle "realloc" specially. declare dso_local noalias i8* @realloc(i8* nocapture, i64) local_unnamed_addr #3
- joosters 7y agoI would guess that it is because it gives some freedom to the compiler. e.g. If you have two pointers 'foo' and 'bar' that point to two separate structures (e.g. two arrays of ints), the compiler can always assume that the pointers, even with some adds/subtracts, will never 'collide', i.e. foo will never == bar, regardless of their relative memory positions.
- cormacrelf 7y agoThat’s quite precise, can you give a sense of why it’s useful to have? Does it translate as “you can never know whether two mallocs are adjacent, so don’t even try merging them”?
- _kst_ 7y agoPointer equality (the == and != operators) is well defined for any pointers (of the same type) to any objects. Relational operators (< <= > >=) on pointers have undefined behavior unless both pointers point to elements of the same array object or just past the end of it. A single non-array object is treated as a 1-element array for this purpose. (That's for object pointers. Function pointers can be compared for equality, but relational operators on function pointers are invalid.)