» Sign in

» Overview
» Making code 64-bit clean
» Using integral types defined in <inttypes.h>
» Guidelines for using <inttypes.h>
» Using <portal.h>
» Using portable bit masks
» Using pstat(2) instead of /dev/kmem
» Getting configurable system information
» Isolating system-specific code
» Using system-specific include files
» See also


This document provides information for developing code that is portable between HP-UX 32-bit, 64-bit, and other UNIX systems. It includes using industry-standard header files and HP-UX extensions that isolate platform-specific code.

Making code 64-bit clean

Applications should be 64-bit clean if they are targeted for execution on both ILP32 and LP64. Tools to help make code 64-bit clean include:

  • using lint,
  • compiling in ANSI C or aC++, and
  • using the portable header file <inttypes.h>.

To make code 64-bit clean, follow these guidelines:

  • Use the same source code and header files for both 32-bit and 64-bit programs.
  • Use appropriate data types consistently and strictly.

    For example, use off_t consistently for file offsets and fpos_t for file positions.

  • Use integral types in <inttypes.h>, where applicable, instead of int and long
  • Use fixed/scalable width integral types, algorithms, and masks as appropriate.

    Fixed types remain a consistent size on 32-bit and 64-bit platforms. For example, use int32_t, defined in <inttypes.h>, if ints and longs should be 32 bits in your application. Scalable types can grow and scale without modification to future architectures.

  • Perform boundary checking on integral type ranges.
  • Update 64-bit code in cases where 32-bit and 64-bit processes share the same memory segment.

Using integral types defined in <inttypes.h>

The <inttypes.h> header files provide the following features that help you write portable code:

  • integral type definitions
  • macros for constants
  • macros for printf()/scanf() specifiers

When you use <inttypes.h>, you can maintain one set of source files for both data models. Using the typedefs and constants included in these header files protects your code from underlying data model changes. It also reduces the need to #ifdef platform-specific code, which improves the readability of your source code.

There are two <inttypes.h> header files. The following information is included in <sys/_inttypes.h>:

  • basic integral data types for 8, 16, 32, and 64 bits
  • macros that can create constants of a specific size and sign

The following information is included in <inttypes.h>:

  • data types for pointers
  • data types for the most efficient integer types
  • data types for the largest integer types
  • data types for the smallest integral types of at least 8, 16, 32, and 64 bits

Integer data types with consistent lengths

The basic types in <sys/_inttypes.h> have consistent lengths on UNIX 32-bit and 64-bit platforms. Use the basic data types in <sys/_inttypes.h> instead of the standard C language types whenever you want data types to remain the same size across different architectures.

This table shows the basic integral types in <sys/_inttypes.h>:

C basic integer Types
Type definition\nameDescription
int8_t8-bit signed integer
uint8_t8-bit unsigned integer
int16_t16-bit signed integer
uint16_t16-bit unsigned integer
int32_t32-bit signed integer
uint32_t32-bit unsigned integer
int64_t64-bit signed integer
uint64_t64-bit unsigned integer

The pointer data types are signed and unsigned integer data types that are large enough to hold a pointer. A pointer can be moved to or from these data types without corruption.

Pointer types in Inttypes
Type definition\
32-bit mode64-bit mode
intptr_t32-bit signed integer64-bit signed integer
uintptr_t32-bit unsigned integer64-bit unsigned integer
Using integer data types with consistent lengths

One way to improve portability of programs that require integral data items to be 32-bits or 64-bits long, regardless of the hardware platform, is to include the <inttypes.h> header file, and to make the following substitutions:

Instead ofUse
unsigned shortuint16_t
unsigned intuint32_t
longint32_t or int64_t
unsigned longuint32_t or unint64_t
long longint64_t
unsigned long longuint64_t
Excerpts from <inttypes.h> and <sys/_inttypes.h>

Here are some typedefs in <sys/_inttypes.h> that conform to the XPG5 standard:

/* 32-bit signed integral type */typedef int int32_t;/* largest signed integral type */typedef int64_t intmax_t;/* 64-bit unsigned integral type */typedef unsigned long uint64_t;

Here are some HP extensions in <inttypes.h>:

/* Create a 32-bit signed constant */#define INT32_C(_c)/* Create a 32-bit unsigned constant */#define UINT32_C(_c)/* If this macro tests false, the   unsigned 64-bit data type is   not supported on the platform. */#define UINT64_MAX/* print specifier for a 64-bit   integral value */#define PRId64/* scanf specifier for a 64-bit   integral value */#define SCNd64
Examples using consistent length data types and macros

The following code uses constructs that are not 64-bit clean:


#include <limits.h>#include <stdio.h>       . . .long value;   long mask = 1L <<     ((sizeof(long) * CHAR_BIT)-1);   scanf("%ld", &value);   /* Test for max 32-bit value. */   if (value = 0x7fffffff)     printf("Number is %ld\n", value);   /* Handle bad value. */   if (mask & value)           . . .

