Must size_t be defined using a typedef as one of the five standard unsigned integer types?

223 Views Asked by At

The C11 standard writes:

6.2.5 Types, ¶4:

There are five standard signed integer types, designated as signed char, short int, int, long int, and long long int.

The five standard unsigned integer types would thus be unsigned char, unsigned short int, unsigned int, unsigned long int, and unsigned long long int.

7.19 Common definitions <stddef.h>, ¶1-2:

The header <stddef.h> [...] declares the following types. [...] size_t which is the unsigned integer type of the result of the sizeof operator

The presentation on cppreference.com

typedef /*implementation-defined*/ size_t;

implies that size_t is always defined via a typedef, and some answers to this question state/imply/assume that it is defined as one of the five standard unsigned integer types (see above) via a typedef.

But couldn't size_t also be defined as one of the specific-width integer types (C11 standard, 7.20 Integer types <stdint.h>), such as uint32_t? (Incidentally, what are they called? Wikipedia refers to them as "fixed-width integer types". The C11 standard occasionally uses the expression "extended integer type", though I can't find a prominent definition in 7.20 to that effect.) Must size_t be defined using a typedef as one of the five standard unsigned integer types? Or can it alternatively be defined as one of the specific-width integer types? Or can it be an entirely separate type? From what wording in the standard does the answer follow?


Edit: I found a relevant quote regarding this topic (Rationale for International Standard—Programming Languages—C, Revision 5.10 (April-2003), 6.5.3.4 The sizeof operator):

The type of sizeof, whatever it is, is published (in the library header <stddef.h>) as size_t, since it is useful for the programmer to be able to refer to this type. This requirement implicitly restricts size_t to be a synonym for an existing unsigned integer type.


Edit: The integer types listed here on Wikipedia are called "specified-width integer types" in the C standard (I checked the C99 and C17 versions).


There is a C++ version of this question on Stack Overflow, but I can't deduce a clear answer for C.

4

There are 4 best solutions below

1
Eric Postpischil On BEST ANSWER

Must size_t be defined using a typedef as one of the five standard unsigned integer types?

No, this is not required by the C standard.

… implies that size_t is always defined via a typedef,…

This is inconsequential, as size_t behaves in source code like a type, and how it came to be a type does not affect that. However, C 2018 7.19 1 says that <stddef.h> declares “the following types”, of which size_t is one. Defining it as a macro would not make it a type, so it must be declared with typedef or some implementation extension that is equivalent.

… some answers to this question state/imply/assume that it is defined as one of the five standard unsigned integer types (see above) via a typedef.

Statements that do not cite an authority and are not written by an authority are not authoritative.

But couldn't size_t also be defined as one of the specific-width integer types (C11 standard, 7.20 Integer types <stdint.h>), such as uint32_t?

Yes. The specification of size_t in C 2018 7.19 does not prohibit this.

Incidentally, what are they called? Wikipedia refers to them as "fixed-width integer types".

The title of C 2018 7.20.1.1 is “Exact-width integer types.”

The C11 standard occasionally uses the expression "extended integer type", though I can't find a prominent definition in 7.20 to that effect.

The term “extended integer types” is defined in C 2018 6.2.5 7, to be the extended signed integer types and the extended unsigned integer types. Those are defined in 6.2.5 4 and 6.2.5 6. They are merely additional integer types defined by the C implementation. A note notes they would have names in the portion of the name space reserved for identifiers in 7.1.3, notably an names beginning with underscore and an uppercase letter or another underscore. For example, __int48_t could be such a name.

Must size_t be defined using a typedef as one of the five standard unsigned integer types? Or can it alternatively be defined as one of the specific-width integer types? Or can it be an entirely separate type?

The wording in the standard is in C 2018 7.19 1:

The header <stddef.h> defines the following macros and declares the following types…

and 7.19 2:

size_t

which is the unsigned integer type of the result of the sizeof operator;…

That wording does not constrain size_t to be one of the standard unsigned types nor to be one of the exact-width integer types, nor to be another type, nor does it prohibit it from being any of those types. (It does not even require that size_t actually be able to represent all object sizes, so it is conceivable that specified result of a sizeof operation may exceed what is representable in the size_t type.)

0
Lundin On

cppreference.com isn't a canonical source. There is nothing in the actual standard stating that typedef must be used. The C standard (C17 7.19) does however make a recommendation (and as such, it isn't normative but obviously highly recommended):

