问题
Is there a way to subtract one pointer from another in C11 and have the result be always defined?
The standard says the behavior is undefined if the result is not representable as type ptrdiff_t.
I am open to a solution relying on static assertions that are expected to pass on a reasonable implementation in a modern general purpose 32 or 64 bit environment. I would like to avoid solutions that rely on any sort of runtime checks.
If the pointed to type has size greater than 1, I can static assert size_t and ptrdiff_t to have the same number of nonpadding bits. This partial solution relies on two things I am not sure about, so any feedback on this would provide a partial answer:
It can be expected that ptrdiff_t has at most one fewer value bit than size_t in a reasonable implementation in a modern general purpose 32 or 64 bit environment.
I am correct in my understanding of the standard, in that the difference between two pointers to objects of size greater than 1 is defined, even when the same difference would be undefined if the pointers were cast to character pointers. This understanding seems inconsistent with footnote 106 in the committee draft, but it is my understanding that footnotes are not normative.
回答1:
According to the Standard
You can only subtract pointers if both pointers point to the same object, which includes the "one-past-the-end" pointer.
Subtracting uintptr_t
or intptr_t
is not necessarily meaningful, because, again, according to the standard, there is no particular way that the conversion from pointer to integer has to be defined. In particular,
Consider far pointers in a segmented memory model, where there may be more than one way to represent a given address (segment + offset, for example, on x86).
Consider pointers with bits that are ignored by processor. (For example, the Motorola 68000 processor, which has 32-bit pointers but the top 8 bits are ignored.)
So, unfortunately, there is no way to do this portably, according to the standard.
Remember: size_t
is the maximum size of an object. It is not the size of your address space. It is entirely legal for size_t
to have less range that uintptr_t
and friends. Same with ptrdiff_t
: it is entirely legal for ptrdiff_t
to have less range than uintptr_t
. Imagine, for example, a segmented memory model where you cannot allocate anything larger than a segment, in this case, size_t
and ptrdiff_t
might be able to represent the size of a segment but not the size of your address space.
According to Practice
On the computers which you use (modern 32-bit and 64-bit computers), a uintptr_t
will just contain the pointer address. Subtract away. This is implementation-defined but not undefined behavior.
Do not subtract the original pointers without casting unless they point to the same object, or to the address past that object. Compilers can and will make aliasing assumptions when you use pointer arithmetic. Not only is your program "technically" wrong, but there is a long history of compilers producing bad code here.
There is a bit of an argument going on right now about what, exactly, it means for a pointer to point to the same object, but this argument was unresolved last time I checked.
来源:https://stackoverflow.com/questions/38442017/is-there-a-defined-way-to-do-pointer-subtraction-in-c11