Jannis, your implementation had several bugs, one of which was unfixable as I've explained. The others were that the operands of the return subtraction expression were reversed, and that it was assigning a const-qualifed variable (s) to a non-const-qualifed variable (p). --Netocrat 00:52, 13 March 2006 (GMT)
Obviously I missed a const and swapped p and s, but there's no unfixable bug: ptrdiff_t is able to represent the difference between any two pointers pointing to or just beyond the same object. (s-p) is not negative and size_t can represent any size possible in the conforming implementation. --Jannis 18:13, 13 March 2006 (GMT)
Unfortunately that just isn't supported by the Standard. Referring to N1124, 6.5.6#9: If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i -th and j -th elements of an array object, the expression (P)-(Q) has the value i−j provided the value fits in an object of type ptrdiff_t.
I still believe that what I wrote in the article was accurate - please reread it in the light of the wording in the Standard (also search for past discussions in comp.lang.c that have covered this if you're not convinced) and let me know if you still disagree. --Netocrat 18:30, 13 March 2006 (GMT)
I'm sorry, you're right, one can construe a bad-behaved conforming implementation where ptrdiff_t overflows. Thus, my implementation obviously relies on implementation-defined behavior. I'm quite content I never came around any such implementation. I've corrested the article to reflect that. --Jannis 19:16, 13 March 2006 (GMT)
Massive strings are probably a rare occurrence, but an implementation that doesn't function properly for that subtraction isn't badly behaved. Consider a typical implementation where size_t is 32 bits (unsigned) and ptrdiff_t is 32 bits (signed). The maximum value storable in a positive ptrdiff_t is necessarily half the maximum possible value storable in the size_t - it's a practical problem that can't be resolved unless the implementation is forced to use 64 bits (signed) for ptrdiff_t (or simply for any pointer subtraction), which the Standard doesn't do, and which no implementation I've ever used chooses to do. I've clarified the wording of the article and introduced a new conformance type "non-portable" since "broken" seems overly harsh in this situation. --Netocrat 20:07, 13 March 2006 (GMT)
On most implementations where size_t is 32 bits, the behavior on overflow (ignore it) and use of 2s-complement still guarantees that the code works for any string smaller than 32². --Jannis 20:31, 13 March 2006 (GMT)
OK that's a fair point - I've reworded again.
The Standard seems to allow for allocation of an object of size larger than SIZE_MAX by using e.g. char *str = calloc(2, SIZE_MAX);
- in that case not even 2s-complement will help; but in that case neither will anything else since the result won't fit in size_t regardless. Calling strlen() on such an object seems to be implictly undefined behaviour. --Netocrat 21:10, 13 March 2006 (GMT)
strlen
Of the two versions of strlen shown, I like the first one better because it *is* more portable. I wouldn't go so far as to call the second one, which uses a ptrdiff_t expression, nonportable. ptrdiff_t is capable of representing the difference between any two addresses within an object in a program that doesn't excede the Translation limit of (— 65535 bytes in an object), from (5.2.4.1 Translation limits), in C99. However, conforming programs which use malloc, do frequently excede these limits.
- That was clearly expressed. I've moved the code back into the "Portable" section and reworded the introduction as:
- The following is slightly less portable code although it meets all of the requirements of the Standard - see the talk page for details.
- --Netocrat 01:47, 23 June 2006 (UTC)