tags:

views:

91

answers:

2

This may seem odd, but in C (size_t)(void*)0 == 0 is not guaranteed by the language spec. Compilers are allowed to use any value they want for null (although they almost always use 0.)

In C#, you can assign null or (T*)0 to a pointer in unsafe code.

  1. Is there any difference?
  2. (long)(void*)0 == 0 (guaranteed or not? put another way: IntPtr.Zero.ToInt64() == 0)

MSDN has this to say about IntPtr.Zero:

"The value of this field is not equivalent to null." Well if you want to be compatible with C code, that makes a lot of sense - it'd be worthless for interop if it didn't convert to a C null pointer. But I want to know if IntPtr.Zero.ToInt64() == 0 which may be possible, even if internally IntPtr.Zero is some other value (the CLR may or may not convert null to 0 in the cast operation)

Not a duplicate of this question

A: 

In C, NULL is defined as architecture specific. While most architectures use 0, some don't. So, you cannot assume that NULL == 0 in C.

Not quite sure how it works in C# though I would recommend that you stick to the standard way of specifying NULL in C# instead of casting it to 0.

sybreon
What is the standard way of specifying null for pointers in C#? I've seen both used, and casting 0 seems to be more common. I like null because it's more readable, but the decision about which to use really depends the answer to whether or not they are equivalent.
Eloff
+4  A: 

You could avoid the issue:

char* foo = (char*)(void*)0;
char* bar = default(char*); // <======= the one to look at
Console.WriteLine(foo == bar); // writes True

So they are the same, but using default avoids having to embed any assumption or nasty casts into the code. Disassembling the above, the only difference is signed / unsigned - both start with a 4-byte constant (i4 / u4) then cast to native-int (i / u) (the // comments are mine) :

.maxstack 2
.locals init (
    [0] char* foo,
    [1] char* bar)
L_0000: ldc.i4.0 // (char*)(void*)0;
L_0001: conv.i 
L_0002: stloc.0 // foo=
L_0003: ldc.i4.0 // default(char*);
L_0004: conv.u 
L_0005: stloc.1 // bar=
L_0006: ldloc.0 
L_0007: ldloc.1 
L_0008: ceq  // foo == bar
L_000a: call void [mscorlib]System.Console::WriteLine(bool)
Marc Gravell
Ah yeh, I forgot that default works on pointers. That's a nice solution.
Eloff