Typical errors of porting C++ code on the 64-bit platform

Storing of pointer addresses

A large number of errors during the migration on 64-bit systems are related to the change of a pointer size in relation to the size of usual integers. In the environment with the data ILP32 usual integers and pointers have the same size. Unfortunately the 32-bit code is based on this supposition everywhere. Pointers are often casted to int, unsigned int and other types improper to fulfill address calculations.

You should understand exactly that one should use only memsize types for integer pointers form. Preference should be given to uintptr_t type for it shows intentions more clearly and makes the code more portable saving it from changes in future

Let’s look at two small examples.

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

 

2) DWORD tmp = (DWORD)malloc(ArraySize); 
   ...
   int *ptr = (int *)tmp;

The both examples do not take into account that the pointer size may differ from 32-bits. They use the explicit type conversion which truncates high bits in the pointer and this is surely a mistake on the 64-bit system. Here are the corrected variants which use integer memsize type intptr_t and DWORD_PTR to store pointer addresses:

1) char *p;
   p = (char *) ((intptr_t)p & PAGEOFFSET);

 

2) DWORD_PTR tmp = (DWORD_PTR)malloc(ArraySize); 
   ...
   int *ptr = (int *)tmp;

The danger of the two examples studied is that the fail in the program may be found much time later. The program may work absolutely correctly with a small data size on the 64-bit system while the truncated addresses lie in first 4 Gb of memory. And then on launching the program for large production aims there will be the memory allocation out of first 4 Gb. The code given in the examples will cause an undefined behavior of the program on the object out of first 4 Gb while processing the pointer.

The following code won’t hide and will show up at the first execution.

void GetBufferAddr(void **retPtr) {
  ...
  // Access violation on 64-bit system
  *retPtr = p;
}

unsigned bufAddress;
GetBufferAddr((void **)&bufAddress); 

The correction is also in the choice of the type capable to store the pointer.

uintptr_t bufAddress;
GetBufferAddr((void **)&bufAddress); //OK

There are situations when storing of the pointer address into a 32-bit type is just necessary. Mostly such situations appear when it is necessary to work with old API functions. For such cases one should resort to special functions LongToIntPtr, PtrToUlong etc.

In the end I’d like to mention that it will be a bad style to store the pointer address into types which are always equal 64-bits. One will have to correct the code shown further again when 128-bit systems will appear.

PVOID p;
// Bad style. The 128-bit time will come.
__int64 n = __int64(p);
p = PVOID(n);

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Java is to JavaScript what Car is to Carpet.” - Chris Heilmann