You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/csharp/language-reference/unsafe-code.md
+51-59Lines changed: 51 additions & 59 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,31 +16,21 @@ helpviewer_keywords:
16
16
17
17
# Unsafe code
18
18
19
-
Unsafe code is a dialect of C# that unlocks powerful capabilities often needed for interoperability with native libraries, high-performance algorithms, and other low-level programming needs. However, these capabilities bypass C#'s usual safety checks, placing the responsibility for correctness squarely on the unsafe code author. Mistakes in unsafe code can lead to bugs like buffer overruns and use-after-free errors. To help isolate risks, unsafe code must appear within an [`unsafe`](keywords/unsafe.md) context, keeping it clearly separated from regular safe code and making it easier to audit.
19
+
C#'s unsafe code feature enables direct memory manipulation using pointers and other low-level constructs—capabilities essential for interop with native libraries and high-performance scenarios. However, unsafe code bypasses C#'s safety guarantees, so it's up to you, the author, to ensure correctness. Bugs like buffer overruns and use-after-free become possible. To help isolate risks, unsafe code must appear within an [`unsafe`](keywords/unsafe.md) contextand requires the [`AllowUnsafeBlocks`](compiler-options/language.md#allowunsafeblocks) compiler option.
20
20
21
-
In contrast, most C# code is safe code. Safe code always accesses, uses, and releases memory in ways that are proven correct by the C# compiler and .NET runtime.
22
-
23
-
Within an `unsafe` context, you can use pointers, manually allocate and free blocks of memory, and call methods through function pointers.
24
-
25
-
Unsafe code has the following characteristics:
26
-
27
-
- Methods, types, and code blocks can be marked as unsafe.
28
-
- Removing array bounds checks in unsafe code can, in some cases, improve performance.
29
-
- Unsafe code is required for calling native functions that use pointers.
30
-
- Using unsafe code introduces security and stability risks.
31
-
- Code containing unsafe blocks must be compiled with the [`AllowUnsafeBlocks`](compiler-options/language.md#allowunsafeblocks) compiler option.
21
+
Most C# code is safe code, where the compiler and .NET runtime enforce memory safety.
32
22
33
23
## Pointer types
34
24
35
-
In an unsafe context, a type can be a pointer type, in addition to a value type, or a reference type. A pointer type declaration takes the following forms (with the `*` being the key syntax difference):
25
+
In an unsafe context, C# supports **pointer types**. Pointers let you work with memory addresses directly, which is necessary for many interop scenarios and advanced optimizations.
36
26
37
-
```csharp
27
+
A pointer type declaration looks like:
28
+
29
+
```csharp
38
30
type*identifier;
39
31
```
40
32
41
-
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:
42
-
43
-
The following example demonstrates a complete example of using pointer types.
33
+
For example:
44
34
45
35
```csharp
46
36
intnumber=42;
@@ -50,77 +40,79 @@ bool same = false;
50
40
unsafe
51
41
{
52
42
int*pointer=&number; // Assigns the address of number
53
-
numberAgain=*pointer; // Retrieves the value at that address (42)
43
+
numberAgain=*pointer; // Retrieves the value at that address (42)
/* Example output (pointer address will vary each run):
61
-
Pointer (address): 6127673188; Pointer value: 42
55
+
Pointer address: 0x16F279F64
56
+
Pointer value: 42
62
57
NumberAgain: 42; Same: True
63
58
*/
64
59
```
65
60
66
-
The example also demonstrates how safe and unsafe code can interact. The first call to `Console.WriteLine` must be within the unsafe block because `pointer` can only be used in the `unsafe` context given its `int*` definition.
61
+
### Declaring and using pointers
67
62
68
-
### Defining pointer types
69
-
70
-
When you declare multiple pointers in the same declaration, you write the asterisk (`*`) together with the underlying type only. For example:
63
+
You can declare multiple pointers in one statement:
71
64
72
65
```csharp
73
-
int*p1, p2, p3; // Ok
74
-
int*p1, *p2, *p3; // Invalid
66
+
int*p1, p2, p3; // All are int pointers
75
67
```
76
68
77
-
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:
78
-
79
-
-`int* p`: `p` is a pointer to an integer.
80
-
-`int** p`: `p` is a pointer to a pointer to an integer.
81
-
-`int*[] p`: `p` is a single-dimensional array of pointers to integers.
82
-
-`char* p`: `p` is a pointer to a char.
83
-
-`void* p`: `p` is a pointer to an unknown type.
69
+
Common pointer types:
84
70
71
+
-`int* p`: pointer to `int`
72
+
-`int** p`: pointer to pointer to `int`
73
+
-`int*[] p`: array of `int` pointers
74
+
-`char* p`: pointer to `char`
75
+
-`void* p`: pointer to unknown type
85
76
86
-
There are several examples of pointers in the articles on the [`fixed` statement](statements/fixed.md). The following example uses the `unsafe` keyword 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 [**AllowUnsafeBlocks**](compiler-options/language.md#allowunsafeblocks) compiler option set.
Pointers don't inherit from [`object`](builtin-types/reference-types.md). You can't box or unbox pointers, and there's no conversion between pointers and `object`. However, you can cast between pointer types and between pointers and integral types (with an explicit cast).
89
80
90
-
### Using pointer types
81
+
The garbage collector doesn't track references from pointers. If you're pointing to a managed object, you must [pin](./statements/fixed.md) it for as long as the pointer is used.
91
82
92
-
Pointer types don't inherit from [object](builtin-types/reference-types.md) and no conversions exist between pointer types and `object`. Also, boxing and unboxing don't support pointers. However, you can convert between different pointer types and between pointer types and integral types.
93
-
94
-
The garbage collector doesn't keep track of whether an object is being pointed to by any pointer types. If the referrant is an object in the managed heap (including local variables captured by lambda expressions or anonymous delegates), the object must be [pinned](./statements/fixed.md) for as long as the pointer is used.
95
-
96
-
You can't 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.
97
-
98
-
A pointer can be `null`. Applying the indirection operator to a null pointer causes an implementation-defined behavior.
99
-
100
-
Passing pointers between methods can cause undefined behavior. Consider a method that returns a pointer to a local variable through an `in`, `out`, or `ref` parameter or as the function result. If the pointer was set in a fixed block, the variable to which it points might no longer be fixed.
101
-
102
-
The following table lists the operators and statements that can operate on pointers in an unsafe context:
83
+
The following operators and statements work with pointers in an unsafe context:
|`->`| Accesses a member of a struct through a pointer. |
108
-
|`[]`| Indexes a pointer. |
109
-
|`&`| Obtains the address of a variable. |
110
-
|`++` and `--`| Increments and decrements pointers. |
111
-
|`+` and `-`| Performs pointer arithmetic. |
112
-
|`==`, `!=`, `<`, `>`, `<=`, and `>=`| Compares pointers. |
113
-
|[`stackalloc`](operators/stackalloc.md)| Allocates memory on the stack. |
114
-
|[`fixed` statement](statements/fixed.md)| Temporarily fixes a variable so that its address can be found. |
87
+
|`*`| Dereference (pointer indirection). |
88
+
|`->`| Access struct member through a pointer. |
89
+
|`[]`| Index a pointer. |
90
+
|`&`| Take the address of a variable. |
91
+
|`++` and `--`| Increment/decrement pointer. |
92
+
|`+` and `-`| Pointer arithmetic. |
93
+
|`==`, `!=`, `<`, `>`, `<=`, and `>=`| Pointer comparison. |
94
+
|[`stackalloc`](operators/stackalloc.md)| Allocate memory on the stack. |
95
+
|[`fixed` statement](statements/fixed.md)| Pin a variable so its address can be taken. |
96
+
97
+
See [Pointer-related operators](operators/pointer-related-operators.md) for details.
115
98
116
-
For more information about pointer-related operators, see [Pointer-related operators](operators/pointer-related-operators.md).
99
+
### Pointer conversions and Interop
117
100
118
-
Any pointer type can be implicitly converted to a `void*` type. Any pointer type can be assigned the value `null`. Any pointer type can be explicitly converted to any other pointer type using a cast expression. You can also convert any integral type to a pointer type, or any pointer type to an integral type. These conversions require an explicit cast.
101
+
- Any pointer type can be implicitly converted to `void*`.
102
+
- Any pointer type can be set to `null`.
103
+
- You can explicitly cast between pointer types and between pointers and integral types (integral types must be at least the size of a pointer: `nint`, `nuint`, `IntPtr`, `UIntPtr`, or — on 64-bit — `long`/`ulong`).
104
+
- You can't dereference a `void*` directly, but you can cast it to another pointer type.
119
105
120
-
The following example converts an `int*` to a `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.
106
+
For example, converting an `int*` to a `byte*` lets you examine individual bytes:
- Dereferencing a null pointer is implementation-defined and may crash your program.
113
+
- Passing pointers to or from methods, especially if they refer to stack or pinned data, can cause undefined behavior if the referent is no longer valid.
114
+
- Never store a pointer to stack memory outside the current method.
115
+
124
116
## Fixed-size buffers
125
117
126
118
You can use the `fixed` keyword to create a buffer with a fixed-size array in a data structure. Fixed-size buffers are useful when you write methods that interoperate with data sources from other languages or platforms. The fixed-size buffer can take any attributes or modifiers that are allowed for regular struct members. The only restriction is that the array type must be `bool`, `byte`, `char`, `short`, `int`, `long`, `sbyte`, `ushort`, `uint`, `ulong`, `float`, or `double`.
0 commit comments