Solaris 64-bit Developer's Guide

Chapter 4 Converting Applications

Two basic issues that regard conversion arise for applications developers:

Trying to maintain a single source with as few #ifdefs as possible is usually better than trying to maintain multiple source trees. This chapter provides guidelines for writing code that works correctly in both 32-bit environments and 64-bit environments. At best, the conversion of current code might require only a recompilation and relinking with the 64-bit libraries. However, for those cases where code changes are required, this chapter discusses the tools that help make conversion easier.

Data Model

As stated previously, the biggest difference between the 32-bit environment and 64-bit environment is the change in two fundamental data types.

The C data-type model used for 32-bit applications is the ILP32 model, so named because ints, longs, and pointers are 32-bit. The LP64 data model is the C data-type model for 64-bit applications. This model was agreed upon by a consortium of companies across the industry. LP64 is so named because longs and pointers grow to 64-bit quantities. The remaining C types int, short, and char are the same as in the ILP32 model.

The following sample program, foo.c, directly illustrates the effect of the LP64 data model in contrast to the ILP32 data models. The same program can be compiled as either a 32–bit program or a 64–bit program.

#include <stdio.h>
int
main(int argc, char *argv[])
{
		(void) printf("char is \t\t%lu bytes\n", sizeof (char));
		(void) printf("short is \t%lu bytes\n", sizeof (short));
		(void) printf("int is \t\t%lu bytes\n", sizeof (int));
		(void) printf("long is \t\t%lu bytes\n", sizeof (long));
		(void) printf("long long is \t\t%lu bytes\n", sizeof (long long));
		(void) printf("pointer is \t%lu bytes\n", sizeof (void *));
		return (0);
} 

The result of 32–bit compilation is:


% cc -O -o foo32 foo.c
% foo32 
char is          1 bytes
short is         2 bytes
int is           4 bytes
long is          4 bytes
long long is     8 bytes
pointer is       4 bytes   

The result of 64–bit compilation is:


% cc -xarch=generic64 -O -o foo64 foo.c 
% foo64
char is          1 bytes
short is         2 bytes
int is           4 bytes
long is          8 bytes
long long is     8 bytes 
pointer is       8 bytes

Note –

The default compilation environment is designed to maximize portability, that is, to create 32–bit applications.


The standard relationship between C integral types still holds true.

sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long)

Table 4–1 lists the basic C types, and their corresponding sizes in bits in the data type models for both LP32 and LP64.

Table 4–1 Data Type Sizes in Bits

C data type 

ILP32 

LP64 

char

unchanged 

short

16 

unchanged 

int

32 

unchanged 

long

32 

64

long long

64 

unchanged 

pointer

32 

64

enum

32 

unchanged 

float

32 

unchanged 

double

64 

unchanged 

long double

128 

unchanged 

Some older 32-bit applications use int, long, and pointer types interchangeably. The size of longs and pointers grow in the LP64 data model. You need to be aware that this change alone can cause many 32-bit to 64-bit conversion problems.

In addition, declarations and casts become very important in showing what is intended. How expressions are evaluated can be affected when the types change. The effects of standard C conversion rules are influenced by the change in data-type sizes. To adequately show what is intended, you might need to declare the types of constants. Casts might also be needed in expressions to make certain that the expression is evaluated the way that you intended. Correct evaluation of expressions is particularly crucial in the case of sign extension, where explicit casting might be essential to achieve the intended effect.

Other problems arise with built-in C operators, format strings, assembly language, and compatibility and interoperability.

The rest of this chapter advises you how to overcome these problems by:

Implementing Single-Source Code

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.

Feature Test Macros

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).

Derived Types

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.

<sys/types.h> File

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> File

The 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:

These are discussed in more detail in the sections that follow.

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 indices. On the other hand, you should use fixed-width types for explicit binary representations of:

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 Defined by <inttypes.h>

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 Support

The lint program, available with the Sun Studio 10 compiler, can 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.

For more information about the capabilities of the C compilers and lint, see the Sun Studio 10: C User's Guide.

lint for 32–bit and 64–bit Environments

lint 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.

Starting with the Solaris 10 release, the -Xarch=amd64 option should be used to lint code intended to be run in the 64–bit AMD environment.


Note –

