V221. Suspicious sequence of types castings: pointer -> memsize -> 32-bit integer.


This warning informs the programmer about the presence of a strange sequence of type conversions. A pointer is explicitly cast to a memsize-type and then again, explicitly or implicitly, to the 32-bit integer type. This sequence of conversions causes a loss of the most significant bits. It usually indicates a serious error in the code.

Take a look at the following example:

int *p = Foo();
unsigned a, b;
a = size_t(p);
b = unsigned(size_t(p));

In both cases, the pointer is cast to the 'unsigned' type, causing its most significant part to be truncated. If you then cast the variable 'a' or 'b' to a pointer again, the resulting pointer is likely to be incorrect.

The difference between the variables 'a' and 'b' is only in that the second case is harder to diagnose. In the first case, the compiler will warn you about the loss of the most significant bits, but keep silent in the second case as what is used there is an explicit type conversion.

To fix the error, we should store pointers in memsize-types only, for example in variables of the size_t type:

int *p = Foo();
size_t a, b;
a = size_t(p);
b = size_t(p);

There may be difficulties with understanding why the analyzer generates the warning on the following code pattern:

BOOL Foo(void *ptr)
{
  return (INT_PTR)ptr;
}

You see, the BOOL type is nothing but a 32-bit 'int' type. So we are dealing with a sequence of type conversions:

pointer -> INT_PTR -> int.

You may think there's actually no error here because what matters to us is only whether or not the pointer is equal to zero. But the error is real. It's just that programmers sometimes confuse the ways the types BOOL and bool behave.

Assume we have a 64-bit variable whose value equals 0x000012300000000. Casting it to bool and BOOL will have different results:

int64_t v = 0x000012300000000ll;

bool b = (bool)(v); // true

BOOL B = (BOOL)(v); // FALSE

In the case of 'BOOL', the most significant bits will be simply truncated and the non-zero value will turn to 0 (FALSE).

It's just the same with the pointer. When explicitly cast to BOOL, its most significant bits will get truncated and the non-zero pointer will turn to the integer 0 (FALSE). Although low, there is still some probability of this event. Therefore, code like that is incorrect.

To fix it, we can go two ways. The first one is to use the 'bool' type:

bool Foo(void *ptr)
{
  return (INT_PTR)ptr;
}

But of course it's better and easier to do it like this:

bool Foo(void *ptr)
{
  return ptr != nullptr;
}

The method shown above is not always applicable. For instance, there is no 'bool' type in the C language. So here's the second way to fix the error:

BOOL Foo(void *ptr)
{
  return ptr != NULL;
}

Keep in mind that the analyzer does not generate the warning when conversion is done over such data types as HANDLE, HWND, HCURSOR, and so on. Although these are in fact pointers (void *), their values always fit into the least significant 32 bits. It is done on purpose so that these handles could be passed between 32-bit and 64-bit processes. For details, see: How to correctly cast a pointer to int in a 64-bit application?


Bugs Found

Checked Projects
364
Collected Errors
13 504