Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I compare two IntPtr's with < and >?

Tags:

c#

pointers

I'm currently having a problem with unsafe pointers which appears to be a compiler bug.

Note: the problem is not with the fact that I am using pointers and unsafe code; the code works pretty well. The problem is related to a confirmed compiler bug that refuses to compile legal code under certain circumstances. If you're interested in that problem, visit my other question

Since my problem is with the declaration of pointer variables, I have decided to work around the problem, and use IntPtr instead, and cast to actual pointers when needed.

However, I noticed that I cannot do something like this:

IntPtr a = something;
IntPtr b = somethingElse;
if (a > b) // ERROR. Can't do this
{
}

The > and < operators don't seem to be defined for IntPtr. Notice that I can indeed compare two actual pointers.

IntPtr has a .ToInt64() method. However, this returns a signed value, which may return incorrect values when comparing with > and < when positive and negative values are involved.

To be honest, I don't really understand what use is there to a .ToInt64() method that returns a signed value, considering that pointer comparisons are performed unsigned, but that's not my question.

One could argue that IntPtrs are opaque handles, and it is therefore meaningless to compare with > and <. However, I would point out that IntPtr has addition and subtraction methods, which mean that there is actually a notion of order for IntPtr, and therefore > and < are indeed meaningful.

I guess I could cast the result of ToInt64() to a ulong and then compare, or cast the IntPtr to a pointer and then do the comparison, but it makes me think why aren't > and < defined for IntPtr.

Why can't I compare two IntPtrs directly?

like image 226
Panda Pajama Avatar asked Apr 07 '15 06:04

Panda Pajama


Video Answer


1 Answers

IntPtr has always been a little neglected. Until .NET 4.0 there weren't even the Add/operator+ and Subtract/operator-.

Now... if you want to compare two pointers, cast them to long if they are an IntPtr or to ulong if they are UIntPtr. Note that on Windows you'll need a UIntPtr only if you are using a 32 bits program with the /3GB option, because otherwise a 32 bits program can only use the lower 2gb of address space, while for 64bit programs, much less than 64 bits of address space is used (48 bits at this time).

Clearly if you are doing kernel programming in .NET this changes :-) (I'm jocking here, I hope :-) )

For the reason of why IntPtr are preferred to UIntPtr: https://msdn.microsoft.com/en-us/library/system.intptr%28v=vs.110%29.aspx

The IntPtr type is CLS-compliant, while the UIntPtr type is not. Only the IntPtr type is used in the common language runtime. The UIntPtr type is provided mostly to maintain architectural symmetry with the IntPtr type.

There are some languages that don't have the distinction between signed and unsigned types. .NET wanted to support them.

Done some tests by using

editbin /LARGEADDRESSAWARE myprogram.exe

(I was even able to crash my graphic adapter :-) )

static void Main(string[] args)
{
    Console.WriteLine("Is 64 bits", Environment.Is64BitProcess);

    const int memory = 128 * 1024;

    var lst = new List<IntPtr>(16384); // More than necessary

    while (true)
    {
        Console.Write("{0} ", lst.Count);

        IntPtr ptr = Marshal.AllocCoTaskMem(memory);
        //IntPtr ptr = Marshal.AllocHGlobal(memory);
        lst.Add(ptr);

        if ((long)ptr < 0)
        {
            Console.WriteLine("\nptr #{0} ({1}, {2}) is < 0", lst.Count, ptr, IntPtrToUintPtr(ptr));
        }
    }
}

I was able to allocate nearly 4 gb of memory with a 32 bits program (on a 64 bit OS) (so I had negative IntPtr)

And here it is a cast from IntPtr to UIntPtr

public static UIntPtr IntPtrToUintPtr(IntPtr ptr)
{
    if (IntPtr.Size == 4)
    {
        return unchecked((UIntPtr)(uint)(int)ptr);
    }

    return unchecked((UIntPtr)(ulong)(long)ptr);
}

Note that thanks to how sign extension works, you can't simply do (UIntPtr)(ulong)(long)ptr, because at 32 bits it will break.

But note that few programs really support > 2 gb on 32 bits... http://blogs.msdn.com/b/oldnewthing/archive/2004/08/12/213468.aspx

like image 179
xanatos Avatar answered Oct 18 '22 01:10

xanatos