The -D__sparcv9 option to lint is no longer necessary and should not be used.


For a description of lint options, see the Sun Studio 10: C User's Guide.

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 can 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 Studio 10: C User's Guide or the lint(1) man page for more information.

Guidelines for Converting to LP64

When using lint(1), remember that not all problems result in lint(1) warnings, nor do all lint(1) warnings indicate that a change is required. Examine each possibility for intent. The examples that follow illustrate some of the more common problems you are likely to encounter when converting code. Where appropriate, the corresponding lint(1) warnings are shown.

Do Not Assume int and Pointers Are the Same Size

Since ints and pointers are the same size in the ILP32 environment, a lot of code relies on this assumption. Pointers are often cast to int or unsigned int for address arithmetic. Instead, pointers could be cast to long because long and pointers are the same size in both ILP32 and LP64 worlds. Rather than explicitly using unsigned long, use uintptr_t because it expresses the intent more closely and makes the code more portable, insulating it against future changes. For example,

char *p;
p = (char *) ((int)p & PAGEOFFSET);

produces the warning:

warning: conversion of pointer loses bits

Using the following code will produce the clean results:

char *p;
p = (char *) ((uintptr_t)p & PAGEOFFSET);

Do Not Assume int and long Are the Same Size

Because ints and longs were never really distinguished in ILP32, a lot of existing code uses them indiscriminately while implicitly or explicitly assuming that they are interchangeable. Any code that makes this assumption must be changed to work for both ILP32 and LP64. While an int and a long are both 32–bits in the ILP32 data model, in the LP64 data model, a long is 64–bits. For example,

int waiting;
long w_io;
long w_swap;
...
waiting = w_io + w_swap;

produces the warning:

warning: assignment of 64-bit integer to 32-bit integer

Sign Extension

Unintended sign extension is a common problem when converting to 64–bits. It is hard to detect before the problem actually occurs because lint(1) does not warn you about it. Furthermore, the type conversion and promotion rules are somewhat obscure. To fix unintended sign extension problems, you must use explicit casting to achieve the intended results.

To understand why sign extension occurs, it helps to understand the conversion rules for ANSI C. The conversion rules that seem to cause the most sign extension problems between 32-bit and 64-bit integral values are:

  1. Integral promotion

    A char, short, enumerated type, or bit-field, whether signed or unsigned, can be used in any expression that calls for an int. If an int can hold all possible values of the original type, the value is converted to an int. Otherwise, it is converted to an unsigned int.

  2. Conversion between signed and unsigned integers

    When a negative signed integer is promoted to an unsigned integer of the same or larger type, it is first promoted to the signed equivalent of the larger type, then converted to the unsigned value.

For a more detailed discussion of the conversion rules, refer to the ANSI C standard. Also included in this standard are useful rules for ordinary arithmetic conversions and integer constants.

When compiled as a 64-bit program, the addr variable in the following example becomes sign-extended, even though both addr and a.base are unsigned types.


Example 4–1 test.c

struct foo {
		unsigned int	base:19, rehash:13;  
};

main(int argc, char *argv[]) 
{
		struct foo	a;
		unsigned long addr;

		a.base = 0x40000;
		addr = a.base << 13;		/* Sign extension here! */
		printf("addr 0x%lx\n", addr);

		addr = (unsigned int)(a.base << 13);  /* No sign extension here! */
		printf("addr 0x%lx\n", addr);
}

This sign extension occurs because the conversion rules are applied as follows:

  1. a.base is converted from an unsigned int to an int because of the integral promotion rule. Thus, the expression a.base << 13 is of type int, but no sign extension has yet occurred.

  2. The expression a.base << 13 is of type int, but it is converted to a long and then to an unsigned long before being assigned to addr, because of the signed and unsigned integer promotion rule. The sign extension occurs when it is converted from an int to a long.


% cc -o test64 -xarch=v9 test.c
% ./test64
addr 0xffffffff80000000
addr 0x80000000
%

When this same example is compiled as a 32-bit program it does not display any sign extension:


% cc -o test32 test.c
% ./test32
addr 0x80000000
addr 0x80000000
%

Use Pointer Arithmetic Instead of Address Arithmetic

