- •Foreword
- •Introduction
- •Scope
- •Conformance
- •Normative references
- •Definitions
- •Notational conventions
- •Acronyms and abbreviations
- •General description
- •Language overview
- •Getting started
- •Types
- •Predefined types
- •Conversions
- •Array types
- •Type system unification
- •Variables and parameters
- •Automatic memory management
- •Expressions
- •Statements
- •Classes
- •Constants
- •Fields
- •Methods
- •Properties
- •Events
- •Operators
- •Indexers
- •Instance constructors
- •Destructors
- •Static constructors
- •Inheritance
- •Static classes
- •Partial type declarations
- •Structs
- •Interfaces
- •Delegates
- •Enums
- •Namespaces and assemblies
- •Versioning
- •Extern Aliases
- •Attributes
- •Generics
- •Why generics?
- •Creating and consuming generics
- •Multiple type parameters
- •Constraints
- •Generic methods
- •Anonymous methods
- •Iterators
- •Lexical structure
- •Programs
- •Grammars
- •Lexical grammar
- •Syntactic grammar
- •Grammar ambiguities
- •Lexical analysis
- •Line terminators
- •Comments
- •White space
- •Tokens
- •Unicode escape sequences
- •Identifiers
- •Keywords
- •Literals
- •Boolean literals
- •Integer literals
- •Real literals
- •Character literals
- •String literals
- •The null literal
- •Operators and punctuators
- •Pre-processing directives
- •Conditional compilation symbols
- •Pre-processing expressions
- •Declaration directives
- •Conditional compilation directives
- •Diagnostic directives
- •Region control
- •Line directives
- •Pragma directives
- •Basic concepts
- •Application startup
- •Application termination
- •Declarations
- •Members
- •Namespace members
- •Struct members
- •Enumeration members
- •Class members
- •Interface members
- •Array members
- •Delegate members
- •Member access
- •Declared accessibility
- •Accessibility domains
- •Protected access for instance members
- •Accessibility constraints
- •Signatures and overloading
- •Scopes
- •Name hiding
- •Hiding through nesting
- •Hiding through inheritance
- •Namespace and type names
- •Unqualified name
- •Fully qualified names
- •Automatic memory management
- •Execution order
- •Types
- •Value types
- •The System.ValueType type
- •Default constructors
- •Struct types
- •Simple types
- •Integral types
- •Floating point types
- •The decimal type
- •The bool type
- •Enumeration types
- •Reference types
- •Class types
- •The object type
- •The string type
- •Interface types
- •Array types
- •Delegate types
- •Boxing and unboxing
- •Boxing conversions
- •Unboxing conversions
- •Variables
- •Variable categories
- •Static variables
- •Instance variables
- •Instance variables in classes
- •Instance variables in structs
- •Array elements
- •Value parameters
- •Reference parameters
- •Output parameters
- •Local variables
- •Default values
- •Definite assignment
- •Initially assigned variables
- •Initially unassigned variables
- •Precise rules for determining definite assignment
- •General rules for statements
- •Block statements, checked, and unchecked statements
- •Expression statements
- •Declaration statements
- •If statements
- •Switch statements
- •While statements
- •Do statements
- •For statements
- •Break, continue, and goto statements
- •Throw statements
- •Return statements
- •Try-catch statements
- •Try-finally statements
- •Try-catch-finally statements
- •Foreach statements
- •Using statements
- •Lock statements
- •General rules for simple expressions
- •General rules for expressions with embedded expressions
- •Invocation expressions and object creation expressions
- •Simple assignment expressions
- •&& expressions
- •|| expressions
- •! expressions
- •?: expressions
- •Anonymous method expressions
- •Yield statements
- •Variable references
- •Atomicity of variable references
- •Conversions
- •Implicit conversions
- •Identity conversion
- •Implicit numeric conversions
- •Implicit enumeration conversions
- •Implicit reference conversions
- •Boxing conversions
- •Implicit type parameter conversions
- •Implicit constant expression conversions
- •User-defined implicit conversions
- •Explicit conversions
- •Explicit numeric conversions
- •Explicit enumeration conversions
- •Explicit reference conversions
- •Unboxing conversions
- •User-defined explicit conversions
- •Standard conversions
- •Standard implicit conversions
- •Standard explicit conversions
- •User-defined conversions
- •Permitted user-defined conversions
- •Evaluation of user-defined conversions
- •User-defined implicit conversions
- •User-defined explicit conversions
- •Anonymous method conversions
- •Method group conversions
- •Expressions
- •Expression classifications
- •Values of expressions
- •Operators
- •Operator precedence and associativity
- •Operator overloading
- •Unary operator overload resolution
- •Binary operator overload resolution
- •Candidate user-defined operators
- •Numeric promotions
- •Unary numeric promotions
- •Binary numeric promotions
- •Member lookup
- •Base types
- •Function members
- •Argument lists
- •Overload resolution
- •Applicable function member
- •Better function member
- •Better conversion
- •Function member invocation
- •Invocations on boxed instances
- •Primary expressions
- •Literals
- •Simple names
- •Invariant meaning in blocks
- •Parenthesized expressions
- •Member access
- •Identical simple names and type names
- •Invocation expressions
- •Method invocations
- •Delegate invocations
- •Element access
- •Array access
- •Indexer access
- •This access
- •Base access
- •Postfix increment and decrement operators
- •The new operator
- •Object creation expressions
- •Array creation expressions
- •Delegate creation expressions
- •The typeof operator
- •The checked and unchecked operators
- •Default value expression
- •Anonymous methods
- •Anonymous method signatures
- •Anonymous method blocks
- •Outer variables
- •Captured outer variables
- •Instantiation of local variables
- •Anonymous method evaluation
- •Implementation example
- •Unary expressions
- •Unary plus operator
- •Unary minus operator
- •Logical negation operator
- •Bitwise complement operator
- •Prefix increment and decrement operators
- •Cast expressions
- •Arithmetic operators
- •Multiplication operator
- •Division operator
- •Remainder operator
- •Addition operator
- •Subtraction operator
- •Shift operators
- •Relational and type-testing operators
- •Integer comparison operators
- •Floating-point comparison operators
- •Decimal comparison operators
- •Boolean equality operators
- •Enumeration comparison operators
- •Reference type equality operators
- •String equality operators
- •Delegate equality operators
- •The is operator
- •The as operator
- •Logical operators
- •Integer logical operators
- •Enumeration logical operators
- •Boolean logical operators
- •Conditional logical operators
- •Boolean conditional logical operators
- •User-defined conditional logical operators
- •Conditional operator
- •Assignment operators
- •Simple assignment
- •Compound assignment
- •Event assignment
- •Expression
- •Constant expressions
- •Boolean expressions
- •Statements
- •End points and reachability
- •Blocks
- •Statement lists
- •The empty statement
- •Labeled statements
- •Declaration statements
- •Local variable declarations
- •Local constant declarations
- •Expression statements
- •Selection statements
- •The if statement
- •The switch statement
- •Iteration statements
- •The while statement
- •The do statement
- •The for statement
- •The foreach statement
- •Jump statements
- •The break statement
- •The continue statement
- •The goto statement
- •The return statement
- •The throw statement
- •The try statement
- •The checked and unchecked statements
- •The lock statement
- •The using statement
- •The yield statement
- •Namespaces
- •Compilation units
- •Namespace declarations
- •Extern alias directives
- •Using directives
- •Using alias directives
- •Using namespace directives
- •Namespace members
- •Type declarations
- •Qualified alias member
- •Classes
- •Class declarations
- •Class modifiers
- •Abstract classes
- •Sealed classes
- •Static classes
- •Class base specification
- •Base classes
- •Interface implementations
- •Class body
- •Partial declarations
- •Class members
- •Inheritance
- •The new modifier
- •Access modifiers
- •Constituent types
- •Static and instance members
- •Nested types
- •Fully qualified name
- •Declared accessibility
- •Hiding
- •this access
- •Reserved member names
- •Member names reserved for properties
- •Member names reserved for events
- •Member names reserved for indexers
- •Member names reserved for destructors
- •Constants
- •Fields
- •Static and instance fields
- •Readonly fields
- •Using static readonly fields for constants
- •Versioning of constants and static readonly fields
- •Volatile fields
- •Field initialization
- •Variable initializers
- •Static field initialization
- •Instance field initialization
- •Methods
- •Method parameters
- •Value parameters
- •Reference parameters
- •Output parameters
- •Parameter arrays
- •Static and instance methods
- •Virtual methods
- •Override methods
- •Sealed methods
- •Abstract methods
- •External methods
- •Method body
- •Method overloading
- •Properties
- •Static and instance properties
- •Accessors
- •Virtual, sealed, override, and abstract accessors
- •Events
- •Field-like events
- •Event accessors
- •Static and instance events
- •Virtual, sealed, override, and abstract accessors
- •Indexers
- •Indexer overloading
- •Operators
- •Unary operators
- •Binary operators
- •Conversion operators
- •Instance constructors
- •Constructor initializers
- •Instance variable initializers
- •Constructor execution
- •Default constructors
- •Private constructors
- •Optional instance constructor parameters
- •Static constructors
- •Destructors
- •Structs
- •Struct declarations
- •Struct modifiers
- •Struct interfaces
- •Struct body
- •Struct members
- •Class and struct differences
- •Value semantics
- •Inheritance
- •Assignment
- •Default values
- •Boxing and unboxing
- •Meaning of this
- •Field initializers
- •Constructors
- •Destructors
- •Static constructors
- •Struct examples
- •Database integer type
- •Database boolean type
- •Arrays
- •Array types
- •The System.Array type
- •Array creation
- •Array element access
- •Array members
- •Array covariance
- •Arrays and the generic IList interface
- •Array initializers
- •Interfaces
- •Interface declarations
- •Interface modifiers
- •Base interfaces
- •Interface body
- •Interface members
- •Interface methods
- •Interface properties
- •Interface events
- •Interface indexers
- •Interface member access
- •Fully qualified interface member names
- •Interface implementations
- •Explicit interface member implementations
- •Interface mapping
- •Interface implementation inheritance
- •Interface re-implementation
- •Abstract classes and interfaces
- •Enums
- •Enum declarations
- •Enum modifiers
- •Enum members
- •The System.Enum type
- •Enum values and operations
- •Delegates
- •Delegate declarations
- •Delegate instantiation
- •Delegate invocation
- •Exceptions
- •Causes of exceptions
- •The System.Exception class
- •How exceptions are handled
- •Common Exception Classes
- •Attributes
- •Attribute classes
- •Attribute usage
- •Positional and named parameters
- •Attribute parameter types
- •Attribute specification
- •Attribute instances
- •Compilation of an attribute
- •Run-time retrieval of an attribute instance
- •Reserved attributes
- •The AttributeUsage attribute
- •The Conditional attribute
- •Conditional Methods
- •Conditional Attribute Classes
- •The Obsolete attribute
- •Unsafe code
- •Unsafe contexts
- •Pointer types
- •Fixed and moveable variables
- •Pointer conversions
- •Pointers in expressions
- •Pointer indirection
- •Pointer member access
- •Pointer element access
- •The address-of operator
- •Pointer increment and decrement
- •Pointer arithmetic
- •Pointer comparison
- •The sizeof operator
- •The fixed statement
- •Stack allocation
- •Dynamic memory allocation
- •Generics
- •Generic class declarations
- •Type parameters
- •The instance type
- •Members of generic classes
- •Static fields in generic classes
- •Static constructors in generic classes
- •Accessing protected members
- •Overloading in generic classes
- •Parameter array methods and type parameters
- •Overriding and generic classes
- •Operators in generic classes
- •Nested types in generic classes
- •Generic struct declarations
- •Generic interface declarations
- •Uniqueness of implemented interfaces
- •Explicit interface member implementations
- •Generic delegate declarations
- •Constructed types
- •Type arguments
- •Open and closed types
- •Base classes and interfaces of a constructed type
- •Members of a constructed type
- •Accessibility of a constructed type
- •Conversions
- •Using alias directives
- •Generic methods
- •Generic method signatures
- •Virtual generic methods
- •Calling generic methods
- •Inference of type arguments
- •Using a generic method with a delegate
- •Constraints
- •Satisfying constraints
- •Member lookup on type parameters
- •Type parameters and boxing
- •Conversions involving type parameters
- •Iterators
- •Iterator blocks
- •Enumerator interfaces
- •Enumerable interfaces
- •Yield type
- •This access
- •Enumerator objects
- •The MoveNext method
- •The Current property
- •The Dispose method
- •Enumerable objects
- •The GetEnumerator method
- •Implementation example
- •Lexical grammar
- •Line terminators
- •White space
- •Comments
- •Unicode character escape sequences
- •Identifiers
- •Keywords
- •Literals
- •Operators and punctuators
- •Pre-processing directives
- •Syntactic grammar
- •Basic concepts
- •Types
- •Expressions
- •Statements
- •Classes
- •Structs
- •Arrays
- •Interfaces
- •Enums
- •Delegates
- •Attributes
- •Generics
- •Grammar extensions for unsafe code
- •Undefined behavior
- •Implementation-defined behavior
- •Unspecified behavior
- •Other Issues
- •Capitalization styles
- •Pascal casing
- •Camel casing
- •All uppercase
- •Capitalization summary
- •Word choice
- •Namespaces
- •Classes
- •Interfaces
- •Enums
- •Static fields
- •Parameters
- •Methods
- •Properties
- •Events
- •Case sensitivity
- •Avoiding type name confusion
- •Documentation Comments
- •Introduction
- •Recommended tags
- •<code>
- •<example>
- •<exception>
- •<list>
- •<para>
- •<param>
- •<paramref>
- •<permission>
- •<remarks>
- •<returns>
- •<seealso>
- •<summary>
- •<value>
- •Processing the documentation file
- •ID string format
- •ID string examples
- •An example
- •C# source code
- •Resulting XML
Chapter 25 Unsafe code
1Conversions between two pointer types never change the actual pointer value. In other words, a conversion
2from one pointer type to another has no effect on the underlying address given by the pointer.
3When one pointer type is converted to another, if the resulting pointer is not correctly aligned for the
4pointed-to type, the behavior is undefined if the result is dereferenced. In general, the concept “correctly
5aligned” is transitive: if a pointer to type A is correctly aligned for a pointer to type B, which, in turn, is
6correctly aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C.
7[Example: Consider the following case in which a variable having one type is accessed via a pointer to a
8different type:
9char c = 'A';
10char* pc = &c;
11void* pv = pc;
12int* pi = (int*)pv;
13 |
int |
i |
= *pi; |
// |
undefined |
14 |
*pi |
= |
123456; |
// |
undefined |
15end example]
16When a pointer type is converted to a pointer to byte, the result points to the lowest addressed byte of the
17variable. Successive increments of the result, up to the size of the variable, yield pointers to the remaining
18bytes of that variable. [Example: The following method displays each of the eight bytes in a double as a
19hexadecimal value:
20using System;
21class Test
22{
23 |
static void Main() { |
24 |
double d = 123.456e23; |
25 |
unsafe { |
26 |
byte* pb = (byte*)&d; |
27 |
for (int i = 0; i < sizeof(double); ++i) |
28 |
Console.Write(" {0,2:X}", (uint)(*pb++)); |
29 |
Console.WriteLine(); |
30 |
} |
31}
32}
33Of course, the output produced depends on byte ordering. end example]
34Mappings between pointers and integers are implementation-defined. [Note: However, on 32and 64-bit
35CPU architectures with a linear address space, conversions of pointers to or from integral types typically
36behave exactly like conversions of uint or ulong values, respectively, to or from those integral types. end
37note]
3825.5 Pointers in expressions
39In an unsafe context, an expression can yield a result of a pointer type, but outside an unsafe context it is a
40compile-time error for an expression to be of a pointer type. In precise terms, outside an unsafe context a
41compile-time error occurs if any simple-name (§14.5.2), member-access (§14.5.4), invocation-expression
42(§14.5.5), or element-access (§14.5.6) is of a pointer type.
43The primary-no-array-creation-expression (§14.5) productions permit the following additional construct:
44primary-no-array-creation-expression:
45
46
…
sizeof-expression
47In an unsafe context, the primary-no-array-creation-expression (§14.5) and unary-expression (§14.5.13)
48productions permit the following additional constructs:
49primary-no-array-creation-expression:
50
51
52
…
pointer-member-access pointer-element-access
385
C# LANGUAGE SPECIFICATION
1
2
3
4
unary-expression:
…
pointer-indirection-expression addressof-expression
5These constructs are described in the following subclauses.
6There are also several predefined unary and binary operators for pointers. When applying unary or binary
7operator overload resolution, if none of the actual operands is a pointer type, then any predefined unary or
8binary operator with a pointer parameter type is removed from consideration.
9[Note: The precedence and associativity of the unsafe operators is implied by the grammar. end note]
1025.5.1 Pointer indirection
11A pointer-indirection-expression consists of an asterisk (*) followed by a unary-expression.
12pointer-indirection-expression:
13* unary-expression
14The unary * operator denotes pointer indirection and is used to obtain the variable to which a pointer points.
15The result of evaluating *P, where P is an expression of a pointer type T*, is a variable of type T. It is a
16compile-time error to apply the unary * operator to an expression of type void* or to an expression that
17isn’t of a pointer type.
18The effect of applying the unary * operator to a null pointer is implementation-defined. In particular, there
19is no guarantee that this operation throws a System.NullReferenceException.
20If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined. [Note:
21Among the invalid values for dereferencing a pointer by the unary * operator are an address inappropriately
22aligned for the type pointed to (see example in §25.4), and the address of a variable after the end of its
23lifetime. end note]
24For purposes of definite assignment analysis, a variable produced by evaluating an expression of the form
25*P is considered initially assigned (§12.3.1).
2625.5.2 Pointer member access
27A pointer-member-access consists of a primary-expression, followed by a “->” token, followed by an
28identifier.
29pointer-member-access:
30 |
primary-expression -> identifier type-argument-listopt |
31In a pointer member access of the form P->I, P shall be an expression of a pointer type other than void*,
32and I shall denote an accessible member of the type to which P points.
33A pointer member access of the form P->I is evaluated exactly as (*P).I. For a description of the pointer
34indirection operator (*), see §25.5.1. For a description of the member access operator (.), see §14.5.4.
35[Example: In the following code
36struct Point
37{
38 |
public int x; |
39 |
public int y; |
40 |
public override string ToString() { |
41 |
return "(" + x + "," + y + ")"; |
42}
43}
44using System;
386
Chapter 25 Unsafe code
1class Test
2{
3 |
static void Main() { |
4 |
Point point; |
5 |
unsafe { |
6 |
Point* p = &point; |
7 |
p->x = 10; |
8 |
p->y = 20; |
9 |
Console.WriteLine(p->ToString()); |
10 |
} |
11}
12}
13the -> operator is used to access fields and invoke a method of a struct through a pointer. Because the
14operation P->I is precisely equivalent to (*P).I, the Main method could equally well have been written:
15using System;
16class Test
17{
18 |
static void Main() { |
19 |
Point point; |
20 |
unsafe { |
21 |
Point* p = &point; |
22 |
(*p).x = 10; |
23 |
(*p).y = 20; |
24 |
Console.WriteLine((*p).ToString()); |
25 |
} |
26}
27}
28end example]
2925.5.3 Pointer element access
30A pointer-element-access consists of a primary-no-array-creation-expression followed by an expression
31enclosed in “[” and “]”.
32pointer-element-access:
33 |
primary-no-array-creation-expression [ expression ] |
34In a pointer element access of the form P[E], P shall be an expression of a pointer type other than void*,
35and E shall be an expression of a type that can be implicitly converted to int, uint, long, or ulong.
36A pointer element access of the form P[E] is evaluated exactly as *(P + E). For a description of the pointer
37indirection operator (*), see §25.5.1. For a description of the pointer addition operator (+), see §25.5.6.
38[Example: In the following code
39class Test
40{
41 |
static void Main() { |
42 |
unsafe { |
43 |
char* p = stackalloc char[256]; |
44 |
for (int i = 0; i < 256; i++) p[i] = (char)i; |
45 |
} |
46}
47}
48a pointer element access is used to initialize the character buffer in a for loop. Because the operation P[E]
49is precisely equivalent to *(P + E), the example could equally well have been written:
387
C# LANGUAGE SPECIFICATION
1class Test
2{
3 |
static void Main() { |
4 |
unsafe { |
5 |
char* p = stackalloc char[256]; |
6 |
for (int i = 0; i < 256; i++) *(p + i) = (char)i; |
7 |
} |
8}
9}
10end example]
11The pointer element access operator does not check for out-of-bounds errors and the behavior when
12accessing an out-of-bounds element is undefined. [Note: This is the same as C and C++. end note]
1325.5.4 The address-of operator
14An addressof-expression consists of an ampersand (&) followed by a unary-expression.
15addressof-expression:
16& unary-expression
17Given an expression E which is of a type T and is classified as a fixed variable (§25.3), the construct &E
18computes the address of the variable given by E. The type of the result is T* and is classified as a value. A
19compile-time error occurs if E is not classified as a variable, if E is classified as a volatile field (§17.4.3), or
20if E denotes a moveable variable. In the last case, a fixed statement (§25.6) can be used to temporarily “fix”
21the variable before obtaining its address.
22The & operator does not require its argument to be definitely assigned, but following an & operation, the
23variable to which the operator is applied is considered definitely assigned in the execution path in which the
24operation occurs. It is the responsibility of the programmer to ensure that correct initialization of the variable
25actually does take place in this situation.
26[Example: In the following code
27using System;
28class Test
29{
30 |
static void Main() { |
31 |
int i; |
32 |
unsafe { |
33 |
int* p = &i; |
34 |
*p = 123; |
35 |
} |
36 |
Console.WriteLine(i); |
37}
38}
39i is considered definitely assigned following the &i operation used to initialize p. The assignment to *p in
40effect initializes i, but the inclusion of this initialization is the responsibility of the programmer, and no
41compile-time error would occur if the assignment were removed. end example]
42[Note: The rules of definite assignment for the & operator exist such that redundant initialization of local
43variables can be avoided. For example, many external APIs take a pointer to a structure which is filled in by
44the API. Calls to such APIs typically pass the address of a local struct variable, and without the rule,
45redundant initialization of the struct variable would be required. end note]
46[Note: As stated in §14.5.4, outside an instance constructor or static constructor for a struct or class that
47defines a readonly field, that field is considered a value, not a variable. As such, its address cannot be taken.
48Similarly, the address of a constant cannot be taken. end note]
49When a local variable, value parameter, or parameter array is captured by an anonymous method
50(§14.5.14.3.1), that local variable, parameter, or parameter array is no longer considered to be a fixed
51variable (§25.3), but is instead considered to be a moveable variable. Thus it is an error for any unsafe
388
