Fixed-Width Integer Types
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 indexes. On the other hand, 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 Types
Other 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.
Note - Use of uintptr_t for casting is usually safer than intptr_t, especially for comparisons.
Constant Macros
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".
Limits
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.
Format String Macros
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.
Tools
Sun Microsystems has provided a new version of the lint(1) program with the 5.0 version of Sun WorkShop Compilers C. It has been enhanced to detect potential 64-bit problems and is useful in making code 64-bit safe. In addition, the -v option to the C compiler can be very helpful. It tells the compiler to perform additional and stricter semantic checks. It also enables certain lint-like checks on the named files.
When you clean up code to be 64-bit safe, use the Solaris header files that have the correct definition of the derived types and data structures for the 64-bit world.
For more information on the debugging capabilities of the C compilers and lint(1), see the Sun WorkShop C User's Guide.
lint(1)
lint(1) can be used on both 32-bit and 64-bit code. Use the -errchk=longptr64 option for code that is intended to be run in both 32-bit and 64-bit environments. The -errchk=longptr64 option checks portability to an environment in which the size of long integers and pointers is 64 bits and the size of plain integers is 32 bits.
The -Xarch=v9 option should be used to lint code intended to be run in the 64-bit SPARC environment. Use the -errchk=longptr64 option together with the -Xarch=v9 option to generate warnings about potential 64-bit problems for code to be run on 64-bit SPARC.
Note - The -D__sparcv9 option to lint is no longer necessary and should not be used.
When warnings are generated, lint(1) prints the line number of the offending code, a warning message that describes the problem, and notes whether a pointer was involved. It can also indicate the sizes of types involved. The fact that a pointer is involved and the size of the types can be useful in finding specific 64-bit problems and avoiding the pre-existing problems between 32-bit and smaller types.
Note - Though lint gives warnings about potential 64-bit problems, it cannot detect all problems. You must remember that not all warnings generated by lint are true 64-bit problems. In many cases, code that generates a warning can be intentional and correct for the application.
The sample program and lint(1) output below illustrate most of the lint warnings that arise in code that is not 64-bit clean.
1 #include <inttypes.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 5 static char chararray[] = "abcdefghijklmnopqrstuvwxyz"; 6 7 static char *myfunc(int i) 8 { 9 return(& chararray[i]); 10 } 11 12 void main(void) 13 { 14 int intx; 15 long longx; 16 char *ptrx; 17 18 (void) scanf("%d", &longx); 19 intx = longx; 20 ptrx = myfunc(longx); 21 (void) printf("%d\n", longx); 22 intx = ptrx; 23 ptrx = intx; 24 intx = (int)longx; 25 ptrx = (char *)intx; 26 intx = 2147483648L; 27 intx = (int) 2147483648L; 28 ptrx = myfunc(2147483648L); 29 } (19) warning: assignment of 64-bit integer to 32-bit integer (20) warning: passing 64-bit integer arg, expecting 32-bit integer: myfunc(arg 1) (22) warning: improper pointer/integer combination: op "=" (22) warning: conversion of pointer loses bits (23) warning: improper pointer/integer combination: op "=" (23) warning: cast to pointer from 32-bit integer (24) warning: cast from 64-bit integer to 32-bit integer (25) warning: cast to pointer from 32-bit integer (26) warning: 64-bit constant truncated to 32 bits by assignment (27) warning: cast from 64-bit integer constant expression to 32-bit integer (28) warning: passing 64-bit integer constant arg, expecting 32-bit integer: myfunc(arg 1) function argument ( number ) type inconsistent with format scanf (arg 2) long * :: (format) int * t.c(18) printf (arg 2) long :: (format) int t.c(21) |
(The lint warning that arises from line 27 of this code sample is issued only if the constant expression will not fit into the type into which it is being cast.)
Warnings for a given source line can be suppressed by placing a /*LINTED*/ comment on the previous line. This is useful where you have really intended the code to be a specific way. An example might be in the case of casts and assignments. Exercise extreme care when using the /*LINTED*/ comment because it can mask real problems. Refer to the Sun WorkShop C User's Guide or the lint(1) man page for more information.