Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine if a variable is a pointer?

Tags:

c++

I was reading the Wolfenstein 3D code, and I encountered ISPOINTER macro:

#define ISPOINTER(x) ((((uintptr_t)(x)) & ~0xffff) != 0)

I know we have std::is_pointer, but how does this macro work? I tried and failed with strange behavior which I couldn't explained why it's happend:

#define ISPOINTER(x) ((((uintptr_t)(x)) & ~0xffff) != 0)
int main()
{
        int* ptr;
        int val;

        if (ISPOINTER(ptr)) {
                std::cout << "`ptr`: Is Pointer" << std::endl;
        }
        if (ISPOINTER(val)) {
                std::cout << "`val`: Is Pointer" << std::endl;
        }
}

I don't have any output, but if I add another pointer:

#define ISPOINTER(x) ((((uintptr_t)(x)) & ~0xffff) != 0)
int main()
{
        int* ptr;
        int val;
        int* ptr2;

        if (ISPOINTER(ptr)) {
                std::cout << "`ptr`: Is Pointer" << std::endl;
        }
        if (ISPOINTER(val)) {
                std::cout << "`val`: Is Pointer" << std::endl;
        }
        if (ISPOINTER(ptr2)) {
                std::cout << "`ptr2`: Is Pointer" << std::endl;
        }
}

The output will be:

`ptr`: Is Pointer

What does ISPOINTER doing? It's undefined behavior?

like image 438
Ghasem Ramezani Avatar asked Dec 23 '22 15:12

Ghasem Ramezani


2 Answers

Let's do this in steps:

((uintptr_t)(x)) is simply a cast from whatever x is into a uintptr_t (an unsigned integer type capable of storing pointer values)

~0xffff is a bit-wise complement of 0xffff (which is 16 bits of all 1s). The result of that is a number that is all 1s except the last 16 bits.

((uintptr_t)(x)) & ~0xffff is a bit-wise AND of the pointer value with the above number. This will effectively zero-out the 16 lowest bits of whatever the pointer value is.

The full expression now just checks if the result is zero or not. So the whole expression basically checks if any bits except the least-significant 16 are set and if so it considers it a pointer.

Since this came from Wolfenstein 3D, they probably made the assumption that all dynamically allocated memory lives in high memory addresses (higher than 2^16). So this is NOT a check if a type is a pointer or not using the type system like std::is_pointer does. This is an assumption based on the target architecture Wolfenstein 3D will likely run on.

Keep in mind that this is not a safe assumption, since "normal" values above 2^16 would also be considered pointers and the memory layout of your process can be very different depending on a lof of factors (e.g. ASLR)

like image 127
perivesta Avatar answered Jan 26 '23 00:01

perivesta


It might be similar to certain parameters in Windows, which can be an ordinal value or a pointer. An ordinal value will be <64K. On that operating system, any legal pointer will be >64K. (On older 16-bit versions of Windows, only 4K was reserved.)

This code may be doing the same thing. A "resource" may be a built-in or registered value referred to by a small number, or a pointer to an ad-hoc object. This macro is used to decide whether to use it as-is or look it up in the table instead.

like image 27
JDługosz Avatar answered Jan 25 '23 23:01

JDługosz