Skip to content

Commit 7a036ec

Browse files
authored
Intro update
1 parent 41110bd commit 7a036ec

File tree

1 file changed

+51
-59
lines changed

1 file changed

+51
-59
lines changed

docs/csharp/language-reference/unsafe-code.md

Lines changed: 51 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,21 @@ helpviewer_keywords:
1616

1717
# Unsafe code
1818

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) context and requires the [`AllowUnsafeBlocks`](compiler-options/language.md#allowunsafeblocks) compiler option.
2020

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.
3222

3323
## Pointer types
3424

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.
3626

37-
``` csharp
27+
A pointer type declaration looks like:
28+
29+
```csharp
3830
type* identifier;
3931
```
4032

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:
4434

4535
```csharp
4636
int number = 42;
@@ -50,77 +40,79 @@ bool same = false;
5040
unsafe
5141
{
5242
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)
5444
same = number == numberAgain; // Will resolve to true
55-
Console.WriteLine($"Pointer (address): {(ulong)pointer}; Pointer value: {*pointer}");
45+
PrintAddress(pointer); // Prints the address of the pointer
46+
Console.WriteLine($"Pointer value: {*pointer}");
5647
}
5748

5849
Console.WriteLine($"NumberAgain: {numberAgain}; Same: {same}");
5950

51+
unsafe void PrintAddress(int* pointer) =>
52+
Console.WriteLine($"Pointer address: 0x{(nuint)p:X}");
53+
6054
/* Example output (pointer address will vary each run):
61-
Pointer (address): 6127673188; Pointer value: 42
55+
Pointer address: 0x16F279F64
56+
Pointer value: 42
6257
NumberAgain: 42; Same: True
6358
*/
6459
```
6560

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
6762

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:
7164

7265
```csharp
73-
int* p1, p2, p3; // Ok
74-
int *p1, *p2, *p3; // Invalid
66+
int* p1, p2, p3; // All are int pointers
7567
```
7668

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:
8470

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
8576

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.
77+
### Pointer operations
8778

88-
:::code language="csharp" source="snippets/unsafe-code/FixedKeywordExamples.cs" ID="5":::
79+
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).
8980

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.
9182

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:
10384

10485
| Operator/Statement | Use |
10586
|------------------------------------------|----------------------------------------------------------------|
106-
| `*` | Performs pointer indirection. |
107-
| `->` | 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.
11598

116-
For more information about pointer-related operators, see [Pointer-related operators](operators/pointer-related-operators.md).
99+
### Pointer conversions and Interop
117100

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.
119105

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:
121107

122108
:::code language="csharp" source="snippets/unsafe-code/Conversions.cs" ID="Conversion":::
123109

110+
### Pointer safety reminders
111+
112+
- 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+
124116
## Fixed-size buffers
125117

126118
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

Comments
 (0)