Skip to content

Commit c40149c

Browse files
RexJaeschkeBillWagner
authored andcommitted
Moved most of stackalloc spec to expressions
1 parent af5a4a9 commit c40149c

File tree

1 file changed

+30
-50
lines changed

1 file changed

+30
-50
lines changed

standard/unsafe-code.md

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,61 +1047,24 @@ When the outermost containing struct variable of a fixed-size buffer member is a
10471047
10481048
## 23.9 Stack allocation
10491049
1050-
In an unsafe context, a local variable declaration ([§13.6.2](statements.md#1362-local-variable-declarations)) may include a stack allocation initializer, which allocates a block of memory from the call stack.
1051-
1052-
```ANTLR
1053-
stackalloc_initializer
1054-
: 'stackalloc' unmanaged_type '[' expression ']'
1055-
| 'stackalloc' unmanaged_type? '[' expression? ']' stackalloc_initializer_elements
1056-
;
1057-
1058-
stackalloc_initializer_elements
1059-
: '{' stackalloc_initializer_element_list? '}'
1060-
| '{' stackalloc_initializer_element_list ',' '}'
1061-
;
1062-
1063-
stackalloc_initializer_element_list
1064-
: stackalloc_element_initializer (',' stackalloc_element_initializer)*
1065-
;
1066-
1067-
stackalloc_element_initializer
1068-
: expression
1069-
;
1070-
```
1071-
1072-
The *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)) indicates the type of the items that will be stored in the newly allocated location, and the *expression* indicates the number of these items. Taken together, these specify the required allocation size. Since the size of a stack allocation cannot be negative, it is a compile-time error to specify the number of items as a *constant_expression* that evaluates to a negative value.
1073-
1074-
If *unmanaged_type* is omitted, it is inferred from the corresponding *stackalloc_initializer_elements*. If *expression* is omitted from *stackalloc_initializer*, it is the number of *stackalloc_element_initializer*s in the corresponding *stackalloc_initializer_elements*.
1075-
1076-
When a *stackalloc_initializer* includes both *expression* and *stackalloc_initializer_elements* the number of elements in that *stackalloc_initializer_elements* shall match the value of *expression*.
1077-
1078-
A stack allocation initializer of the form stackalloc `T[E]` requires `T` to be an unmanaged type ([§8.8](types.md#88-unmanaged-types)) and `E` to be an expression implicitly convertible to type `int`. The construct allocates `E * sizeof(T)` bytes from the call stack and returns a pointer, of type `T*`, to the newly allocated block. If `E` is a negative value, then the behavior is undefined. If `E` is zero, then no allocation is made, and the pointer returned is implementation-defined. If there is not enough memory available to allocate a block of the given size, a `System.StackOverflowException` is thrown.
1079-
1080-
When *stackalloc_initializer_elements* is present, the *stackalloc_initializer_elelement_list* shall consist of a sequence of expressions, each having an implicit conversion to *unmanaged_type* ([§11.2](conversions.md#112-implicit-conversions)). The expressions initialize elements in the allocated memory in increasing order, starting with the element at index zero. In the absence of a *stackalloc_initializer_elements*, the content of the newly allocated memory is undefined.
1081-
1082-
Unlike access to arrays, access to the elements of a `stackalloc`ed block is an unsafe operation and is not range checked.
1050+
See §stack-allocation for general information about the operator `stackalloc`. Here, the ability of that operator to result in a pointer is discussed.
10831051
10841052
> *Example*:
1053+
>
10851054
> ```csharp
1086-
> int* pArr1 = stackalloc int[3]; // size 3, undefined values
1087-
> int* pArr2 = stackalloc int[3] { 10, 20, 30 }; // size 3, well-defined values
1088-
> int* pArr3 = stackalloc [] { 11, 12, 13 }; // size 3, type int inferred
1089-
> long* pArr4 = stackalloc[] { 11, 12, 13 }; // error; result is int*, but long* needed
1090-
> long* pArr5 = stackalloc[] { 11, 12L, 13 }; // converts 11 and 13; result is long*
1091-
> long* pArr6 = stackalloc long[] { 11, 12, 13 }; // converts all, result is long*
1055+
> int* p1 = stackalloc int[3]; // memory uninitialized
1056+
> int* p2 = stackalloc int[3] { -10, -15, -30 }; // memory initialized
1057+
> int* p3 = stackalloc[] { 11, 12, 13 }; // type int is inferred
1058+
> var p4 = stackalloc[] { 11, 12, 13 }; // can't infer context, so pointer result assumed
1059+
> long* p5 = stackalloc[] { 11, 12, 13 }; // error; no conversion exists
1060+
> long* p6 = stackalloc[] { 11, 12L, 13 }; // converts 11 and 13, and returns long*
1061+
> long* p7 = stackalloc long[] { 11, 12, 13 }; // converts all and returns long*
10921062
> ```
1063+
>
10931064
> *end example*
10941065
1095-
Stack allocation initializers are not permitted in `catch` or `finally` blocks ([§13.11](statements.md#1311-the-try-statement)).
1096-
1097-
> *Note*: There is no way to explicitly free memory allocated using `stackalloc`. *end note*
1098-
1099-
All stack-allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns.
1100-
1101-
> *Note*: This corresponds to the `alloca` function, an extension commonly found in C and C++ implementations. *end note*
1102-
<!-- markdownlint-disable MD028 -->
1066+
Unlike access to arrays, access to the elements of a `stackalloc`ed block is an unsafe operation and is not range checked.
11031067
1104-
<!-- markdownlint-enable MD028 -->
11051068
> *Example*: In the following code
11061069
>
11071070
> <!-- Example: {template:"standalone-console", name:"StackAllocation", expectedOutput:["12345","-999"]} -->
@@ -1142,8 +1105,25 @@ All stack-allocated memory blocks created during the execution of a function mem
11421105
>
11431106
> a `stackalloc` initializer is used in the `IntToString` method to allocate a buffer of 16 characters on the stack. The buffer is automatically discarded when the method returns.
11441107
>
1108+
> Note, however, that `IntToString` can be rewritten in safe mode; that is, without using pointers, as follows:
1109+
>
1110+
> ```csharp
1111+
> public static string IntToString(int value)
1112+
> {
1113+
> if (value == int.MinValue) return "-2147483648"; // this value has no positive equivalent
1114+
> int n = value >= 0 ? value : -value;
1115+
> Span<char> buffer = stackalloc char[16];
1116+
> int idx = 16;
1117+
> do
1118+
> {
1119+
> buffer[--idx] = (char)(n % 10 + '0');
1120+
> n /= 10;
1121+
> } while (n != 0);
1122+
> if (value < 0) buffer[--idx] = '-';
1123+
> return buffer.Slice(idx).ToString();
1124+
> }
1125+
>
1126+
> ```
11451127
> *end example*
11461128
1147-
Except for the `stackalloc` operator, C# provides no predefined constructs for managing non-garbage collected memory. Such services are typically provided by supporting class libraries or imported directly from the underlying operating system.
1148-
11491129
**End of conditionally normative text.**

0 commit comments

Comments
 (0)