Recommended practice

The types used for size_t and ptrdiff_t should not have an integer conversion rank greater than that of signed long int unless the implementation supports objects large enough to make this necessary.

This recommendation means that in practice size_t is very likely one of unsigned short, unsigned int or unsigned long. Or in case of 64 bit address bus where the above recommendation doesn't apply, perhaps unsigned long long.

It is not likely a character type because 7.20.3 specifies SIZE_MAX to be at least 65535. Character types can be that large, but that's quite exotic.

Very strictly speaking, size_t could as well be defined as an extended integer type or something like that. It is important to recognize that standard C libraries need not necessarily be written in conforming standard C, as long as they provide an API which is conforming.

Example to determine which type it is:

#include <stdio.h>

int main (void)
{
  
  _Generic( (sizeof 0),
            unsigned short:      puts("unsigned short"),
            unsigned int:        puts("unsigned int"),
            unsigned long:       puts("unsigned long"),
            unsigned long long:  puts("unsigned long long"),
            /* size_t: puts("compiler error here, size_t compatible with another type"), */
            default: puts("this ain't gonna get printed") );
}

If we uncomment the size_t line there will be a compiler error since size_t is identical to one of the other types.

In practice, mainstream computers existing in the real world use these sizes:

  • 8 bit CPU, 16 bit address bus, size_t = 32 bit unsigned long.
  • 16 bit CPU, 16 bit address bus, size_t = 32 bit unsigned long.
  • 32 bit CPU, 32 bit address bus, size_t = 32 bit unsigned long.
  • 64 bit CPU, 64 bit address bus, compiler flavour #1, size_t = 64 bit unsigned long long.
  • 64 bit CPU, 64 bit address bus, compiler flavour #2, size_t = 64 bit unsigned long.
0
Serge Ballesta On

Or can it be an entirely separate type?

Well it could. More precisely nothing in the standard mandates for size_t to be defined as one of the 5 standard unsigned types

Draft n1570 for C11 says at 6.2.5 Types §5 (emphasize mine):

For each of the signed integer types, there is a corresponding (but different) unsigned integer type (designated with the keyword unsigned) that uses the same amount of storage (including sign information) and has the same alignment requirements. The type _Bool and the unsigned integer types that correspond to the standard signed integer types are the standard unsigned integer types. The unsigned integer types that correspond to the extended signed integer types are the extended unsigned integer types. The standard and extended unsigned integer types are collectively called unsigned integer types.

The (non normative) note 40 is even more explicit:

Therefore, any statement in this Standard about unsigned integer types also applies to the extended unsigned integer types.

As 6.5.3.4 The sizeof and _Alignof operators only says at §5 (again emphasize mine):

The value of the result of both operators is implementation-defined, and its type (an unsigned integer type) is size_t, defined in <stddef.h> (and other headers).

Nothing in the standard prevents size_t to be an implementation defined extended type.

That being said on all architecture I know, size_t is either unsigned int, unsigned long or unsigned long long, directly or indirectly.

0
John Bollinger On

Must size_t be defined using a typedef as one of the five standard unsigned integer types?

No, because although typedef is how you can define type aliases in your code, nothing requires the headers provided by the implementation to use that mechanism to "define" a type. The standard library headers are not even required to be actual files at all (though usually they are). They could, for example, be wholly built into the compiler, with no external representation.

The underlying question might be simply whether size_t must be the same type as one of the standard unsigned integer types, but again no. The spec defines size_t as the (unsigned integer) type of the result of the sizeof operator (C23 7.21/2), and the specs for sizeof also specify that that result is an unsigned integer type (C23 6.5.3.4/5), but nowhere is it specified that that must be one of the standard unsigned integer types. The spec explicitly allows implementations to provide other ("extended") unsigned integer types (C23 6.2.5/5-7), and size_t could be one of these.

Or can it alternatively be defined as one of the specific-width integer types?

The specific-width integer types provided by an implementation are not necessarily distinct types from the standard integer types, but if any of them were, those would be among the extended integer types. The unsigned among those are possible candidates for size_t, as already discussed.

Or can it be an entirely separate type?

Nothing in the spec requires that the type identified by size_t be the same type as any of the others the spec requires implementations to provide. If it were not, that would place it among the implementation's extended unsigned integer types, which I have touched on already.