Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Was `long` guaranteed to be as wide as `size_t`

When looking for evidence of unsigned long being enough to hold size_t for the purpose of being argument to printf I ran into two fact(oid)s.

First there's this answer stating that long is indeed not guaranteed to be large enough for size_t. On the other hand I saw this answer suggesting to use printf("%lu", (unsigned long)x) in pre C99, x being of size_t.

So the question is could you assume or were you guaranteed that long were enough to hold size_t in pre C99. The other question is whether there exists any guarantee that size_t would fit in any of the other standardized integer types (except the obvious exceptions like ssize_t, ptrdiff_t and such).

like image 787
skyking Avatar asked Dec 25 '22 15:12

skyking


2 Answers

There is no such guarantee.

While it is common for implementation to have same size for long and size_t, it is not always the case. As put in the comments Windows 64-bit have different size for long and size_t.

Also notice that the minimum value of SIZE_MAX for an implementation is 65535 while the minimum value of ULONG_MAX is 4294967295 (2147483647 for LONG_MAX). (Note that SIZE_MAX appeared with C99.) It means that size_t is guaranteed to be at least 16-bit but unsigned long / long are guaranteed to be at least 32-bit.

EDIT: Question has changed a little bit after this answer... So:

So the question is could you assume or were you guaranteed that long were enough to hold size_t in pre C99.

There is no such guarantee even in C89. long can be 32-bit and size_t 64-bit. (See C89 example with MSVC in Windows 64-bit above.)

The other question is whether there exists any guarantee that size_t would fit in any of the other standardized integer types (except the obvious exceptions like ssize_t, ptrdiff_t and such).

Again there is no such guarantee by the Standard. size_t is an alias for another standard unsigned integer type (and it cannot be an extended integer type as C89 does not have extended integer types).

like image 54
ouah Avatar answered Dec 27 '22 05:12

ouah


So the question is could you assume or were you guaranteed that long were enough to hold size_t in pre C99.

Not long, but unsigned long.

In C89/C90, size_t is required to be an unsigned integral type. There are exactly 4 unsigned integer types in C89/C90: unsigned char, unsigned short, unsigned int, and unsigned long. Therefore in C89/C90, size_t can be no wider than unsigned long, and therefore any value of type size_t can be converted to unsigned long without loss of information. (This applies only to unsigned long, not to long.)

This implicit guarantee vanished in C99, with the introduction of unsigned long long and of extended integer types. In C99 and later, size_t can be wider than unsigned long. For example, a C99 implementation might have 32-bit long and 64-bit long long, and make size_t an alias for unsigned long long.

Even in C89/C90, you can rely on the guarantee only if you have a conforming C89/C90 implementation. It was common for pre-C99 compilers to provide extensions on top of the C89/C90 standard -- for example a compiler might support long long, and might make size_t an alias for unsigned long long, even if the compiler didn't fully support the C99 (or C11) standard.

The question was about printf. Keep in mind that an argument to printf must be of an appropriate type for the format string. This:

printf("sizeof (int) = %lu\n", sizeof (int));

has undefined behavior unless size_t happens to be an alias for unsigned long (even if size_t and unsigned long happen to have the same size). You need to cast the value to the correct type:

printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));

For C99 and later, you can print size_t values directly:

printf("sizeof (int) = %zu\n", sizeof (int));

And if like, you can test the value of __STDC_VERSION__ to determine which one to use.

(A note on editions of the C standard. The first C standard was published in 1989 by ANSI. It was republished, with extra boilerplate sections added, by ISO in 1990. So C89 and C90 are two different documents that describe the same language. The later C99 and C11 standards were published by ISO. All three ISO C standards were officially adopted by ANSI. So strictly speaking "ANSI C" should refer to ISO C11 -- but for historical reasons the phrase is still used to refer to the 1989 standard.)

like image 44
Keith Thompson Avatar answered Dec 27 '22 05:12

Keith Thompson