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

Mixed use of simple integer types and memsize types

Mixed use of memsize and non-memsize types in expressions may cause incorrect results on 64-bit systems and be related to the change of the input values rate. Let’s study some examples.

size_t Count = BigValue;
for (unsigned Index = 0; Index != Count; ++Index)
{ ... }  

This is an example of an eternal loop if Count > UINT_MAX. Suppose this code worked on 32-bit systems with the range less than UINT_MAX iterations. But a 64-bit variant of the program may process more data and it can demand more iterations. As far as the values of the variable Index lie in range [0..UINT_MAX] the condition "Index != Count" will never be executed and this causes the infinite loop.

Another frequent error is a record of the expressions of the following kind:

int x, y, z;
intptr_t SizeValue = x * y * z;

Similar examples were discussed earlier when during the calculation of values with the use of non-memsize types an arithmetic overflow occurred. And the last result was incorrect. Search and correction of the given code is made more difficult because compilers do not show any warning messages on it as a rule. From the point of view of C++ language this is absolutely correct construction. Several variables of int type are multiplied and after that the result is implicitly converted to intptr_t type and assignment occurs.

Let’s give an example of a small code which shows the danger of inaccurate expressions with mixed types (the results are got with the use Microsoft Visual C++ 2005, 64-bit compilation mode).

int x = 100000;
int y = 100000;
int z = 100000;
intptr_t size = 1;                  // Result:
intptr_t v1 = x * y * z;            // -1530494976
intptr_t v2 = intptr_t(x) * y * z;  // 1000000000000000
intptr_t v3 = x * y * intptr_t(z);  // 141006540800000
intptr_t v4 = size * x * y * z;     // 1000000000000000
intptr_t v5 = x * y * z * size;     // -1530494976
intptr_t v6 = size * (x * y * z);   // -1530494976
intptr_t v7 = size * (x * y) * z;   // 141006540800000
intptr_t v8 = ((size * x) * y) * z; // 1000000000000000
intptr_t v9 = size * (x * (y * z)); // -1530494976

It is necessary that all the operands in such expressions have been converted to the type of larger capacity in time. Remember that the expression of the kind

intptr_t v2 = intptr_t(x) * y * z;

does not promise the right result. It promises only that "intptr_t(x) * y * z" expression will have intptr_t type. The right result shown by this expression in the example is good luck caused by a particular compiler version and occasional process.

The order of the calculation of an expression with operators of the same priority is not defined. To be more exact, the compiler can calculate sub-expressions in such an order which it considers to be more efficient even if sub-expressions cause (side effect). The order of the appearing of side effects is not defined. Expressions including communicative and association operations (*, +, &, |, ^), may be converted in a free way even if there are brackets. To assign the strict order of the calculation of the expression it is necessary to use the explicit temporary variable.

That’s why if the result of the expression should be of memsize type, only memsize types must participate in the expression. The right variant:

intptr_t v2 = intptr_t(x) * intptr_t(y) * intptr_t(z); // OK!

Notice. If you have a lot of integer calculations and control over the overflows is an important task for you we offer to pay your attention to SafeInt class, the realization and description of which can be found in MSDN.

Mixed use of types may occur in the change of the program logic.

ptrdiff_t val_1 = -1;
unsigned int val_2 = 1;
if (val_1 > val_2)
  printf ("val_1 is greater than val_2\n");
else
  printf ("val_1 is not greater than val_2\n");

//Output on 32-bit system: "val_1 is greater than val_2"
//Output on 64-bit system: "val_1 is not greater than val_2"

On the 32-bit system the variable val_1 according to C++ rules was extended to unsigned int and became value 0xFFFFFFFFu. As a result the condition "0xFFFFFFFFu > 1" was executed. On the 64--bit system it’s just the other way round - the variable val_2 is extended to ptrdiff_t type. In this case the expression "-1 > 1" is checked.

If you need to return the previous behavior you should change the variable val_2 type.

ptrdiff_t val_1 = -1;
size_t val_2 = 1;
if (val_1 > val_2)
  printf ("val_1 is greater than val_2\n");
else
  printf ("val_1 is not greater than val_2\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.

“Before software should be reusable, it should be usable.” - Ralph Johnson