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.
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
The void* type represents a pointer to an unknown type. Because the referent type is unknown, the indirection operator cannot be applied to a pointer of type void* , nor can any arithmetic be performed on such a pointer.
Now to understand what UnsafeMode is. Unsafe is a C# programming language keyword to denote a section of code that is not managed by the Common Language Runtime (CLR) of the . NET Framework, or unmanaged code. Unsafe is used in the declaration of a type or member or to specify a block code.
Unsafe code in C# isn't necessarily dangerous; it's just code whose safety cannot be verified. Unsafe code has the following properties: Methods, types, and code blocks can be defined as unsafe. In some cases, unsafe code may increase an application's performance by removing array bounds checks.
Unsafe code in general is a keyword that denotes a code section that is not handled by the Common Language Runtime(CLR). Pointers are not supported by default in C# but unsafe keyword allows the use of the pointer variables.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With