The after code has been made 64-bit clean by using <inttypes.h>:


#include <limits.h>#include <stdio.h>#include <inttypes.h>       . . .int32_t value;int32_t mask = INT32_C(1) <<  ((sizeof(int32_t) * CHAR_BIT)-1);scanf("%", SCNd32, &value);/* Test for max 32-bit value.*/if (value = INT32_MAX)  printf(    "Maximum 32-bit signed integer: %",    PRId32, "\n", value);if (mask & value)   /* Handle bad value. */. . .

Example 2: Formatted I/O using printf(3S)

The PRIxn constants defined in <inttypes.h> are used to select the correct printf() formatting option for the data types defined in <inttypes.h>. For example:


long result_value;printf ("Result = %lx\n", result_value);


#include <inttypes.h>int32_t result_value;printf ("Result = %"       PRIx32 "\n", result_value);

The before code prints a 32-bit number on 32-bit platforms and a 64-bit number on a 64-bit platform. The after code prints a 32-bit number, regardless of the platform. The macro PRIx32 from <inttypes.h> is used instead of the literal x. PRIx32 is then defined as the appropriate formatting symbol.

int_fast data types with defined minimum sizes

When execution speed of programs is important, use the int_fastx_t data types in <sys/_inttypes.h>. You select data types based on the minimum integer size required by your application. The <inttype.h> header file then maps the int_fast data types to the fastest appropriate data type for the target hardware platform. These data types include:

fastest signed integral data type of at least 16-bits
fastest signed integral data type at least 8-bits long
fastest unsigned integral data type at least 8-bits long
fastest signed integral data type at least 16-bits long
fastest unsigned integral data type at least 16-bits long
fastest signed integral data type at least 32-bits long
fastest unsigned integral data type at least 32-bits long
Example using int_fastn_t data types

Suppose you need a loop to execute as fast as possible on a target platform. You could change the following:


int i, j;j = 0;for  (i = 1; i<=127; i++){     j = j+i;}

to use the most efficient and portable integer data types as follows:


int_fast8_t i;int_fast16_t j = 0;for (i =1; i<=INT8_MAX; i++){     j = j+i;}

Guidelines for using <inttypes.h>

When porting to 64 bits, only use 64-bit data types when they are required. This data type may not be processed efficiently on some platforms. Programs may run slower and executable file sizes may be bigger.

Here are additional guidelines:

  • Use int32_t for integers that must be 32-bits in length on 64-bit systems in order for programs to work correctly. Because <inttypes.h> is available on both 32-bit and 64-bit HP-UX platforms, int32_t works in both environments.
  • Use the smallest integral type possible in order to control the size of the application.
  • Use the intfastn_t data types for counters and counter loops that need to be fast due to frequent expression evaluations (such as incrementing).
  • Use the constant that matches the integer type definition. For example, use the constant UINT64_MAX with uint_fast64_t. Do not use INT_MAX or UINT_MAX with uint_fast64_t.
  • Use intmax_t or uintmax_t for items that must be the largest available integral type as specified by the compiler. The sizes of these data types may change in a future release, but they will always be the largest integral type supported on the platform, regardless of possible performance implications.
  • Limit the use of x64_t data types. These types cannot automatically take advantage of potentially higher limits for future integral sizes as with intmax_t and uintmax_t.
  • Older 32-bit systems may not have a 64-bit integral data type. Therefore, int64_t and uint64_t data types may need to be protected by #ifdefs if the source code is shared between 64-bit and older 32-bit systems.
  • Convert long long to int64_t. Convert unsigned long long to uint64_t.
  • End user APIs reflect the data types defined by standards such as X/Open, POSIX.2, or legacy HP definitions. They will not cause source level incompatibilities. The function prototype looks the same in any integral data type model. Therefore, your application will be protected when there are changes to the underlying size of data types. For example, the function prototype for lseek():
    off_t lseek(  int fildes, off_t offset, int whence);int fseek(  FILE *stream, long int  offset, int whence);
    looks the same on a 32-bit or 64-bit system.
  • Use the same type definition names supported by the API definition. For example, use off_t in your code offsets. Do not assume off_t is an int, long, or any other data type.
  • Data declarations related to API parameters or return values should be of the same consistent data definition as defined by the function prototype.
  • Integral types (for example, long) that must be 32-bits on a 32-bit system and 64-bits on a 64-bit system can be left as long. These types will automatically be declared with the correct size.
  • Use scalable masks with scalable typedefs and fixed size masks with fixed size typedefs. (See "Using Portable Bit Masks" for examples on scalable and fixed size masks.)

Using portal.h

The <sys/portal.h> header file helps you to write code that is portable across HP-UX 32-bit and 64-bit systems. This header file contains macros for setting bit masks and sign bits of data types defined in <inttypes.h> and for convenience it includes the header files <limits.h> and <inttypes.h>.

