52

The C standard (ISO/IEC 9899:2011 or 9899:1999) defines a type ptrdiff_t in <stddef.h>.

The POSIX standard (ISO/IEC 9945; IEEE Std 1003.1-2008) defines a type ssize_t in <sys/types.h>.

  • What is the difference between these types (or why were both deemed necessary)?
  • Is there an implementation where the underlying base type for ssize_t is not the same as for ptrdiff_t?
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 3
    perhaps it's the semantics of `ptrdiff_t` that called for the addition of `ssize_t`? Sometimes, a signed size type is useful, if you want to be able to intermediately have it represent `-1`. The semantics of `ptrdiff_t` is "difference between two pointers", which is not exactly the semantics of "size". – Johannes Schaub - litb Dec 27 '11 at 20:55
  • 3
    Probably a bit of NIH paranoia in the relevant stadards committees – Chris Dodd Dec 27 '11 at 21:21

2 Answers2

34

Is there an implementation where the underlying base type for ssize_t is not the same as for ptrdiff_t?

x86-16 with the large memory model. Pointers are far (32-bit), but individual objects are limited to one segment (so size_t is allowed to be 16-bit).

dan04
  • 87,747
  • 23
  • 163
  • 198
  • 18
    wouldn't `ptrdiff_t` also be 16 bits in that situation, since pointer difference is only defined when both pointers point at the same object... – Chris Dodd Dec 27 '11 at 21:15
  • 5
    @Dhris: `ptrdiff_t` is an implementation-defined type, and making it the size of a pointer seems reasonable. Operations on pointers are supposed to be unsurprising to anybody familiar with the memory model, and a 16-bit `ptrdiff_t` would likely surprise people using 32-bit pointers. – David Thornley Dec 27 '11 at 21:30
  • 10
    @ChrisDodd: An object can be up to 65535 bytes, so a valid pointer subtraction could easily exceed 32767; for `ptrdiff_t`, you want a signed type that can hold values up to 65536. – Keith Thompson Dec 30 '11 at 17:42
  • 1
    One could imagine a similar memory model where pointers are 64 bits but objects can`t be bigger than one 32-bit segment, so `size_t` can be 32 bits. – Keith Thompson Dec 30 '11 at 17:44
  • 2
    This answer appears incomplete as the answer does not discuss `ssize_t`. – chux - Reinstate Monica Feb 27 '15 at 18:24
  • 1
    @chux It assumes that `ssize_t` is the same width as `size_t` (although IDK if POSIX guarantees that) – M.M Mar 08 '16 at 20:11
  • 1
    @M.M 10 I have found no such POSIX guarantee. Using a `16-bit unsigned` --> `size_t` and `32-bit signed long` --> `ssize_t`, at least, in theory (or per a vague recollection), is possible. – chux - Reinstate Monica Mar 08 '16 at 20:25
  • 3
    @KeithThompson C 6.5.6-9 allows pointer subtraction (of pointers into the same object) to overflow ptrdiff_t, probably for this very reason. In a more relevant ILP32/Linux system (where all three types are 32-bit), you could at least theoretically allocate an object exceeding 2GB, where subtracting pointers into it would overflow both ptrdiff_t and ssize_t – Dan Bonachea Dec 28 '17 at 01:20
  • @DanBonachea: GCC on 32-bit x86 Linux only allows objects up to 2GiB, so sizes and pointer differences always fit in `ptrdiff_t`, and for wider types the subtraction doesn't have to keep the carry-out from subtraction before right-shifting or dividing by the type width. It's possible to allocate more than that with `mmap`, but UB to *use* it as one single object on that C implementation. See my comment on Keith's answer on [What is the maximum size of an array in C?](//stackoverflow.com/posts/comments/85866179) – Peter Cordes Mar 03 '19 at 23:24
  • But yes the standard's wording allows for implementations where objects can be too big for `end-start` to avoid overflow. But note that it's only UB according to ISO C if the final result after scaling by the type width doesn't fit in `ptrdiff_t`; for pointer types wider than `char` that means the raw pointers *would* have to keep an extra bit when subtracting the raw pointers (e.g. rotate-right through carry after sub), or right-shift both inputs to a subtraction (because unaligned pointers are UB). GCC avoids that by making such huge objects always UB, regardless of type width. – Peter Cordes Mar 03 '19 at 23:31
30

The Open Group Base Specifications Issue 7, IEEE Std 1003.1, 2013 Edition, description of <sys/types.h> says:

The type ssize_t is capable of storing values at least in the range [-1, SSIZE_MAX].

In other words, ssize_t is signed, but the set of negative values it can represent may be limited to just {-1}.

A ptrdiff_t, on the other hand, is guaranteed to have a more symmetric positive/negative range.

I admit that in practice, it doesn't seem likely that ssize_t would be this limited in the negative range, but it is possible.

Of course, another difference is that ptrdiff_t is available whenever you're programming in standard C or C++, but ssize_t may not be available unless you're targeting a standard POSIX system.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • 1
    Thanks for the historical information; it helps explain where it came from, even if the current standard no longer mandates the range. (My search of the current standard was not what I'd regard as definitive, though I believe it was fairly thorough; hence the 'weasel-wording' of 'does not seem to be in the 2008 standard'.) – Jonathan Leffler Jun 04 '12 at 17:36
  • I wasn't familiar with `ssize_t` before today. I was just pointing out what I learned from the top search result. – Adrian McCarthy Jun 04 '12 at 22:12
  • 2
    [2008 `sys/types.h` standard](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html) has the same `[-1, {SSIZE_MAX}]` requirement for `ssize_t`. – jw013 Jun 13 '12 at 12:40
  • and let me guess: `SSIZE_MAX` is defined to be at most `sizeof(size_t)/2`? – MestreLion Feb 26 '15 at 11:42
  • 5
    POSIX says that `ssize_t` is an integer type, and the C standard requires signed integer types to have their maximum negative value be at least as great in magnitude as their maximum positive value. – M.M Mar 08 '16 at 20:14
  • @M.M: Citation needed. I can't find anything in the C or C++ standards that says integer types need "to have their maximum negative value be at least as great in magnitude as their maximum positive value." It so happens that's true for the ranges for the _standard_ integer types, but the standard allows for the existence of other integer types (such as the extended integer types) without any qualifications about the ranges they can represent. – Adrian McCarthy Mar 08 '16 at 20:56
  • 4
    @AdrianMcCarthy See C11 6.2.6.2 which explains the permitted meanings for bits within an integer. The term "signed integer types" is defined by 6.2.5 as including both the standard and the extended signed integer types. – M.M Mar 08 '16 at 21:01
  • 1
    @M.M: Ah, you're absolutely right about 6.2.6.2. I didn't find anything like that in the C++ standard (which has far fewer hits for "integer type" than the C standard does). The 6.2.5 language looks approximately the same as what's in the C++ standard, but that doesn't constrain the representation or range of an arbitrary integer type. Thanks! – Adrian McCarthy Mar 08 '16 at 21:55