From 41110bd3873cd7a8961443241ba7fdace537bd0a Mon Sep 17 00:00:00 2001
From: Rich Lander <2608468+richlander@users.noreply.github.com>
Date: Mon, 7 Jul 2025 16:57:06 -0700
Subject: [PATCH 1/5] Update intro sections
---
docs/csharp/language-reference/unsafe-code.md | 76 ++++++++++++-------
1 file changed, 50 insertions(+), 26 deletions(-)
diff --git a/docs/csharp/language-reference/unsafe-code.md b/docs/csharp/language-reference/unsafe-code.md
index f3e4f95b032de..e8be8eeffa7fb 100644
--- a/docs/csharp/language-reference/unsafe-code.md
+++ b/docs/csharp/language-reference/unsafe-code.md
@@ -1,7 +1,7 @@
---
-title: "Unsafe code, pointers to data, and function pointers"
+title: "Unsafe code"
description: Learn about unsafe code, pointers, and function pointers. C# requires you to declare an unsafe context to use these features to directly manipulate memory or function pointers (unmanaged delegates).
-ms.date: 02/06/2025
+ms.date: 07/07/2025
f1_keywords:
- "functionPointer_CSharpKeyword"
helpviewer_keywords:
@@ -13,41 +13,66 @@ helpviewer_keywords:
- "C# language, pointers"
- "pointers [C#], about pointers"
---
-# Unsafe code, pointer types, and function pointers
-Most of the C# code you write is "verifiably safe code." *Verifiably safe code* means .NET tools can verify that the code is safe. In general, safe code doesn't directly access memory using pointers. It also doesn't allocate raw memory. It creates managed objects instead.
+# Unsafe code
-C# supports an [`unsafe`](keywords/unsafe.md) context, in which you can write *unverifiable* code. In an `unsafe` context, code can use pointers, allocate and free blocks of memory, and call methods using function pointers. Unsafe code in C# isn't necessarily dangerous; it's just code whose safety can't be verified.
+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.
-Unsafe code has the following properties:
+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.
-- Methods, types, and code blocks can be defined as unsafe.
-- In some cases, unsafe code can increase an application's performance by removing array bounds checks.
-- Unsafe code is required when you call native functions that require pointers.
+Within an `unsafe` context, you can use pointers, manually allocate and free blocks of memory, and call methods through function pointers.
+
+Unsafe code has the following characteristics:
+
+- Methods, types, and code blocks can be marked as unsafe.
+- Removing array bounds checks in unsafe code can, in some cases, improve performance.
+- Unsafe code is required for calling native functions that use pointers.
- Using unsafe code introduces security and stability risks.
-- The code that contains unsafe blocks must be compiled with the [**AllowUnsafeBlocks**](compiler-options/language.md#allowunsafeblocks) compiler option.
+- Code containing unsafe blocks must be compiled with the [`AllowUnsafeBlocks`](compiler-options/language.md#allowunsafeblocks) compiler option.
## Pointer types
-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 one of the following forms:
+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):
``` csharp
type* identifier;
-void* identifier; //allowed but not recommended
```
-The type specified before the `*` in a pointer type is called the **referent type**.
-
-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.
+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:
-When you declare multiple pointers in the same declaration, you write the asterisk (`*`) together with the underlying type only. It isn't used as a prefix to each pointer name. For example:
+The following example demonstrates a complete example of using pointer types.
```csharp
-int* p1, p2, p3; // Ok
-int *p1, *p2, *p3; // Invalid in C#
+int number = 42;
+int numberAgain = 0;
+bool same = false;
+
+unsafe
+{
+ int* pointer = &number; // Assigns the address of number
+ numberAgain = *pointer; // Retrieves the value at that address (42)
+ same = number == numberAgain; // Will resolve to true
+ Console.WriteLine($"Pointer (address): {(ulong)pointer}; Pointer value: {*pointer}");
+}
+
+Console.WriteLine($"NumberAgain: {numberAgain}; Same: {same}");
+
+/* Example output (pointer address will vary each run):
+Pointer (address): 6127673188; Pointer value: 42
+NumberAgain: 42; Same: True
+*/
```
-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.
+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.
+
+### Defining pointer types
+
+When you declare multiple pointers in the same declaration, you write the asterisk (`*`) together with the underlying type only. For example:
+
+```csharp
+int* p1, p2, p3; // Ok
+int *p1, *p2, *p3; // Invalid
+```
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:
@@ -57,18 +82,17 @@ The value of the pointer variable of type `MyType*` is the address of a variable
- `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:
-
-```csharp
-int* myVariable;
-```
-
-The expression `*myVariable` denotes the `int` variable found at the address contained in `myVariable`.
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.
:::code language="csharp" source="snippets/unsafe-code/FixedKeywordExamples.cs" ID="5":::
+### Using pointer types
+
+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.
+
+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.
+
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.
A pointer can be `null`. Applying the indirection operator to a null pointer causes an implementation-defined behavior.
From 7a036ec2f7c73d6d539b4467e8771a9adee4759c Mon Sep 17 00:00:00 2001
From: Rich Lander <2608468+richlander@users.noreply.github.com>
Date: Mon, 7 Jul 2025 17:34:26 -0700
Subject: [PATCH 2/5] Intro update
---
docs/csharp/language-reference/unsafe-code.md | 110 ++++++++----------
1 file changed, 51 insertions(+), 59 deletions(-)
diff --git a/docs/csharp/language-reference/unsafe-code.md b/docs/csharp/language-reference/unsafe-code.md
index e8be8eeffa7fb..6965df26ed2b1 100644
--- a/docs/csharp/language-reference/unsafe-code.md
+++ b/docs/csharp/language-reference/unsafe-code.md
@@ -16,31 +16,21 @@ helpviewer_keywords:
# Unsafe code
-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.
+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.
-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.
-
-Within an `unsafe` context, you can use pointers, manually allocate and free blocks of memory, and call methods through function pointers.
-
-Unsafe code has the following characteristics:
-
-- Methods, types, and code blocks can be marked as unsafe.
-- Removing array bounds checks in unsafe code can, in some cases, improve performance.
-- Unsafe code is required for calling native functions that use pointers.
-- Using unsafe code introduces security and stability risks.
-- Code containing unsafe blocks must be compiled with the [`AllowUnsafeBlocks`](compiler-options/language.md#allowunsafeblocks) compiler option.
+Most C# code is safe code, where the compiler and .NET runtime enforce memory safety.
## Pointer types
-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):
+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.
-``` csharp
+A pointer type declaration looks like:
+
+```csharp
type* identifier;
```
-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:
-
-The following example demonstrates a complete example of using pointer types.
+For example:
```csharp
int number = 42;
@@ -50,77 +40,79 @@ bool same = false;
unsafe
{
int* pointer = &number; // Assigns the address of number
- numberAgain = *pointer; // Retrieves the value at that address (42)
+ numberAgain = *pointer; // Retrieves the value at that address (42)
same = number == numberAgain; // Will resolve to true
- Console.WriteLine($"Pointer (address): {(ulong)pointer}; Pointer value: {*pointer}");
+ PrintAddress(pointer); // Prints the address of the pointer
+ Console.WriteLine($"Pointer value: {*pointer}");
}
Console.WriteLine($"NumberAgain: {numberAgain}; Same: {same}");
+unsafe void PrintAddress(int* pointer) =>
+ Console.WriteLine($"Pointer address: 0x{(nuint)p:X}");
+
/* Example output (pointer address will vary each run):
-Pointer (address): 6127673188; Pointer value: 42
+Pointer address: 0x16F279F64
+Pointer value: 42
NumberAgain: 42; Same: True
*/
```
-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.
+### Declaring and using pointers
-### Defining pointer types
-
-When you declare multiple pointers in the same declaration, you write the asterisk (`*`) together with the underlying type only. For example:
+You can declare multiple pointers in one statement:
```csharp
-int* p1, p2, p3; // Ok
-int *p1, *p2, *p3; // Invalid
+int* p1, p2, p3; // All are int pointers
```
-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:
-
-- `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.
+Common pointer types:
+- `int* p`: pointer to `int`
+- `int** p`: pointer to pointer to `int`
+- `int*[] p`: array of `int` pointers
+- `char* p`: pointer to `char`
+- `void* p`: pointer to unknown type
-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.
+### Pointer operations
-:::code language="csharp" source="snippets/unsafe-code/FixedKeywordExamples.cs" ID="5":::
+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).
-### Using pointer types
+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.
-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.
-
-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.
-
-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.
-
-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 `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.
-
-The following table lists the operators and statements that can operate on pointers in an unsafe context:
+The following operators and statements work with pointers in an unsafe context:
| 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`](operators/stackalloc.md) | Allocates memory on the stack. |
-| [`fixed` statement](statements/fixed.md) | Temporarily fixes a variable so that its address can be found. |
+| `*` | Dereference (pointer indirection). |
+| `->` | Access struct member through a pointer. |
+| `[]` | Index a pointer. |
+| `&` | Take the address of a variable. |
+| `++` and `--` | Increment/decrement pointer. |
+| `+` and `-` | Pointer arithmetic. |
+| `==`, `!=`, `<`, `>`, `<=`, and `>=` | Pointer comparison. |
+| [`stackalloc`](operators/stackalloc.md) | Allocate memory on the stack. |
+| [`fixed` statement](statements/fixed.md) | Pin a variable so its address can be taken. |
+
+See [Pointer-related operators](operators/pointer-related-operators.md) for details.
-For more information about pointer-related operators, see [Pointer-related operators](operators/pointer-related-operators.md).
+### Pointer conversions and Interop
-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.
+- Any pointer type can be implicitly converted to `void*`.
+- Any pointer type can be set to `null`.
+- 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`).
+- You can't dereference a `void*` directly, but you can cast it to another pointer type.
-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.
+For example, converting an `int*` to a `byte*` lets you examine individual bytes:
:::code language="csharp" source="snippets/unsafe-code/Conversions.cs" ID="Conversion":::
+### Pointer safety reminders
+
+- Dereferencing a null pointer is implementation-defined and may crash your program.
+- 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.
+- Never store a pointer to stack memory outside the current method.
+
## Fixed-size buffers
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`.
From c8868d9c4f6d774f038d248c35e101d630180168 Mon Sep 17 00:00:00 2001
From: Rich Lander <2608468+richlander@users.noreply.github.com>
Date: Tue, 8 Jul 2025 14:59:29 -0700
Subject: [PATCH 3/5] Update samples
---
.../snippets/unsafe-code/Conversions.cs | 6 +-
.../unsafe-code/FixedKeywordExamples.cs | 59 +++++++++++--------
.../snippets/unsafe-code/FunctionPointers.cs | 2 +-
docs/csharp/language-reference/unsafe-code.md | 4 +-
4 files changed, 39 insertions(+), 32 deletions(-)
diff --git a/docs/csharp/language-reference/snippets/unsafe-code/Conversions.cs b/docs/csharp/language-reference/snippets/unsafe-code/Conversions.cs
index 766d41dc2613d..6c38669db5d29 100644
--- a/docs/csharp/language-reference/snippets/unsafe-code/Conversions.cs
+++ b/docs/csharp/language-reference/snippets/unsafe-code/Conversions.cs
@@ -3,7 +3,7 @@ public class ClassConvert
public static void Conversions()
{
//
- int number = 1024;
+ int number = 1025;
unsafe
{
@@ -23,8 +23,8 @@ public static void Conversions()
System.Console.WriteLine($"The value of the integer: {number}");
/* Output:
- The 4 bytes of the integer: 00 04 00 00
- The value of the integer: 1024
+ The 4 bytes of the integer: 01 04 00 00
+ The value of the integer: 1025
*/
}
//
diff --git a/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs b/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
index 85f61116620ba..b4b22e5e3a125 100644
--- a/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
+++ b/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
@@ -20,38 +20,47 @@ private static void PointerTypes()
// 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.
+ Console.WriteLine("Print first two elements using interior pointers:");
+ Console.WriteLine(*p);
+ // p and its container object a are pinned
+ // The fixed pointer (p) cannot be incremented directly
+ // Another pointer is required for incrementing
int* p2 = p;
- Console.WriteLine(*p2);
- // Incrementing p2 bumps the pointer by four bytes due to its type ...
+ // Increment the p2 pointer by the size of one int
p2 += 1;
Console.WriteLine(*p2);
- p2 += 1;
+ // Increment the p2 pointer by the size of three ints (to the last element)
+ p2 += 3;
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("Increment each element by 5 using interior pointers");
+ for (int i = 0; i < a.Length; i++)
+ {
+ // p3 will reference past the end of the loop on the last iteration
+ // a good reason to keep it private to the loop
+ int* p3 = p + i;
+ *p3 += 5; // Increment the value at the pointer
+ }
}
}
- Console.WriteLine("--------");
- Console.WriteLine(a[0]);
+ Console.WriteLine("Print final values:");
+ foreach (var item in a)
+ {
+ Console.WriteLine(item);
+ }
- /*
- Output:
+ /* Output:
+ Print first two elements using interior pointers:
10
20
- 30
- --------
- 10
- 11
- 12
- --------
- 12
+ 50
+ Increment each element by 5 using interior pointers
+ Print final values:
+ 15
+ 25
+ 35
+ 45
+ 55
*/
//
}
@@ -102,10 +111,8 @@ 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("source or target is null");
- }
+ ArgumentNullException.ThrowIfNull(source, nameof(source));
+ ArgumentNullException.ThrowIfNull(target, nameof(target));
// If either offset, or the number of bytes to copy, is negative, you
// cannot complete the copy.
diff --git a/docs/csharp/language-reference/snippets/unsafe-code/FunctionPointers.cs b/docs/csharp/language-reference/snippets/unsafe-code/FunctionPointers.cs
index b9bb8b16b349e..64e017fef8aeb 100644
--- a/docs/csharp/language-reference/snippets/unsafe-code/FunctionPointers.cs
+++ b/docs/csharp/language-reference/snippets/unsafe-code/FunctionPointers.cs
@@ -11,9 +11,9 @@ public static void PointerExamples()
//
int product = 0;
+ static int localMultiply(int x, int y) => x * y;
unsafe
{
- static int localMultiply(int x, int y) => x * y;
product = UnsafeCombine(&localMultiply, 3, 4);
}
//
diff --git a/docs/csharp/language-reference/unsafe-code.md b/docs/csharp/language-reference/unsafe-code.md
index 6965df26ed2b1..39856ffecab27 100644
--- a/docs/csharp/language-reference/unsafe-code.md
+++ b/docs/csharp/language-reference/unsafe-code.md
@@ -16,7 +16,7 @@ helpviewer_keywords:
# Unsafe code
-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.
+C#'s unsafe code feature enables direct memory manipulation using pointers and other low-level constructs. These capabilities are 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 reading uninitialized/incorrect memory, buffer overruns, and use-after-free become possible. Unsafe code must appear within an [`unsafe`](keywords/unsafe.md) context and requires the [`AllowUnsafeBlocks`](compiler-options/language.md#allowunsafeblocks) compiler option.
Most C# code is safe code, where the compiler and .NET runtime enforce memory safety.
@@ -135,7 +135,7 @@ The size of the 128 element `char` array is 256 bytes. Fixed-size [char](builtin
The preceding example demonstrates accessing `fixed` fields without pinning. Another common fixed-size array is the [bool](builtin-types/bool.md) array. The elements in a `bool` array are always 1 byte in size. `bool` arrays aren't appropriate for creating bit arrays or buffers.
-Fixed-size buffers are compiled with the , which instructs the common language runtime (CLR) that a type contains an unmanaged array that can potentially overflow. Memory allocated using [stackalloc](operators/stackalloc.md) also automatically enables buffer overrun detection features in the CLR. The previous example shows how a fixed-size buffer could exist in an `unsafe struct`.
+Fixed-size buffers are compiled with the , which instructs the .NET runtime that a type contains an unmanaged array that can potentially overflow. Memory allocated using [stackalloc](operators/stackalloc.md) also automatically enables buffer overrun detection features in the CLR. The previous example shows how a fixed-size buffer could exist in an `unsafe struct`.
```csharp
internal unsafe struct Buffer
From 20164ea12ffcfd2e7a9ed9648b0bf5382bec20b8 Mon Sep 17 00:00:00 2001
From: Rich Lander <2608468+richlander@users.noreply.github.com>
Date: Tue, 8 Jul 2025 15:04:29 -0700
Subject: [PATCH 4/5] Delete confusion section
---
.../unsafe-code/FixedKeywordExamples.cs | 95 -------------------
docs/csharp/language-reference/unsafe-code.md | 10 --
2 files changed, 105 deletions(-)
diff --git a/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs b/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
index b4b22e5e3a125..6b5886dfd804a 100644
--- a/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
+++ b/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
@@ -105,99 +105,4 @@ private static void AccessEmbeddedArray()
}
}
//
-
- //
- 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.
- ArgumentNullException.ThrowIfNull(source, nameof(source));
- ArgumentNullException.ThrowIfNull(target, nameof(target));
-
- // 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("offset or bytes to copy is negative");
- }
-
- // 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("offset to end of array is less than bytes to be copied");
- }
-
- // 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
- */
- }
- //
}
diff --git a/docs/csharp/language-reference/unsafe-code.md b/docs/csharp/language-reference/unsafe-code.md
index 39856ffecab27..33cf0c60fdd4a 100644
--- a/docs/csharp/language-reference/unsafe-code.md
+++ b/docs/csharp/language-reference/unsafe-code.md
@@ -169,16 +169,6 @@ Fixed-size buffers differ from regular arrays in the following ways:
- They're always vectors, or one-dimensional arrays.
- The declaration should include the length, such as `fixed char id[8]`. You can't use `fixed char id[]`.
-## How to use pointers to copy an array of bytes
-
-The following example uses pointers to copy bytes from one array to another.
-
-This example uses the [unsafe](keywords/unsafe.md) keyword, which enables you to use pointers in the `Copy` method. The [fixed](statements/fixed.md) 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 garbage collection doesn't move the arrays. 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 [**AllowUnsafeBlocks**](compiler-options/language.md#allowunsafeblocks) 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.
-
-:::code language="csharp" source="snippets/unsafe-code/FixedKeywordExamples.cs" ID="8":::
-
## Function pointers
C# provides [`delegate`](builtin-types/reference-types.md#the-delegate-type) types to define safe function pointer objects. Invoking a delegate involves instantiating a type derived from and making a virtual method call to its `Invoke` method. This virtual call uses the `callvirt` IL instruction. In performance critical code paths, using the `calli` IL instruction is more efficient.
From 005c6a2ebf28aee19c2cb55ea39f6f236d17ddc4 Mon Sep 17 00:00:00 2001
From: Rich Lander <2608468+richlander@users.noreply.github.com>
Date: Thu, 10 Jul 2025 09:09:42 -0700
Subject: [PATCH 5/5] Remove method call
---
.../snippets/unsafe-code/FixedKeywordExamples.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs b/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
index 6b5886dfd804a..84adc53b827e7 100644
--- a/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
+++ b/docs/csharp/language-reference/snippets/unsafe-code/FixedKeywordExamples.cs
@@ -6,7 +6,6 @@ public static void Examples()
{
PointerTypes();
AccessEmbeddedArray();
- UnsafeCopyArrays();
}
private static void PointerTypes()