To #include this header file, type:

#include <sys/portal.h>

Examples of some macros contained in portal.h follow:

  • SET_MASK_BIT(bit_num, data_type) -- Creates a mask that has one bit set.

    For example:

    SET_MASK_BIT(SIGN_BIT(int64_t), int64_t)

    turns on the high bit in a 64-bit integer

  • SIGN_BIT (data_type)--Returns the bit position of the sign bit for the specified type.

    For example:


    returns the position of the sign bit in a 32-bit integer.

  • SIGN_EXTEND (value, old_type, new_type)--Sign extends from one data type to another.

    For example:

    char c;int64_t i;i = SIGN_EXTEND(c, char, int64_t);

    converts the 8-bit integer stored in a char data type to a 64-bit integer and correctly extends the sign.

Using portable bit masks

Use scalable masks with scalable typedefs. Scalable types, such as int and long, may be different sizes on different platforms. For example, use the portable bit mask construct:

  • Solution 1:
    #include <inttypes.h>#ifdef __LP64__  int64_t node_id=0xfffffffffffffffc;#else  int32_t node_id=0xfffffffc;


  • Solution 2:
    #include <inttypes.h>intmax_t node_id = ~0x3;

instead of the non-portable construct:

long node_id =  0xfffffffc;

When compiled on an HP-UX 32-bit platform, the first two constructs above result in the intended value:

1111 1111 1111 1111 1111 1111 1111 1100

However, when compiled on an HP-UX 64-bit platform, the first and second construct produce the correct binary value:

1111 1111 1111 1111 1111 1111 1111 1111   1111 1111 1111 1111 1111 1111 1111 1100

while the third construct generates an incorrect bit mask value:

0000 0000 0000 0000 0000 0000 0000 0000   1111 1111 1111 1111 1111 1111 1111 1100

Use fixed size masks with fixed size type definitions. For example, if you want a 32-bit mask use the portable construct:

#include <inttypes.h>int32_t intmask = INT32_MAX;

instead of:

long intmask = 0x7fffffff;

Using pstat(2) instead of /dev/kmem

Avoid writing programs that read system files such as /dev/kmem directly. These files may have different formats on different systems. Instead, use the pstat(2) routines to get system information. These routines were first introduced in the HP-UX 10.0 release. Using pstat(2), information can be retrieved about the following:

  • memory--static, dynamic, shared, virtual
  • system CPU processors
  • system processes and messaging
  • disks, swap space, and file systems
  • semaphores

The pstat(2) routines are HP-specific routines and are not available on other vendor platforms.

A wide (64 bit) version of the pstat(2) interfaces are available for narrow (32 bit) applications to use. A narrow application could use the flag -D_PSTAT64 at compile time to switch to the wide interfaces.

If the wide version is not used, the following pstat_get*(2) system calls may fail, with errno set to EOVERFLOW, when invoked within 32-bit applications. This is because within 64-bit HP-UX, many parameters, limits and addresses are 64-bit values and they cannot fit into fields of the corresponding struct pst_* data structure.


Getting configurable system information

Instead of hardcoding system values, use any of the portable alternatives:

  • Use sysconf(2), confstr(3C), and getconf(1) to get configurable system information, such as determining if the underlying operating system is 64 bits or 32 bits.
  • Use pathconf(2) to get configurable pathnames for files and directory values.
  • Use <limits.h> to get static system limits.

Beginning with the 11.00 release, sysconf(2) returns additional information:

  • the processor implementation
  • the operating system execution mode--32 bit or 64 bit
  • whether the system is capable of running a 64-bit operating system

Additionally, confstr(3C) (configuration string) returns the appropriate compiler options, libraries, and lint options to build and check an application using a 32-bit or 64-bit programming environment.

Isolating system-specific code

Use conditional directives to isolate sections of platform or release-specific code. This is useful when the number of lines of release or platform-specific source code is small in relation to the number of lines of common code. You could, for example, encapsulate code that is different between the 32-bit and 64-bit HP-UX with conditional directives.

The following example uses a #define macro within a conditional directive:

#ifdef __LP64__. . .  /* LP64 specific code goes here  */#else. . .  /* ILP32 specific code goes here */#endif. . .  /* Code common to both platforms          goes here                     */

Using system-specific include files

Sometimes platform or release-specific source code differences are large. These differences can be isolated in different #include files and then be referenced by conditional directives.

Here is an example of including 32-bit or 64-bit type definitions depending on the word size of the machine:

#include <limits.h>#if WORD_BIT > 32   . . .      /* integers must be 64-bit */#   include "typedefs_64.h"#else    . . .     /* integers must be 32-bit */#   include "typedefs_32.h"#endif        /* 32-bit environment */

See also

» IPF Software Developer's Portability Checklist
» IPF Software Developer's Performance Checklist

Manage My AllianceOne Membership

 » Sign in
 » Join AllianceOne
 » Contact us