unsafe 嚮導文檔

/////////////////////////////////////////////////////////////////

C# programming guide

Unsafe code and pointers

/////////////////////////////////////////////////////////////////

Unsafe code and pointers (C# Programming Guide)

07/20/2015   2 minutes to read

To maintain type safety and security, C# does not support pointer arithmetic, by default. However, by using the unsafe keyword, you can define an unsafe context in which pointers can be used. For more information about pointers, see Pointer types.

 Note

In the common language runtime (CLR), unsafe code is referred to as unverifiable code. Unsafe code in C# is not necessarily dangerous; it is just code whose safety cannot be verified by the CLR. The CLR will therefore only execute unsafe code if it is in a fully trusted assembly. If you use unsafe code, it is your responsibility to ensure that your code does not introduce security risks or pointer errors.

Unsafe code overview

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 is required when you call native functions that require pointers.
  • Using unsafe code introduces security and stability risks.
  • The code that contains unsafe blocks must be compiled with the -unsafe compiler option.

Related sections

For more information, see:

C# language specification

For more information, see the Unsafe code topic of the C# language specification.

See also

////////////////////////////////////////////////////////////////////////////

Fixed Size Buffers (C# Programming Guide)

04/20/2018   2 minutes to read

In C#, you can use the fixed statement to create a buffer with a fixed size array in a data structure. Fixed size buffers are useful when you write methods that interop with data sources from other languages or platforms. The fixed array can take any attributes or modifiers that are allowed for regular struct members. The only restriction is that the array type must be boolbytecharshortintlongsbyteushortuintulongfloat, or double.

C#Copy

private fixed char name[30];

Remarks

In safe code, a C# struct that contains an array does not contain the array elements. Instead, the struct contains a reference to the elements. You can embed an array of fixed size in a struct when it is used in an unsafe code block.

Size of the following struct doesn't depend on the number of elements in the array, since pathName is a reference:

C#Copy

public struct PathArray

{

    public char[] pathName;

    private int reserved;

}

struct can contain an embedded array in unsafe code. In the following example, the fixedBuffer array has a fixed size. You use a fixed statement to establish a pointer to the first element. You access the elements of the array through this pointer. The fixedstatement pins the fixedBuffer instance field to a specific location in memory.

C#Copy

internal unsafe struct MyBuffer

{

    public fixed char fixedBuffer[128];

}

 

internal unsafe class MyClass

{

    public MyBuffer myBuffer = default;

}

 

private static void AccessEmbeddedArray()

{

    MyClass myC = new MyClass();

 

    unsafe

    {

        // Pin the buffer to a fixed location in memory.

        fixed (char* charPtr = myC.myBuffer.fixedBuffer)

        {

            *charPtr = 'A';

        }

        // Access safely through the index:

        char c = myC.myBuffer.fixedBuffer[0];

        Console.WriteLine(c);

        // modify through the index:

        myC.myBuffer.fixedBuffer[0] = 'B';

        Console.WriteLine(myC.myBuffer.fixedBuffer[0]);

    }

}

The size of the 128 element char array is 256 bytes. Fixed size char buffers always take two bytes per character, regardless of the encoding. This is true even when char buffers are marshaled to API methods or structs with CharSet = CharSet.Auto or CharSet = CharSet.Ansi. For more information, see CharSet.

The preceding example demonstrates accessing fixed fields without pinning, which is available starting with C# 7.3.

Another common fixed-size array is the bool array. The elements in a bool array are always one byte in size. bool arrays are not appropriate for creating bit arrays or buffers.

 Note

Except for memory created by using stackalloc, the C# compiler and the common language runtime (CLR) do not perform any security buffer overrun checks. As with all unsafe code, use caution.

Unsafe buffers differ from regular arrays in the following ways:

  • You can only use unsafe buffers in an unsafe context.
  • Unsafe buffers are always vectors, or one-dimensional arrays.
  • The declaration of the array should include a count, such as char id[8]. You cannot use char id[].
  • Unsafe buffers can only be instance fields of structs in an unsafe context.

See also

////////////////////////////////////////////////////////////////////////////////////

Pointer types (C# Programming Guide)

04/20/2018   3 minutes to read

In an unsafe context, a type may be a pointer type, a value type, or a reference type. A pointer type declaration takes one of the following forms:

C#Copy

type* identifier;

void* identifier; //allowed but not recommended

The type specified before the * in a pointer type is called the referent type. Only an unmanaged type can be a referent type.

Pointer types do not inherit from object and no conversions exist between pointer types and object. Also, boxing and unboxing do not support pointers. However, you can convert between different pointer types and between pointer types and integral types.

When you declare multiple pointers in the same declaration, the asterisk (*) is written together with the underlying type only; it is not used as a prefix to each pointer name. For example:

C#Copy

int* p1, p2, p3;   // Ok

int *p1, *p2, *p3;   // Invalid in C#

A pointer cannot point to a reference or to a struct that contains references, because an object reference can be garbage collected even if a pointer is pointing to it. The garbage collector does not keep track of whether an object is being pointed to by any pointer types.

The value of the pointer variable of type myType* is the address of a variable of type myType. The following are examples of pointer type declarations:

TABLE 1

Example

Description

int* p

p is a pointer to an integer.

int** p

p is a pointer to a pointer to an integer.

int*[] p

p is a single-dimensional array of pointers to integers.

char* p

p is a pointer to a char.

void* p

p is a pointer to an unknown type.

The pointer indirection operator * can be used to access the contents at the location pointed to by the pointer variable. For example, consider the following declaration:

C#Copy

int* myVariable;

The expression *myVariable denotes the int variable found at the address contained in myVariable.

There are several examples of pointers in the topics fixed Statement and Pointer Conversions. The following example uses the unsafekeyword and the fixed statement, and shows how to increment an interior pointer. You can paste this code into the Main function of a console application to run it. These examples must be compiled with the -unsafe compiler option set.

C#Copy

// Normal pointer to an object.

int[] a = new int[5] { 10, 20, 30, 40, 50 };

// Must be in unsafe code to use interior pointers.

unsafe

{

    // Must pin object on heap so that it doesn't move while using interior pointers.

    fixed (int* p = &a[0])

    {

        // p is pinned as well as object, so create another pointer to show incrementing it.

        int* p2 = p;

        Console.WriteLine(*p2);

        // Incrementing p2 bumps the pointer by four bytes due to its type ...

        p2 += 1;

        Console.WriteLine(*p2);

        p2 += 1;

        Console.WriteLine(*p2);

        Console.WriteLine("--------");

        Console.WriteLine(*p);

        // Dereferencing p and incrementing changes the value of a[0] ...

        *p += 1;

        Console.WriteLine(*p);

        *p += 1;

        Console.WriteLine(*p);

    }

}

 

Console.WriteLine("--------");

Console.WriteLine(a[0]);

 

/*

Output:

10

20

30

--------

10

11

12

--------

12

*/

You cannot apply the indirection operator to a pointer of type void*. However, you can use a cast to convert a void pointer to any other pointer type, and vice versa.

A pointer can be null. Applying the indirection operator to a null pointer causes an implementation-defined behavior.

Passing pointers between methods can cause undefined behavior. Consider a method that returns a pointer to a local variable through an inout, or ref parameter or as the function result. If the pointer was set in a fixed block, the variable to which it points may no longer be fixed.

The following table lists the operators and statements that can operate on pointers in an unsafe context:

TABLE 2

Operator/Statement

Use

*

Performs pointer indirection.

->

Accesses a member of a struct through a pointer.

[]

Indexes a pointer.

&

Obtains the address of a variable.

++ and --

Increments and decrements pointers.

+ and -

Performs pointer arithmetic.

==, !=, <, >, <=, and >=

Compares pointers.

stackalloc

Allocates memory on the stack.

fixed statement

Temporarily fixes a variable so that its address may be found.

For more information about pointer related operators, see Pointer related operators.

C# language specification

For more information, see the Pointer types section of the C# language specification.

See also

////////////////////////////////////////////////////////////////////////////////////

Pointer Conversions (C# Programming Guide)

07/20/2015    2 minutes to read

The following table shows the predefined implicit pointer conversions. Implicit conversions might occur in many situations, including method invoking and assignment statements.

Implicit pointer conversions

TABLE 1

From

To

Any pointer type

void*

null

Any pointer type

Explicit pointer conversion is used to perform conversions, for which there is no implicit conversion, by using a cast expression. The following table shows these conversions.

Explicit pointer conversions

TABLE 2

From

To

Any pointer type

Any other pointer type

sbyte, byte, short, ushort, int, uint, long, or ulong

Any pointer type

Any pointer type

sbyte, byte, short, ushort, int, uint, long, or ulong

Example

In the following example, a pointer to int is converted to a pointer to byte. Notice that the pointer points to the lowest addressed byte of the variable. When you successively increment the result, up to the size of int (4 bytes), you can display the remaining bytes of the variable.

C#Copy

// compile with: -unsafe

C#Copy

class ClassConvert

{

    static void Main()

    {

        int number = 1024;

 

        unsafe

        {

            // Convert to byte:

            byte* p = (byte*)&number;

 

            System.Console.Write("The 4 bytes of the integer:");

 

            // Display the 4 bytes of the int variable:

            for (int i = 0 ; i < sizeof(int) ; ++i)

            {

                System.Console.Write(" {0:X2}", *p);

                // Increment the pointer:

                p++;

            }

            System.Console.WriteLine();

            System.Console.WriteLine("The value of the integer: {0}", number);

 

            // Keep the console window open in debug mode.

            System.Console.WriteLine("Press any key to exit.");

            System.Console.ReadKey();

        }

    }

}

    /* Output:

        The 4 bytes of the integer: 00 04 00 00

        The value of the integer: 1024

    */

See also

///////////////////////////////////////////////////////////////////////////////////

How to use pointers to copy an array of bytes (C# Programming Guide)

04/20/2018     3 minutes to read

The following example uses pointers to copy bytes from one array to another.

This example uses the unsafe keyword, which enables you to use pointers in the Copy method. The fixed statement is used to declare pointers to the source and destination arrays. The fixed statement pins the location of the source and destination arrays in memory so that they will not be moved by garbage collection. The memory blocks for the arrays are unpinned when the fixed block is completed. Because the Copy method in this example uses the unsafe keyword, it must be compiled with the -unsafe compiler option.

This example accesses the elements of both arrays using indices rather than a second unmanaged pointer. The declaration of the pSource and pTarget pointers pins the arrays. This feature is available starting with C# 7.3.

Example

C#Copy

static unsafe void Copy(byte[] source, int sourceOffset, byte[] target,

    int targetOffset, int count)

{

    // If either array is not instantiated, you cannot complete the copy.

    if ((source == null) || (target == null))

    {

        throw new System.ArgumentException();

    }

 

    // If either offset, or the number of bytes to copy, is negative, you

    // cannot complete the copy.

    if ((sourceOffset < 0) || (targetOffset < 0) || (count < 0))

    {

        throw new System.ArgumentException();

    }

 

    // If the number of bytes from the offset to the end of the array is

    // less than the number of bytes you want to copy, you cannot complete

    // the copy.

    if ((source.Length - sourceOffset < count) || (target.Length - targetOffset < count))

    {

        throw new System.ArgumentException();

    }

 

    // The following fixed statement pins the location of the source and

    // target objects in memory so that they will not be moved by garbage

    // collection.

    fixed (byte* pSource = source, pTarget = target)

    {

        // Copy the specified number of bytes from source to target.

        for (int i = 0; i < count; i++)

        {

            pTarget[targetOffset + i] = pSource[sourceOffset + i];

        }

    }

}

 

static void UnsafeCopyArrays()

{

    // Create two arrays of the same length.

    int length = 100;

    byte[] byteArray1 = new byte[length];

    byte[] byteArray2 = new byte[length];

 

    // Fill byteArray1 with 0 - 99.

    for (int i = 0; i < length; ++i)

    {

        byteArray1[i] = (byte)i;

    }

 

    // Display the first 10 elements in byteArray1.

    System.Console.WriteLine("The first 10 elements of the original are:");

    for (int i = 0; i < 10; ++i)

    {

        System.Console.Write(byteArray1[i] + " ");

    }

    System.Console.WriteLine("\n");

 

    // Copy the contents of byteArray1 to byteArray2.

    Copy(byteArray1, 0, byteArray2, 0, length);

 

    // Display the first 10 elements in the copy, byteArray2.

    System.Console.WriteLine("The first 10 elements of the copy are:");

    for (int i = 0; i < 10; ++i)

    {

        System.Console.Write(byteArray2[i] + " ");

    }

    System.Console.WriteLine("\n");

 

    // Copy the contents of the last 10 elements of byteArray1 to the

    // beginning of byteArray2.

    // The offset specifies where the copying begins in the source array.

    int offset = length - 10;

    Copy(byteArray1, offset, byteArray2, 0, length - offset);

 

    // Display the first 10 elements in the copy, byteArray2.

    System.Console.WriteLine("The first 10 elements of the copy are:");

    for (int i = 0; i < 10; ++i)

    {

        System.Console.Write(byteArray2[i] + " ");

    }

    System.Console.WriteLine("\n");

    /* Output:

        The first 10 elements of the original are:

        0 1 2 3 4 5 6 7 8 9

 

        The first 10 elements of the copy are:

        0 1 2 3 4 5 6 7 8 9

 

        The first 10 elements of the copy are:

        90 91 92 93 94 95 96 97 98 99

    */

}

See also

///////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////

unsafe (C# Reference)

07/20/2015              2 minutes to read

The unsafe keyword denotes an unsafe context, which is required for any operation involving pointers. For more information, see Unsafe Code and Pointers.

You can use the unsafe modifier in the declaration of a type or a member. The entire textual extent of the type or member is therefore considered an unsafe context. For example, the following is a method declared with the unsafe modifier:

C#Copy

unsafe static void FastCopy(byte[] src, byte[] dst, int count)

{

    // Unsafe context: can use pointers here.

}

The scope of the unsafe context extends from the parameter list to the end of the method, so pointers can also be used in the parameter list:

C#Copy

unsafe static void FastCopy ( byte* ps, byte* pd, int count ) {...}

You can also use an unsafe block to enable the use of an unsafe code inside this block. For example:

C#Copy

unsafe

{

    // Unsafe context: can use pointers here.

}

To compile unsafe code, you must specify the -unsafe compiler option. Unsafe code is not verifiable by the common language runtime.

Example

C#Copy

// compile with: -unsafe

class UnsafeTest

{

    // Unsafe method: takes pointer to int.

    unsafe static void SquarePtrParam(int* p)

    {

        *p *= *p;

    }

 

    unsafe static void Main()

    {

        int i = 5;

        // Unsafe method: uses address-of operator (&).

        SquarePtrParam(&i);

        Console.WriteLine(i);

    }

}

// Output: 25

C# language specification

For more information, see Unsafe code in the C# Language Specification. The language specification is the definitive source for C# syntax and usage.

See also

/////////////////////////////////////////////////////////////////////////////////////////////

fixed Statement (C# Reference)

05/10/2018  3 minutes to read

The fixed statement prevents the garbage collector from relocating a movable variable. The fixed statement is only permitted in an unsafe context. You can also use the fixed keyword to create fixed size buffers.

The fixed statement sets a pointer to a managed variable and "pins" that variable during the execution of the statement. Pointers to movable managed variables are useful only in a fixed context. Without a fixed context, garbage collection could relocate the variables unpredictably. The C# compiler only lets you assign a pointer to a managed variable in a fixed statement.

C#Copy

class Point

{

    public int x;

    public int y;

}

 

unsafe private static void ModifyFixedStorage()

{

    // Variable pt is a managed variable, subject to garbage collection.

    Point pt = new Point();

 

    // Using fixed allows the address of pt members to be taken,

    // and "pins" pt so that it is not relocated.

 

    fixed (int* p = &pt.x)

    {

        *p = 1;

    }

}

You can initialize a pointer by using an array, a string, a fixed-size buffer, or the address of a variable. The following example illustrates the use of variable addresses, arrays, and strings:

C#Copy

Point point = new Point();

double[] arr = { 0, 1.5, 2.3, 3.4, 4.0, 5.9 };

string str = "Hello World";

 

// The following two assignments are equivalent. Each assigns the address

// of the first element in array arr to pointer p.

 

// You can initialize a pointer by using an array.

fixed (double* p = arr) { /*...*/ }

 

// You can initialize a pointer by using the address of a variable.

fixed (double* p = &arr[0]) { /*...*/ }

 

// The following assignment initializes p by using a string.

fixed (char* p = str) { /*...*/ }

 

// The following assignment is not valid, because str[0] is a char,

// which is a value, not a variable.

//fixed (char* p = &str[0]) { /*...*/ }

Starting with C# 7.3, the fixed statement operates on additional types beyond arrays, strings, fixed size buffers, or unmanaged variables. Any type that implements a method named GetPinnableReference can be pinned. The GetPinnableReference must return a ref variable of an unmanaged type. The .NET types System.Span<T> and System.ReadOnlySpan<T> introduced in .NET Core 2.0 make use of this pattern and can be pinned. This is shown in the following example:

C#Copy

unsafe private static void FixedSpanExample()

{

    int[] PascalsTriangle = {

                  1,

                1,  1,

              1,  2,  1,

            1,  3,  3,  1,

          1,  4,  6,  4,  1,

        1,  5,  10, 10, 5,  1

    };

 

    Span<int> RowFive = new Span<int>(PascalsTriangle, 10, 5);

 

    fixed (int* ptrToRow = RowFive)

    {

        // Sum the numbers 1,4,6,4,1

        var sum = 0;

        for (int i = 0; i < RowFive.Length; i++)

        {

            sum += *(ptrToRow + i);

        }

        Console.WriteLine(sum);

    }

}

If you are creating types that should participate in this pattern, see Span<T>.GetPinnableReference() for an example of implementing the pattern.

Multiple pointers can be initialized in one statement if they are all the same type:

C#Copy

fixed (byte* ps = srcarray, pd = dstarray) {...}

To initialize pointers of different types, simply nest fixed statements, as shown in the following example.

C#Copy

fixed (int* p1 = &point.x)

{

    fixed (double* p2 = &arr[5])

    {

        // Do something with p1 and p2.

    }

}

After the code in the statement is executed, any pinned variables are unpinned and subject to garbage collection. Therefore, do not point to those variables outside the fixed statement. The variables declared in the fixed statement are scoped to that statement, making this easier:

C#Copy

fixed (byte* ps = srcarray, pd = dstarray)

{

   ...

}

// ps and pd are no longer in scope here.

Pointers initialized in fixed statements are readonly variables. If you want to modify the pointer value, you must declare a second pointer variable, and modify that. The variable declared in the fixed statement cannot be modified:

C#Copy

fixed (byte* ps = srcarray, pd = dstarray)

{

    byte* pSourceCopy = ps;

    pSourceCopy++; // point to the next element.

    ps++; // invalid: cannot modify ps, as it is declared in the fixed statement.

}

You can allocate memory on the stack, where it is not subject to garbage collection and therefore does not need to be pinned. To do that, use a stackalloc expression.

C# language specification

For more information, see The fixed statement section of the C# language specification.

See also

//////////////////////////////////////////////////////////////////////////////////////////

stackalloc expression (C# reference)

03/13/2020     3 minutes to read

stackalloc expression allocates a block of memory on the stack. A stack allocated memory block created during the method execution is automatically discarded when that method returns. You cannot explicitly free the memory allocated with stackalloc. A stack allocated memory block is not subject to garbage collection and doesn't have to be pinned with a fixed statement.

You can assign the result of a stackalloc expression to a variable of one of the following types:

C#Copy

int length = 3;

Span<int> numbers = stackalloc int[length];

for (var i = 0; i < length; i++)

{

    numbers[i] = i;

}

You don't have to use an unsafe context when you assign a stack allocated memory block to a Span<T> or ReadOnlySpan<T>variable.

When you work with those types, you can use a stackalloc expression in conditional or assignment expressions, as the following example shows:

C#Copy

int length = 1000;

Span<byte> buffer = length <= 1024 ? stackalloc byte[length] : new byte[length];

Beginning with C# 8.0, you can use a stackalloc expression inside other expressions whenever a Span<T> or ReadOnlySpan<T> variable is allowed, as the following example shows:

C#Copy

Span<int> numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };

var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6 ,8 });

Console.WriteLine(ind);  // output: 1

 Note

We recommend using Span<T> or ReadOnlySpan<T> types to work with stack allocated memory whenever possible.

C#Copy

unsafe

{

    int length = 3;

    int* numbers = stackalloc int[length];

    for (var i = 0; i < length; i++)

    {

        numbers[i] = i;

    }

}

As the preceding example shows, you must use an unsafe context when you work with pointer types.

In the case of pointer types, you can use a stackalloc expression only in a local variable declaration to initialize the variable.

The amount of memory available on the stack is limited. If you allocate too much memory on the stack, a StackOverflowException is thrown. To avoid that, follow the rules below:

  • Limit the amount of memory you allocate with stackalloc:

C#Copy

const int MaxStackLimit = 1024;

Span<byte> buffer = inputLength <= MaxStackLimit ? stackalloc byte[inputLength] : new byte[inputLength];

Because the amount of memory available on the stack depends on the environment in which the code is executed, be conservative when you define the actual limit value.

  • Avoid using stackalloc inside loops. Allocate the memory block outside a loop and reuse it inside the loop.

The content of the newly allocated memory is undefined. You should initialize it before the use. For example, you can use the Span<T>.Clear method that sets all the items to the default value of type T.

Beginning with C# 7.3, you can use array initializer syntax to define the content of the newly allocated memory. The following example demonstrates various ways to do that:

C#Copy

Span<int> first = stackalloc int[3] { 1, 2, 3 };

Span<int> second = stackalloc int[] { 1, 2, 3 };

ReadOnlySpan<int> third = stackalloc[] { 1, 2, 3 };

In expression stackalloc T[E]T must be an unmanaged type and E must evaluate to a non-negative int value.

Security

The use of stackalloc automatically enables buffer overrun detection features in the common language runtime (CLR). If a buffer overrun is detected, the process is terminated as quickly as possible to minimize the chance that malicious code is executed.

C# language specification

For more information, see the Stack allocation section of the C# language specification and the Permit stackalloc in nested contextsfeature proposal note.

See also

////////////////////////////////////////////////////////////////////////////

Pointer related operators (C# reference)

05/20/2019 7 minutes to read

You can use the following operators to work with pointers:

For information about pointer types, see Pointer types.

 Note

Any operation with pointers requires an unsafe context. The code that contains unsafe blocks must be compiled with the -unsafe compiler option.

Address-of operator &

The unary & operator returns the address of its operand:

C#Copy

unsafe

{  int number = 27;
int* pointerToNumber = &number; 
Console.WriteLine($"Value of the variable: {number}");
Console.WriteLine($"Address of the variable: {(long)pointerToNumber:X}");
}// Output is similar to:
// Value of the variable: 27
// Address of the variable: 6C1457DBD4

The operand of the & operator must be a fixed variable. Fixed variables are variables that reside in storage locations that are unaffected by operation of the garbage collector. In the preceding example, the local variable number is a fixed variable, because it resides on the stack. Variables that reside in storage locations that can be affected by the garbage collector (for example, relocated) are called movable variables. Object fields and array elements are examples of movable variables. You can get the address of a movable variable if you "fix", or "pin", it with a fixed statement. The obtained address is valid only inside the block of a fixedstatement. The following example shows how to use a fixed statement and the & operator:

C#Copy

unsafe{

byte[] bytes = { 1, 2, 3 };

fixed (byte* pointerToFirst = &bytes[0])

{

// The address stored in pointerToFirst

// is valid only inside this fixed statement block.

}

}

You can't get the address of a constant or a value.

For more information about fixed and movable variables, see the Fixed and moveable variables section of the C# language specification.

The binary & operator computes the logical AND of its Boolean operands or the bitwise logical AND of its integral operands.

Pointer indirection operator *

The unary pointer indirection operator * obtains the variable to which its operand points. It's also known as the dereference operator. The operand of the * operator must be of a pointer type.

C#Copy

unsafe

{char letter = 'A';

char* pointerToLetter = &letter;

Console.WriteLine($"Value of the `letter` variable: {letter}");

Console.WriteLine($"Address of the `letter` variable: {(long)pointerToLetter:X}");

*pointerToLetter ='Z';

Console.WriteLine($"Value of the `letter` variable after update: {letter}");

}// Output is similar to:

// Value of the `letter` variable: A

// Address of the `letter` variable: DCB977DDF4

// Value of the `letter` variable after update: Z

You cannot apply the * operator to an expression of type void*.

The binary * operator computes the product of its numeric operands.

Pointer member access operator ->

The -> operator combines pointer indirection and member access. That is, if x is a pointer of type T* and y is an accessible member of type T, an expression of the form

C#Copy

x->y

is equivalent to

C#Copy

(*x).y

The following example demonstrates the usage of the -> operator:

C#Copy

public struct Coords

{

public int X;

public int Y;

public override string ToString() => $"({X}, {Y})";

}

public class PointerMemberAccessExample

{

public static unsafe void Main()

{

Coords coords;

Coords* p = &coords;

p->X = 3;

p->Y = 4;

Console.WriteLine(p->ToString());  // output: (3, 4)

}

}

You cannot apply the -> operator to an expression of type void*.

Pointer element access operator []

For an expression p of a pointer type, a pointer element access of the form p[n] is evaluated as *(p + n), where n must be of a type implicitly convertible to intuintlong, or ulong. For information about the behavior of the + operator with pointers, see the Addition or subtraction of an integral value to or from a pointer section.

The following example demonstrates how to access array elements with a pointer and the [] operator:

C#Copy

unsafe

{

char* pointerToChars = stackalloc char[123];

for (int i = 65; i < 123; i++)

{

pointerToChars[i] = (char)i;

}

Console.Write("Uppercase letters: ");

for (int i = 65; i < 91; i++)

{

Console.Write(pointerToChars[i]);

}

}// Output:

// Uppercase letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ

In the preceding example, a stackalloc expression allocates a block of memory on the stack.

 Note

The pointer element access operator doesn't check for out-of-bounds errors.

You cannot use [] for pointer element access with an expression of type void*.

You also can use the [] operator for array element or indexer access.

Pointer arithmetic operators

You can perform the following arithmetic operations with pointers:

  • Add or subtract an integral value to or from a pointer
  • Subtract two pointers
  • Increment or decrement a pointer

You cannot perform those operations with pointers of type void*.

For information about supported arithmetic operations with numeric types, see Arithmetic operators.

Addition or subtraction of an integral value to or from a pointer

For a pointer p of type T* and an expression n of a type implicitly convertible to intuintlong, or ulong, addition and subtraction are defined as follows:

  • Both p + n and n + p expressions produce a pointer of type T* that results from adding n * sizeof(T) to the address given by p.
  • The p - n expression produces a pointer of type T* that results from subtracting n * sizeof(T) from the address given by p.

The sizeof operator obtains the size of a type in bytes.

The following example demonstrates the usage of the + operator with a pointer:

C#Copy

unsafe

{

const int Count = 3;

int[] numbers = new int[Count] { 10, 20, 30 };

fixed (int* pointerToFirst = &numbers[0])

{

int* pointerToLast = pointerToFirst + (Count - 1);

Console.WriteLine($"Value {*pointerToFirst} at address {(long)pointerToFirst}");

Console.WriteLine($"Value {*pointerToLast} at address {(long)pointerToLast}");

}

}

// Output is similar to:
// Value 10 at address 1818345918136
// Value 30 at address 1818345918144

Pointer subtraction

For two pointers p1 and p2 of type T*, the expression p1 - p2 produces the difference between the addresses given by p1 and p2divided by sizeof(T). The type of the result is long. That is, p1 - p2 is computed as ((long)(p1) - (long)(p2)) / sizeof(T).

The following example demonstrates the pointer subtraction:

C#Copy

unsafe

{

int* numbers = stackalloc int[] { 0, 1, 2, 3, 4, 5 };

int* p1 = &numbers[1];

int* p2 = &numbers[5];

Console.WriteLine(p2 - p1);  // output: 4

}

Pointer increment and decrement

The ++ increment operator adds 1 to its pointer operand. The -- decrement operator subtracts 1 from its pointer operand.

Both operators are supported in two forms: postfix (p++ and p--) and prefix (++p and --p). The result of p++ and p-- is the value of p before the operation. The result of ++p and --p is the value of p after the operation.

The following example demonstrates the behavior of both postfix and prefix increment operators:

C#Copy

unsafe

{

int* numbers = stackalloc int[] { 0, 1, 2 };

int* p1 = &numbers[0];

int* p2 = p1;

Console.WriteLine($"Before operation: p1 - {(long)p1}, p2 - {(long)p2}");

Console.WriteLine($"Postfix increment of p1: {(long)(p1++)}");

Console.WriteLine($"Prefix increment of p2: {(long)(++p2)}");

Console.WriteLine($"After operation: p1 - {(long)p1}, p2 - {(long)p2}");

}

// Output is similar to
// Before operation: p1 - 816489946512, p2 - 816489946512
// Postfix increment of p1: 816489946512
// Prefix increment of p2: 816489946516
// After operation: p1 - 816489946516, p2 - 816489946516

Pointer comparison operators

You can use the ==!=<><=, and >= operators to compare operands of any pointer type, including void*. Those operators compare the addresses given by the two operands as if they were unsigned integers.

For information about the behavior of those operators for operands of other types, see the Equality operators and Comparison operators articles.

Operator precedence

The following list orders pointer related operators starting from the highest precedence to the lowest:

  • Postfix increment x++ and decrement x-- operators and the -> and [] operators
  • Prefix increment ++x and decrement --x operators and the & and * operators
  • Additive + and - operators
  • Comparison <><=, and >= operators
  • Equality == and != operators

Use parentheses, (), to change the order of evaluation imposed by operator precedence.

For the complete list of C# operators ordered by precedence level, see the Operator precedence section of the C# operators article.

Operator overloadability

A user-defined type cannot overload the pointer related operators &*->, and [].

C# language specification

For more information, see the following sections of the C# language specification:

See also

///////////////////////////////////////////////////////////////////////

 

///////////////////////////////////////////////////////////////////////

-unsafe (C# Compiler Options)

04/25/2018      2 minutes to read

The -unsafe compiler option allows code that uses the unsafe keyword to compile.

Syntax

consoleCopy

-unsafe 

Remarks

For more information about unsafe code, see Unsafe Code and Pointers.

To set this compiler option in the Visual Studio development environment

  1. Open the project's Properties page.
  1. Click the Build property page.
  1. Select the Allow Unsafe Code check box.

To add this option in a csproj file

Open the .csproj file for a project, and add the following elements:

XMLCopy

  <PropertyGroup>    <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>

For information about how to set this compiler option programmatically, see AllowUnsafeBlocks.

Example

Compile in.cs for unsafe mode:

consoleCopy

csc -unsafe in.cs 

See also

/////////////////////////////////////////////////////////////////////////

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章