The sections that follow describe some of the resources available to application developers that help you write single-source code that supports both 32–bit and 64–bit compilation.
The system include files <sys/types.h> and <inttypes.h> contain constants, macros, and derived types that are helpful in making applications 32-bit and 64-bit safe. While a detailed discussion of these is beyond the scope of this document, some are discussed in the sections that follow, as well as in Appendix A, Changes in Derived Types.
An application source file that includes <sys/types.h> makes the definitions of the programming model symbols, _LP64 and _ILP32, available through inclusion of <sys/isa_defs.h>.
For information about preprocessor symbols (_LP64 and _ILP32) and macros (_LITTLE_ENDIAN and _BIG_ENDIAN6), see types(3HEAD).
Using the system derived types helps make code 32-bit and 64-bit safe, since the derived types themselves are safe for both the ILP32 and LP64 data models. In general, using derived types to allow for change is good programming practice. Should the data model change in the future, or when porting to a different platform, only the system derived types need to change rather than the application.
The <sys/types.h> header contains a number of basic derived types that should be used whenever appropriate. In particular, the following are of special interest:
clock_t
The type clock_t
represents the system times in clock
ticks.
dev_t
The type dev_t
is used for device numbers.
off_t
The type off_t
is used for file sizes and offsets.
ptrdiff_t
The type ptrdiff_t
is the signed integral type for the
result of subtracting two pointers.
size_t
The type size_t
is for the size, in bytes, of objects
in memory.
ssize_t
The signed size type ssize_t
is used by functions that
return a count of bytes or an error indication.
time_t
The type time_t
is used for time in seconds.
All of these types remain 32-bit quantities in the ILP32 compilation environment and grow to 64-bit quantities in the LP64 compilation environment.
The use of some of these types is explained in more detail later in this chapter under Guidelines for Converting to LP64.
<inttypes.h>
FileThe include file <inttypes.h> was added to the Solaris 2.6 release to provide constants, macros, and derived types that help programmers make their code compatible with explicitly sized data items, independent of the compilation environment. It contains mechanisms for manipulating 8-bit, 16-bit, 32-bit, and 64-bit objects. The file is part of an ANSI C proposal and tracks the ISO/JTC1/SC22/WG14 C committee's working draft for the revision of the current ISO C standard, ISO/IEC 9899:1990 Programming Language – C.
The basic features provided by <inttypes.h> are:
A set of fixed-width integer types
uintptr_t
and other helpful types
Constant macros
Limits
Format string macros
These are discussed in more detail in the sections that follow.
The fixed-width integer types provided by <inttypes.h> include
both signed and unsigned integer types, such as int8_t
, int16_t
, int32_t
, int64_t
, uint8_t
, uint16_t
, uint32_t
, and uint64_t
. Derived types defined
as the smallest integer types that can hold the specified number of bits include int_least8_t
, int_least64_t
, uint_least8_t
, uint_least64_t
.
These fixed-width types should not be used indiscriminately.
For example, int
can continue to be used for such things as loop counters
and file descriptors, and long
can be used for array indices. On the
other hand, you should use fixed-width types for explicit binary representations of:
On-disk data
Over-the-wire data
Hardware registers
Binary interface specifications (that have explicitly sized objects or involve sharing or communication between 32–bit and 64–bit programs)
Binary data structures (that are used by 32–bit and 64–bit programs through shared memory, files, and so on)
uintptr_t
and Other Helpful TypesOther useful types provided by <inttypes.h> include signed
and unsigned integer types large enough to hold a pointer. These are given as intptr_t
and uintptr_t
. In addition, intmax_t
and uintmax_t
are defined to be the longest (in bits) signed and unsigned integer
types available.
Using the uintptr_t
type as the integral type for pointers is a
better option than using a fundamental type such as unsigned long
. Even
though an unsigned long
is the same size as a pointer in both the ILP32
and LP64 data models, the use of the uintptr_t
requires only the definition
of uintptr_t
to change when a different data model is used. This makes
it portable to many other systems. It is also a clearer way to express your intentions
in C.
The intptr_t
and uintptr_t
types are extremely useful
for casting pointers when you want to do address arithmetic. They should be used instead
of long
or unsigned long
for this purpose.
Use of uintptr_t
for casting is usually safer than intptr_t
, especially for comparisons.
Macros are provided to specify the size and sign of a given constant. The macros are INT8_C(c), ..., INT64_C(c), UINT8_C(c),..., UINT64_C(c). Basically, these macros place an l, ul, ll, or ull at the end of the constant, if necessary. For example, INT64_C(1) appends ll to the constant 1 for ILP32 and an l for LP64.
Macros for making a constant the biggest type are INTMAX_C(c) and UINTMAX_C(c). These macros can be very useful for specifying the type of constants described in Guidelines for Converting to LP64.
The limits defined by <inttypes.h> are constants specifying
the minimum and maximum values of various integer types. This includes minimum and
maximum values of each of the fixed-width types, such as INT8_MIN
,..., INT64_MIN
, INT8_MAX
,..., INT64_MAX
, and their unsigned
counterparts.
The minimum and maximum for each of the least-sized types are given, too. These
include INT_LEAST8_MIN
,..., INT_LEAST64_MIN
, INT_LEAST8_MAX
,..., INT_LEAST64_MAX
, and their unsigned counterparts.
Finally, the minimum and maximum value of the largest supported integer types
are defined. These include INTMAX_MIN
and INTMAX_MAX
and
their corresponding unsigned versions.
Macros for specifying the printf and scanf format
specifiers are also provided in <inttypes.h> . Essentially,
these macros prepend the format specifier with an l or ll to specify the argument as a long
or long long
,
given the number of bits in the argument, which is built into the name of the macro.
Macros for printf(3C) format specifiers exist for printing 8-bit, 16-bit, 32-bit, and 64-bit integers, the smallest integer types, and the biggest integer types, in decimal, octal, unsigned, and hexadecimal. For example, printing a 64–bit integer in hexadecimal notation:
int64_t i; printf("i =%" PRIx64 "\n", i);
Similarly, there are macros for scanf(3C) format specifiers for reading 8-bit, 16-bit, 32-bit, and 64-bit integers and the biggest integer type in decimal, octal, unsigned, and hexadecimal. For example, reading an unsigned 64–bit decimal integer:
uint64_t u; scanf("%" SCNu64 "\n", &u);
Do not use these macros indiscriminately. They are best used in conjunction with the fixed-width types. Refer to the section Fixed-Width Integer Types for more details.