About size_t and ptrdiff_t(medium.com)
medium.com
About size_t and ptrdiff_t
https://medium.com/@CPP_Coder/about-size-t-and-ptrdiff-t-a1654234d842
4 comments
ssize_t isn't standard C so it doesn't get you very far for portability.
POSIX gets you pretty far.
Why do you need a "ptrdiff_t" type, why isn't "size_t" sufficient?
Why need a "ptrdiff_t" type:
1) http://www.viva64.com/en/l/0013/
2) http://www.viva64.com/en/b/0039/
1) http://www.viva64.com/en/l/0013/
2) http://www.viva64.com/en/b/0039/
[deleted]
Also, why use the signed ptrdiff_t for a loop index that is never negative?
"size_t can store a pointer, it is better to use another unsinged integer type uintptr_t for that purpose "
So he is actually recommending the unsigned version. Although there are some people (like the Google guidelines for C++) that recommend not using unsigned types because of overflows (which is arguable but it is a point)
I thought using signed indexes in loops allow GCC to perform some optimizations because signed overflow is undefined.
ptrdiff_t is the wrong type for exactly this reason. OP's article is... not good.
ptrdiff_t is a signed type - after all, the diff stands for difference, which implies that it could be negative.
size_t is an unsigned type - it is used to represent the sizes of objects in memory, which can never be negative.
size_t is an unsigned type - it is used to represent the sizes of objects in memory, which can never be negative.
But couldn't you just get along with unsigned types for both (i.e. just size_t)? I mean, two's complement means adding or subtracting is the same for unsigned and signed ints? Is it just for the convenience of being able to compare along the lines of (somePtrDiff < 0)? But then again you could just as well compare (somePtr > someOtherPtr)?
Another funny thing I just discovered: You can't declare an array like "char somearray[SIZE_MAX]"; clang gives "error: array is too large (18446744073709551615 elements)". But you can use SIZE_MAX as an array index (although it gives a warning "array index -1 is past the end of the array"). Also, trying to declare an array like "char somearray[SIZE_MAX/8]" inside a simple main() just sends clang into 100% cpu...
Another funny thing I just discovered: You can't declare an array like "char somearray[SIZE_MAX]"; clang gives "error: array is too large (18446744073709551615 elements)". But you can use SIZE_MAX as an array index (although it gives a warning "array index -1 is past the end of the array"). Also, trying to declare an array like "char somearray[SIZE_MAX/8]" inside a simple main() just sends clang into 100% cpu...
Sometimes you actually want to do math beyond adding and ubstracting with ptrdiff_t. Multiply a potentially negative offset with something? Compare two offsets, one of them negative, with each other?
For multiplication, I think the math works out the same for unsigned and signed, within mod bit-size and you throw away the "overflow"? I.e. (for 16bit pointers), 0xff_ff * 2 == 0x1_ff_fe (-1*2 == -2).
For greater-than/smaller-than comparisons, I see the utility of working in the signed space though. I guess it all depends on expectations. If you have a pointer to the very last byte of a 2^32 address space (0xFFFF_FFFF), and subtract a pointer to the second byte from the start (0x0000_0001), you'll end up with a diff that should be "SIZE_MAX-2", but will be parsed as "-2" in the signed interpretation.
For greater-than/smaller-than comparisons, I see the utility of working in the signed space though. I guess it all depends on expectations. If you have a pointer to the very last byte of a 2^32 address space (0xFFFF_FFFF), and subtract a pointer to the second byte from the start (0x0000_0001), you'll end up with a diff that should be "SIZE_MAX-2", but will be parsed as "-2" in the signed interpretation.
Pointer subtraction is only valid for pointers within the same object. To overflow a signed ptrdiff_t, you'd need to allocate an array of char with more than 2^31 elements. This can be done on some systems, but it's really rare.
> For multiplication, I think the math works out the same for unsigned and signed, within mod bit-size and you throw away the "overflow"?
Yeah, true. I recalled there being separate instructions and then messed up my napkin math to check if it made a difference within the relevant range.
Yeah, true. I recalled there being separate instructions and then messed up my napkin math to check if it made a difference within the relevant range.
ptrdiff_t is signed (because subtraction). size_t is unsigned (because length).
This page is a much shorter summary than the article: http://www.gnu.org/software/libc/manual/html_node/Important-...
Also an important note: if you use special types like ptrdiff_t and size_t, you must not use the underlying native type printf specifiers when printing them. e.g. never print a ptrdiff_t using %ld, use the ptrdiff_t format specifier %td. (because some systems may say ptrdiff_t is long while others may say long long, and all your printf strings will then print compile time warnings.) Also see https://en.wikipedia.org/wiki/Printf_format_string#Length_fi...
Plus, the medium article is a complete copy/paste of this 2009 article: http://www.viva64.com/en/a/0050/ — looks like the account https://news.ycombinator.com/user?id=PVS-Studio is copying from viva64 and posting to medium for a lot of old articles.
This page is a much shorter summary than the article: http://www.gnu.org/software/libc/manual/html_node/Important-...
Also an important note: if you use special types like ptrdiff_t and size_t, you must not use the underlying native type printf specifiers when printing them. e.g. never print a ptrdiff_t using %ld, use the ptrdiff_t format specifier %td. (because some systems may say ptrdiff_t is long while others may say long long, and all your printf strings will then print compile time warnings.) Also see https://en.wikipedia.org/wiki/Printf_format_string#Length_fi...
Plus, the medium article is a complete copy/paste of this 2009 article: http://www.viva64.com/en/a/0050/ — looks like the account https://news.ycombinator.com/user?id=PVS-Studio is copying from viva64 and posting to medium for a lot of old articles.
Article republished by the author’s permission.
I guess that is one way to write articles.
I guess that is one way to write articles.
The author of the original article is one Andrey Karpov. The Medium article is posted by someone called Sergey Vasiliev Marina Makarova and Andrey Karpov recommend". I guess it's the same Andrey Karpov in both cases, especially as hovering over the name Andrey Karpov on the Medium article indicates that his username is "Code_Analysis" and the original article was posted on the blog of a static-code-analysis company.
Seems legit unless someone's actually impersonating someone else. I don't quite understand why, if Karpov wrote the original article and is a Medium contributor, Vasiliev rather than Karpov should be doing the reposting. (You could imagine something like this: K is a senior technical guy at PVS-Studio, V is a more junior technical guy or is in the marketing department; in 2009 they thought it better to post things under K's name to get it out there, but now K wants only the more impressive articles under his name and lets V post the others. But this doesn't feel terribly plausible.)
Seems legit unless someone's actually impersonating someone else. I don't quite understand why, if Karpov wrote the original article and is a Medium contributor, Vasiliev rather than Karpov should be doing the reposting. (You could imagine something like this: K is a senior technical guy at PVS-Studio, V is a more junior technical guy or is in the marketing department; in 2009 they thought it better to post things under K's name to get it out there, but now K wants only the more impressive articles under his name and lets V post the others. But this doesn't feel terribly plausible.)
OMG! It is a great conspiracy theory! Everything is clear. Much time has elapsed since then, many people didn't read old articles about 64-bit. But these articles are useful and as we can see attract interest. Plus, the audience has changed. We would like to share with new one a good material. It is impossible to share old link that is why we decided republish some good articles in new places. This information will be interesting for recruits.
A diff can be negative, whereas a size can't. However, my inclination was to ask the opposite: doesn't ptrdiff_t need to actually be larger than size_t if it must hold signed values and still cover the entire size_t range of inputs?
>> doesn't ptrdiff_t need to actually be larger than size_t if it must hold signed values and still cover the entire size_t range of inputs?
Yes, but if you're just storing the delta between two pointers and then adding/subtracting this diff to get from one to the other it will still work. Of course it will also work using size_t.
Yes, but if you're just storing the delta between two pointers and then adding/subtracting this diff to get from one to the other it will still work. Of course it will also work using size_t.
>> if you're just storing the delta between two pointers and then adding/subtracting this diff to get from one to the other
This would require keeping track of which one is bigger somewhere else unless you had enough room to store a sign. Seems like a pretty goofy way to go about it if you're going to the trouble of making a type specifically to hold a delta.
This would require keeping track of which one is bigger somewhere else unless you had enough room to store a sign. Seems like a pretty goofy way to go about it if you're going to the trouble of making a type specifically to hold a delta.
>> This would require keeping track of which one is bigger somewhere else unless you had enough room to store a sign.
Nope. You assume the math is being done mod 2^32 or 2^64 and it just works weather you use signed or unsigned integers. Pro-tip: the hardware does the exact same bit operations for signed and unsigned addition and subtraction (except for carry/borrow which aren't part of the C standard anyway). integer overflow is undefined, but this behavior is AFAICT universal anyway. If it didn't behave this way then a difference would in fact need the extra bit.
Nope. You assume the math is being done mod 2^32 or 2^64 and it just works weather you use signed or unsigned integers. Pro-tip: the hardware does the exact same bit operations for signed and unsigned addition and subtraction (except for carry/borrow which aren't part of the C standard anyway). integer overflow is undefined, but this behavior is AFAICT universal anyway. If it didn't behave this way then a difference would in fact need the extra bit.
Oh, excellent observation. I am wrong. :)
Yes, this limits the utility of ptrdiff_t. But I don't believe you're allowed to subtract arbitrary pointers in C anyway. So you might need a >63-bit char array in order to legally hit this limit, and you probably don't have 263 of RAM.
Practically, amd64 is already limited to more like 48 bits (4 levels of 9 bit pagetables describing 12 bit pages) of memory.
Practically, amd64 is already limited to more like 48 bits (4 levels of 9 bit pagetables describing 12 bit pages) of memory.
I guess it could happen for applications targeting win32 with the /3GB switch
Let me enumerate the worst errors in this article.
1. Their "headline" recommendation near the start is that you should write things like for (ptrdiff_t i=0; i<...; ++i) { ... a[i]; ... }. No, for this purpose you should almost certainly be using size_t rather than ptrdiff_t. The article nowhere gives any reason for preferring ptrdiff_t to size_t in this context. (There might be some; e.g., if you're processing that array in reverse order, you might want to test whether i>=0, and that won't give useful results if i is of an unsigned type. But you won't learn that from the article.)
2. The article claims that size_t and uintptr_t are synonyms, and likewise for ptrdiff_t and intptr_t. I dare say in practice they're always equivalent types, but they certainly don't mean the same thing and encouraging readers to think they do is a bad idea.
3. Similarly, the article says that a size_t or ptrdiff_t "can store a pointer". There are -- as the article admits -- integral types actually guaranteed to be usable in this way, namely intptr_t and uintptr_t. There is no guarantee that you can do that with size_t or ptrdiff_t, and no good reason why you should try.
4. The article says that ptrdiff_t "could store the maximum size of a theoretically possible array of any type". No, that would be size_t.
5. The article lists things that size_t and ptrdiff_t are "usually used for". The difference between the two lists is that the one for ptrdiff_t includes "size storage", which is exactly backwards. (You'd think the name size_t might be a clue.)
6. Those lists include, in both cases, "storing pointers". No: use pointer types for storing pointers, and if you really truly need to force them into integral types then use intptr_t or uintptr_t.
7. In the second example of a "sleeping error" -- the one with variables A and B -- the more fundamental problem isn't the failure to use size_t/ptrdiff_t, it's the fact that the expression A+B is of an unsigned type but is being used to hold a negative array offset. It's true that using size_t instead of unsigned will probably make the error go away, but the right way to make it go away is never to have the equivalent of ((unsigned)-2) in your code to begin with. You want a signed type for that, and int will do just as well as ptrdiff_t.
8. The final section of the article makes it clear that its real goal is to convince developers that (1) we should use size_t and ptrdiff_t if we care at all about portability and future-proofing, but (2) converting code to do so is too painful for human beings, so (3) you should buy their product.
It's maybe a bit unfair to classify that as an "error", but I don't think I would want to entrust static analysis of integer-type issues to software written by someone who could write this article.
1. Their "headline" recommendation near the start is that you should write things like for (ptrdiff_t i=0; i<...; ++i) { ... a[i]; ... }. No, for this purpose you should almost certainly be using size_t rather than ptrdiff_t. The article nowhere gives any reason for preferring ptrdiff_t to size_t in this context. (There might be some; e.g., if you're processing that array in reverse order, you might want to test whether i>=0, and that won't give useful results if i is of an unsigned type. But you won't learn that from the article.)
2. The article claims that size_t and uintptr_t are synonyms, and likewise for ptrdiff_t and intptr_t. I dare say in practice they're always equivalent types, but they certainly don't mean the same thing and encouraging readers to think they do is a bad idea.
3. Similarly, the article says that a size_t or ptrdiff_t "can store a pointer". There are -- as the article admits -- integral types actually guaranteed to be usable in this way, namely intptr_t and uintptr_t. There is no guarantee that you can do that with size_t or ptrdiff_t, and no good reason why you should try.
4. The article says that ptrdiff_t "could store the maximum size of a theoretically possible array of any type". No, that would be size_t.
5. The article lists things that size_t and ptrdiff_t are "usually used for". The difference between the two lists is that the one for ptrdiff_t includes "size storage", which is exactly backwards. (You'd think the name size_t might be a clue.)
6. Those lists include, in both cases, "storing pointers". No: use pointer types for storing pointers, and if you really truly need to force them into integral types then use intptr_t or uintptr_t.
7. In the second example of a "sleeping error" -- the one with variables A and B -- the more fundamental problem isn't the failure to use size_t/ptrdiff_t, it's the fact that the expression A+B is of an unsigned type but is being used to hold a negative array offset. It's true that using size_t instead of unsigned will probably make the error go away, but the right way to make it go away is never to have the equivalent of ((unsigned)-2) in your code to begin with. You want a signed type for that, and int will do just as well as ptrdiff_t.
8. The final section of the article makes it clear that its real goal is to convince developers that (1) we should use size_t and ptrdiff_t if we care at all about portability and future-proofing, but (2) converting code to do so is too painful for human beings, so (3) you should buy their product.
It's maybe a bit unfair to classify that as an "error", but I don't think I would want to entrust static analysis of integer-type issues to software written by someone who could write this article.
Nitpicks:
"ptrdiff_t type...ptrdiff_t will take 32 bits, on a 64-bit one 64 bits" -- that's not really true as that would made it equivalent to size_t. So in reality its 31 bits and 63 bits.
"ptrdiff_t type...ptrdiff_t will take 32 bits, on a 64-bit one 64 bits" -- that's not really true as that would made it equivalent to size_t. So in reality its 31 bits and 63 bits.
Nope. It's 32 and 64, but it's a signed type (which is why it isn't equivalent to size_t). The range of possible values is still of size 2^32 or 2^64, it's just centred at 0.
[EDITED to add: Having said which, the above isn't really any wronger than the OP which is full of mistakes.]
[EDITED to add: Having said which, the above isn't really any wronger than the OP which is full of mistakes.]
What I meant to say is that the text for size_t and ptrdiff_t are identical, implying that the underlying types are identical. That is there is logical fallacy.
They author states "The type’s size is chosen so that it could store the maximum size of a theoretically possible array of any type" and the same is said for ptrdiff_t, which is quit confusing.
Now I realise that in reality they both use 64 bits, it is somewhat strange to use the same text for both.
They author states "The type’s size is chosen so that it could store the maximum size of a theoretically possible array of any type" and the same is said for ptrdiff_t, which is quit confusing.
Now I realise that in reality they both use 64 bits, it is somewhat strange to use the same text for both.
Yeah, the article is just terrible, and when it says a ptrdiff_t can store the size of any object it's completely wrong.
size_t represents the size of objects in memory. So of course it scales with virtual memory space.
ssize_t is like size_t (same width), but signed. It's useful for routines that want to return a size_t or error (network routines like send/recv).
ptrdiff_t is specifically for the result of subtracting one pointer from another. Obviously it scales with pointer size as well.