In general, using pointer arithmetic works better than address arithmetic because pointer arithmetic is independent of the data model, whereas address arithmetic might not be. It usually leads to simpler code as well. For example,

int *end;
int *p;
p = malloc(4 * NUM_ELEMENTS);
end = (int *)((unsigned int)p + 4 * NUM_ELEMENTS);

produces the warning:

warning: conversion of pointer loses bits

The following code will produce clean results:

int *end; 
int *p;
p = malloc(sizeof (*p) * NUM_ELEMENTS);
end = p + NUM_ELEMENTS;

Repacking a Structure

Extra padding may be added to a structure by the compiler to meet alignment requirements as long and pointer fields grow to 64 bits for LP64. For both the SPARCV9 ABI and the amd64 ABI, all types of structures are aligned to at least the size of the largest quantity within them. A simple rule for repacking a structure is to move the long and pointer fields to the beginning of the structure and rearrange the rest of the fields—usually, but not always, in descending order of size, depending on how well they can be packed. For example,

struct bar {
		int i;
		long j;
		int k;
		char *p;
};			/* sizeof (struct bar) = 32 */

For better results, use:

struct bar {
		char *p;
		long j;
		int i;
		int k;
};			/* sizeof (struct bar) = 24 */

Note –

The alignment of fundamental types changes between the i386 and amd64 ABIs. See Alignment Issues.


Check Unions

Be sure to check unions because their fields might have changed sizes between ILP32 and LP64. For example,

typedef union {
       double   _d;
       long _l[2];
} llx_t;

should be:

typedef union {
       double _d;
       int _l[2];
} llx_t;

Specify Constant Types

A loss of data can occur in some constant expressions because of lack of precision. These types of problems are very hard to find. Be explicit about specifying the type(s) in your constant expressions. Add some combination of {u,U,l,L} to the end of each integer constant to specify its type. You might also use casts to specify the type of a constant expression. For example,

int i = 32;
long j = 1 << i;		/* j will get 0 because RHS is integer expression */

should be:

int i = 32;
long j = 1L << i;

Beware of Implicit Declaration

For some compilation modes, the compiler might assume the type int for any function or variable that is used in a module and not defined or declared externally. Any longs and pointers used in this way are truncated by the compiler's implicit int declaration. The appropriate extern declaration for a function or variable should be placed in a header and not in the C module. The header should then be included by any C module that uses the function or variable. In the case of a function or variable defined by the system headers, the proper header should still be included in the code.

For example, because getlogin() is not declared, the following code:

int
main(int argc, char *argv[])
{
		char *name = getlogin()
		printf("login = %s\n", name);
		return (0);
}

produces the warnings:

warning: improper pointer/integer combination: op "="
warning: cast to pointer from 32-bit integer
implicitly declared to return int 
getlogin        printf   

For better results, use::

#include <unistd.h>
#include <stdio.h>
 
int
main(int argc, char *argv[])
{
		char *name = getlogin();
		(void) printf("login = %s\n", name);
		return (0);
}

sizeof is an unsigned long

In the LP64 environment, sizeof has the effective type of size_t which is implemented as an unsigned long. Occasionally, sizeof is passed to a function expecting an argument of type int, or is assigned or cast to an int. In some cases, this truncation might cause loss of data. For example,

long a[50];
unsigned char size = sizeof (a);

produces the warnings:

warning: 64-bit constant truncated to 8 bits by assignment
warning: initializer does not fit or is out of range: 0x190

Use Casts to Show Your Intentions

Relational expressions can be tricky because of conversion rules. You should be very explicit about how you want the expression to be evaluated by adding casts wherever necessary.

Check Format String Conversion Operation

The format strings for printf(3C), sprintf(3C), scanf(3C), and sscanf(3C) might need to be changed for long or pointer arguments. For pointer arguments, the conversion operation given in the format string should be %p to work in both the 32-bit and 64-bit environments. For example,

char *buf;
struct dev_info *devi;
...
(void) sprintf(buf, "di%x", (void *)devi);

produces the warning:

warning: function argument (number) type inconsistent with format
  sprintf (arg 3)     void *: (format) int

Use the following code to produce clean results:

char *buf;
struct dev_info *devi;
...
(void) sprintf(buf, "di%p", (void *)devi);

