There are several possible 64-bit programming model choices depending on how the Standard C data types, such as pointers and integers (long, int, and short), are represented and on whether or not nonstandard data types are added. Recall that ANSI Standard C does not strictly define the data type sizes, although it does require that a long int have at least as many bits as an int, which, in turn, must have at least as many as a short int.
The Goal
The objective is to have a single definition for the Windows API (that is, both Win32 and Win64), therefore allowing for a single source code base. Some source code changes may be required to use this single definition, but the changes should be minimized.
Microsoft selected the LLP64 model (long data type and 64-bit pointer), which is usually referred to simply as the P64 model. In particular, for both signed and unsigned data, we have the following definitions for the standard data types, which apply to both signed and unsigned data.
-
A char is 8 bits, and a wchar_t data item is 16 bits.
-
A short int requires 16 bits.
-
An int is 32 bits.
-
A long int is also 32 bits.
-
A pointer of any type, such as PVOID, is 64 bits.
Additional data types are provided when you need to specify the length. Thus, _int16, _int32, and _int64 are all data types recognized by the Microsoft compiler.
The tables in this chapter are taken directly from the on-line help and represent the Windows Uniform Data Model. The type definitions can be found in BASETSD.H, part of the Visual Studio.NET (Version 7.0) and Version 6.0 systems.
The fixed-precision data types provide a length suffix to familiar Win32 types such as DWORD and LONG, as shown in Table 16-1.
Table 16-1. The Fixed-Precision Data Types |
Type
|
Definition
|
DWORD32
|
32-bit unsigned integer
|
DWORD64
|
64-bit unsigned integer
|
INT32
|
32-bit signed integer
|
INT64
|
64-bit signed integer
|
LONG32
|
32-bit signed integer
|
LONG64
|
64-bit signed integer
|
UINT32
|
Unsigned INT32
|
UINT64
|
Unsigned INT64
|
ULONG32
|
Unsigned LONG32
|
ULONG64
|
Unsigned LONG64
|
Pointer Precision Data Types
Quoting from a Microsoft paper, "The New Data Types" (available at the Microsoft Web site), "As the pointer precision changes (that is, as it becomes 32 bits with Win32 code and 64 bits with Win64 code), these data types reflect the precision accordingly. Therefore, it is safe to cast a pointer to one of these types when performing pointer arithmetic; if the pointer precision is 64 bits, the type is 64 bits. The count types also reflect the maximum size to which a pointer can refer." These types, then, allow integer sizes to track pointer sizes and are sometimes called polymorphic data types or platform scaled types. Table 16-2, from the same paper, shows the pointer precision data types.
Table 16-2. The Pointer Precision Data Types |
Type
|
Definition
|
DWORD_PTR
|
Unsigned long type for pointer precision.
|
HALF_PTR
|
Half the size of a pointer. Use within a structure that contains a pointer and two small fields.
|
INT_PTR
|
Signed integral type for pointer precision.
|
LONG_PTR
|
Signed long type for pointer precision.
|
SIZE_T
|
The maximum number of bytes to which a pointer can refer. Use for a count that must span the full range of a pointer.
|
SSIZE_T
|
Signed SIZE_T.
|
UHALF_PTR
|
Unsigned HALF_PTR.
|
UINT_PTR
|
Unsigned INT_PTR.
|
ULONG_PTR
|
Unsigned LONG_PTR.
|
SIZE_T is the most important of these data types and is used in Chapter 5 to describe memory block sizes.
Finally, note that a Windows HANDLE is 64 bits in Win64.
Example: Using Pointer Precision Data Types
The thread argument passed to a thread function by CreateThread and _beginthreadex (see Chapter 7) is a PVOID pointer. In some situations, the programmer may wish only to pass an integer value indicating, for example, the thread's number or an index to a global table. The thread function, which interprets the parameter as an unsigned integer, might be written as follows:
DWORD WINAPI MyThreadFunc (PVOID Index_PTR)
{
DWORD_PTR Index;
...
Index = (DWORD_PTR) Index_PTR;
...
}
Similarly, knowing that the actual argument is an integer, you might write the main parent thread this way:
...
DWORD_PTR Ix;
...
for (Ix = 0; Ix < NumThreads; Ix++) {
hTh [Ix] = _beginthreadex (NULL, 0, MyThreadFunc,
(PVOID) Ix, 0, NULL);
...
}
Notice that existing code should be changed as required. There will be more about this later in the Legacy Code Migration section.
A Caution
Do not expect to have the full virtual address space, at least in initial implementations. Virtual address space may be constrained to values such as 512GB, indicating a limitation of 39 bits. Over time, this upper bound can be expected to change as processors and systems evolve.
Windows and UNIX Divergence
Windows and UNIX have selected different strategies. Most UNIX vendors implement the LP64 model, which means that both long and pointer data types are 64 bits. This is sometimes called the I32, LP64 model to emphasize that int data types are still 32 bits. The divergence, then, is simply the length of long integers. What is more, the data types in Tables 16-1 and 16-2 are unique to Windows.
Both models represent reasonable solutions, and the UNIX choice is justified in the "Aspen" white paper listed in the Additional Reading section. It would be convenient, however, if both OSs used the same conventions.
|
Share with your friends: |