Section VI: Extended Integer Types

As described in Chapter 3, "Data and C," the C99 header file inttypes.h provides a systematic set of alternative names for the various integer types. These names describe the properties of the type more clearly than do the standard names. For example, type int might be 16 bits, 32 bits, or 64 bits, but the int32_t type always is 32 bits.

More precisely, the inttypes.h header file defines macros that can be used with scanf() and printf() to read and write integers of these types. This header file includes the stdlib.h header file, which provides the actual type definitions. The formatting macros are strings that can be concatenated with other strings to produce the proper formatting directions.

The types are defined using typedef. For example, a system with a 32-bit int might use this definition:

typedef int int32_t;

The format specifiers are defined using the #define directive. For example, a system using the previous definition for int32_t might have this definition:

If you moved the original code to a system with a 16-bit int, that system might define int32_t as long, PRId32 as "ld", and SCNd32 as "ld". But you could use the same code, knowing that it uses a 32-bit integer.

The rest of this reference section lists the extended types along with the format specifiers and macros representing the type limits.

Exact-Width Types

One set of typedefs identify types with precise sizes. The general form is intN_t for signed types and uintN_t for unsigned types, with N indicating the number of bits. Note, however, that not all systems can support all the types. For example, there could be a system for which the smallest usable memory size is 16 bits; such a system would not support the int8_t and uint8_t types. The format macros can use either d or i for the signed types, so PRIi8 and SCNi8 also work. For the unsigned types, you can substitute o, x, or X for u to obtain the %o , %x, or %X specifier instead of %u. For example, you can use PRIX32 to print a uint32_t type value in hexadecimal format. Table RS.VI.1 lists the exact-width types, format specifiers, and value limits.

Table RS.VI.1. Exact-Width Types

Type Name

printf() Specifier

scanf() Specifier

Minimum Value

Maximum Value

int8_t

PRId8

SCNd8

INT8_MIN

INT8_MAX

int16_t

PRId16

SCNd16

INT16_MIN

INT16_MAX

int32_t

PRId32

SCNd32

INT32_MIN

INT32_MAX

int64_t

PRId64

SCNd64

INT64_MIN

INT64_MAX

uint8_t

PRIu8

SCNu8

0

UINT8_MAX

uint16_t

PRIu16

SCNu16

0

UINT16_MAX

uint32_t

PRIu32

SCNu32

0

UINT32_MAX

uint64_t

PRIu64

SCNu64

0

UINT64_MAX

Minimum-Width Types

The minimum-width types guarantee a type that is at least a certain number of bits in size. These types always exist. For example, a system that does not support 8-bit units could define int_least_8 as a 16-bit type. Table RS.VI.2 lists minimum-width types, format specifiers, and value limits.

Table RS.VI.2. Minimum-Width Types

TypeName

printf() Specifier

scanf() Specifier

Minimum Value

Maximum Value

int_least8_t

PRILEASTd8

SCNLEASTd8

INT_LEAST8_MIN

INT_LEAST8_MAX

int_least16_t

PRILEASTd16

SCNLEASTd16

INT_LEAST16_MIN

INT_LEAST16_MAX

int_least32_t

PRILEASTd32

SCNLEASTd32

INT_LEAST32_MIN

INT_LEAST32_MAX

int_least 64_t

PRILEASTd64

SCNLEASTd64

INT_LEAST64_MIN

INT_LEAST64_MAX

uint_least 8_t

PRILEASTu8

SCNLEASTu8

0

UINT_LEAST8_MAX

uint_least 16_t

PRILEASTu16

SCNLEASTu16

0

UINT_LEAST16_MAX

uint_least 32_t

PRILEASTu32

SCNLEASTu32

0

UINT_LEAST32_MAX

uint_least 64_t

PRILEASTu64

SCNLEASTu64

0

UINT_LEAST64_MAX

Fastest Minimum-Width Types

For a particular system, some integer representations can be faster than others. For example, int_least16_t might be implemented as short, but the system might do arithmetic faster using type int. So inttypes.h also defines the fastest type for representing at least a certain number of bits. These types always exist. In some cases, there might be no clear-cut choice for fastest; in that case, the system simply specifies one of the choices. Table RS.VI.3 lists fastest minimum-width types, format specifiers, and value limits.

Table RS.VI.3. Fastest Minimum-Width Types

Type Name

printf() Specifier

scanf() Specifier

Minimum Value

Maximum Value

int_fast8_t

PRIFASTd8

SCNFASTd8

INT_FAST8_MIN

INT_FAST8_MAX

int_fast16_t

PRIFASTd16

SCNFASTd16

INT_FAST16_MIN

INT_FAST16_MAX

int_fast32_t

PRIFASTd32

SCNFASTd32

INT_FAST32_MIN

INT_FAST32_MAX

int_fast 64_t

PRIFASTd64

SCNFASTd64

INT_FAST64_MIN

INT_FAST64_MAX

uint_fast 8_t

PRIFASTu8

SCNFASTu8

0

UINT_FAST8_MAX

uint_fast 16_t

PRIFASTu16

SCNFASTu16

0

UINT_FAST16_MAX

uint_fast 32_t

PRIFASTu32

SCNFASTu32

0

UINT_FAST32_MAX

uint_fast 64_t

PRIFASTu64

SCNFASTu64

0

UINT_FAST64_MAX

Maximum-Width Types

Sometimes you may want the largest integer type available. Table RS.VI.4 lists these types. They may, in fact, be wider than long long or unsigned long long, because a system may provide additional types wider than the required types.

Table RS.VI.4. Maximum-Width Types

Type Name

printf() Specifier

scanf() Specifier

Minimum Value

Maximum Value

intmax_t

PRIdMAX

SCNdMAX

INTMAX_MIN

INTMAX_MAX

uintmax_t

PRIuMAX

SCBuMAX

0

UINTMAX_MAX

Integers That Can Hold Pointer Values

The inttypes.h header file (via the included stdint.h header file) defines two integer types, listed in Table RS.VI.5, that can hold pointer values accurately. That is, if you assign a type void * value to one of these types, and then assign the integer type back to the pointer, no information is lost. Either or both types might not exist.

Table RS.VI.5. Integer Types for Holding Pointer Values

Type Name

printf() Specifier

scanf() Specifier

Minimum Value

Maximum Value

intptr_t

PRIdPTR

SCNdPTR

INTPTR_MIN

INTPTR_MAX

uintptr_t

PRIuPTR

SCBuPTR

0

UINTPTR_MAX

Extended Integer Constants

You can indicate a long constant with the L suffix, as in 445566L. How do you indicate that a constant is type int32_t? Use macros defined in inttypes.h. For example, the expression INT32_C(445566) expands to a type int32_t constant. Essentially, the macro is a type cast to the underlying type—that is, to the fundamental type that represents int32_t in a particular implementation.

The macro names are formed by taking the type name, replacing the _t with _C, and making all the letters uppercase. For example, to make 1000 a type uint_least64_t constant, use the expression UINT_LEAST64_C(1000).