Also check to be sure that the storage pointed to by buf is large enough to contain 16 digits. For long arguments, the long size specification, l, should be prepended to the conversion operation character in the format string. For example,

    size_t nbytes;
    ulong_t align, addr, raddr, alloc;
    printf("kalloca:%d%%%d from heap got %x.%x returns %x\n", 
            nbytes, align, (int)raddr, (int)(raddr + alloc), (int)addr);

produces the warnings:

warning: cast of 64-bit integer to 32-bit integer
warning: cast of 64-bit integer to 32-bit integer
warning: cast of 64-bit integer to 32-bit integer

The following code will produce clean results:

    size_t nbytes;
    ulong_t align, addr, raddr, alloc;
    printf("kalloca:%lu%%%lu from heap got %lx.%lx returns %lx\n", 
            nbytes, align, raddr, raddr + alloc, addr);

Other Considerations

The remaining guidelines highlight common problems encountered when converting an application to a full 64–bit program.

Derived Types That Have Grown in Size

A number of derived types have changed so they represent 64-bit quantities in the 64-bit application environment. This change does not affect 32-bit applications; however, any 64-bit applications that consume or export data described by these types need to be reevaluated for correctness. An example of this is in applications that directly manipulate the utmpx(4) files. For correct operation in the 64-bit application environment, you should not attempt to directly access these files. Instead, you should use the getutxent(3C) and related family of functions.

A list of changed derived types is included in Appendix A, Changes in Derived Types.

Check for Side Effects of Changes

One problem to be aware of is that a type change in one area might result in an unexpected 64-bit conversion in another area. For example, in the case of a function that previously returned an int and now returns an ssize_t, all the callers need to be checked.

Check Whether Literal Uses of long Still Make Sense

Because a long is 32 bits in the ILP32 model and 64 bits in the LP64 model, there might be cases where what was previously defined as a long is neither appropriate nor necessary. In this case, it might be possible to use a more portable derived type.

Related to this, a number of derived types might have changed under the LP64 data model for the reason stated above. For example, pid_t remains a long in the 32-bit environment, but under the 64-bit environment, a pid_t is an int. For a list of derived types modified for the LP64 compilation environment, see Appendix A, Changes in Derived Types.

Use #ifdef for Explicit 32-bit Versus 64-bit Prototypes

In some cases, specific 32-bit and 64-bit versions of an interface are unavoidable. In the headers, these would be distinguishable by the use of the _LP64 or _ILP32 feature test macros. Similarly, code that is to work in 32-bit and 64-bit environments might also need to utilize the appropriate #ifdefs, depending on the compilation mode.

Algorithmic Changes

After code has been made 64-bit safe, review it again to verify that the algorithms and data structures still make sense. The data types are larger, so data structures might use more space. The performance of your code might change as well. Given these concerns, you might need to adapt your code appropriately.

Checklist for Getting Started

Assuming you need to convert your code to 64-bit, the following checklist might be helpful:

Sample Program

The following sample program, foo.c, directly illustrates the effect of the LP64 data model in contrast to the ILP32 data models. The same program can be compiled as either a 32–bit program or a 64–bit program.

#include <stdio.h>
int
main(int argc, char *argv[])
{
		(void) printf("char is \t\t%lu bytes\n", sizeof (char));
		(void) printf("short is \t%lu bytes\n", sizeof (short));
		(void) printf("int is \t\t%lu bytes\n", sizeof (int));
		(void) printf("long is \t\t%lu bytes\n", sizeof (long));
		(void) printf("long long is \t\t%lu bytes\n", sizeof (long long));
		(void) printf("pointer is \t%lu bytes\n", sizeof (void *));
		return (0);
} 

The result of 32–bit compilation is:


% cc -O -o foo32 foo.c
% foo32 
char is          1 bytes
short is         2 bytes
int is           4 bytes
long is          4 bytes
long long is     8 bytes
pointer is       4 bytes   

The result of 64–bit compilation is:


% cc -xarch=generic64 -O -o foo64 foo.c 
% foo64
char is          1 bytes
short is         2 bytes
int is           4 bytes
long is          8 bytes
long long is     8 bytes 
pointer is       8 bytes

Note –

The default compilation environment is designed to maximize portability, that is, to create 32–bit applications.