アンセーフ コードUnsafe code

前の章で定義されているように、core、c# 言語でのデータ型としてのポインターの省略、C および C++ とは大きく異なります。The core C# language, as defined in the preceding chapters, differs notably from C and C++ in its omission of pointers as a data type. 代わりに、c# には、参照、およびガベージ コレクターによって管理されているオブジェクトを作成することができます。Instead, C# provides references and the ability to create objects that are managed by a garbage collector. このデザインは、その他の機能を組み合わせることによって、c# は、C や C++ よりもはるかにより安全な言語にします。This design, coupled with other features, makes C# a much safer language than C or C++. Core c# 言語で単にことはできません、初期化されていない変数、「未解決」のポインターまたは配列の境界を越えてのインデックスを作成する式。In the core C# language it is simply not possible to have an uninitialized variable, a "dangling" pointer, or an expression that indexes an array beyond its bounds. バグの全体カテゴリがちな C および C++ プログラムのため削除します。Whole categories of bugs that routinely plague C and C++ programs are thus eliminated.

C または C++ で実際上すべてのポインター型のコンス トラクター (C#)、対応する参照型には、それでも、状況は、ポインター型へのアクセスが必要になります。While practically every pointer type construct in C or C++ has a reference type counterpart in C#, nonetheless, there are situations where access to pointer types becomes a necessity. たとえば、基になるオペレーティング システムとやり取り、メモリ マップト デバイスへのアクセスやタイム クリティカルなアルゴリズムを実装できない可能性があります不可能または非現実的ポインターへのアクセスなし。For example, interfacing with the underlying operating system, accessing a memory-mapped device, or implementing a time-critical algorithm may not be possible or practical without access to pointers. このニーズに対処する c# 機能を提供します、書き込むアンセーフ コードします。To address this need, C# provides the ability to write unsafe code.

安全でないコードを宣言し、ポインターと、変数のアドレスを整数型の間の変換を実行する、ポインターに使用してなどです。In unsafe code it is possible to declare and operate on pointers, to perform conversions between pointers and integral types, to take the address of variables, and so forth. アンセーフ コードを記述することは、ある意味で、c# プログラム内での C コードの記述と似ています。In a sense, writing unsafe code is much like writing C code within a C# program.

アンセーフ コードは、実際には、開発者とユーザーの両方の観点からの「安全」機能です。Unsafe code is in fact a "safe" feature from the perspective of both developers and users. 修飾子の付いた、アンセーフ コードを明確にマークする必要がありますunsafe開発者の安全でない機能を誤って、使用可能性があることはできませんし、実行エンジンは信頼されていない環境でアンセーフ コードを実行できないようにするため、します。Unsafe code must be clearly marked with the modifier unsafe, so developers can't possibly use unsafe features accidentally, and the execution engine works to ensure that unsafe code cannot be executed in an untrusted environment.

Unsafe コンテキストUnsafe contexts

C# の安全でない機能は、unsafe コンテキストでのみ使用できます。The unsafe features of C# are available only in unsafe contexts. Unsafe コンテキストを導入、unsafeまたは使用することによって、型またはメンバーの宣言に修飾子をunsafe_statement:An unsafe context is introduced by including an unsafe modifier in the declaration of a type or member, or by employing an unsafe_statement:

  • クラス、構造体、インターフェイス、またはデリゲートの宣言を含めることができます、unsafe修飾子でその型の宣言 (クラス、構造体、またはインターフェイスの本文を含む) の全体的なテキスト範囲の場合は、unsafe コンテキストと見なされます。A declaration of a class, struct, interface, or delegate may include an unsafe modifier, in which case the entire textual extent of that type declaration (including the body of the class, struct, or interface) is considered an unsafe context.
  • フィールド、メソッド、プロパティ、イベント、インデクサー、演算子、インスタンス コンス トラクター、デストラクター、または静的コンス トラクターの宣言を含めることができます、unsafe修飾子でそのメンバーの宣言の全体的なテキスト範囲の場合は、安全でないです。コンテキスト。A declaration of a field, method, property, event, indexer, operator, instance constructor, destructor, or static constructor may include an unsafe modifier, in which case the entire textual extent of that member declaration is considered an unsafe context.
  • Unsafe_statement 、unsafe コンテキスト内で使用できるように、ブロックします。An unsafe_statement enables the use of an unsafe context within a block. 全体的なテキスト範囲の関連付けられているブロックは unsafe コンテキストと見なされます。The entire textual extent of the associated block is considered an unsafe context.

関連付けられている文法は、以下に示します。The associated grammar productions are shown below.

class_modifier_unsafe
    : 'unsafe'
    ;

struct_modifier_unsafe
    : 'unsafe'
    ;

interface_modifier_unsafe
    : 'unsafe'
    ;

delegate_modifier_unsafe
    : 'unsafe'
    ;

field_modifier_unsafe
    : 'unsafe'
    ;

method_modifier_unsafe
    : 'unsafe'
    ;

property_modifier_unsafe
    : 'unsafe'
    ;

event_modifier_unsafe
    : 'unsafe'
    ;

indexer_modifier_unsafe
    : 'unsafe'
    ;

operator_modifier_unsafe
    : 'unsafe'
    ;

constructor_modifier_unsafe
    : 'unsafe'
    ;

destructor_declaration_unsafe
    : attributes? 'extern'? 'unsafe'? '~' identifier '(' ')' destructor_body
    | attributes? 'unsafe'? 'extern'? '~' identifier '(' ')' destructor_body
    ;

static_constructor_modifiers_unsafe
    : 'extern'? 'unsafe'? 'static'
    | 'unsafe'? 'extern'? 'static'
    | 'extern'? 'static' 'unsafe'?
    | 'unsafe'? 'static' 'extern'?
    | 'static' 'extern'? 'unsafe'?
    | 'static' 'unsafe'? 'extern'?
    ;

embedded_statement_unsafe
    : unsafe_statement
    | fixed_statement
    ;

unsafe_statement
    : 'unsafe' block
    ;

In the example

public unsafe struct Node
{
    public int Value;
    public Node* Left;
    public Node* Right;
}

unsafe構造体の宣言で指定した修飾子により、unsafe コンテキストに構造体の宣言の全体的なテキスト範囲。the unsafe modifier specified in the struct declaration causes the entire textual extent of the struct declaration to become an unsafe context. したがって、宣言することは、LeftRightポインター型にするフィールド。Thus, it is possible to declare the Left and Right fields to be of a pointer type. 上記の例も記述できます。The example above could also be written

public struct Node
{
    public int Value;
    public unsafe Node* Left;
    public unsafe Node* Right;
}

ここでは、unsafeフィールドの宣言に修飾子がそれらの宣言に、unsafe コンテキストと見なされます。Here, the unsafe modifiers in the field declarations cause those declarations to be considered unsafe contexts.

したがって、ポインター型の使用を許可する、unsafe コンテキストを確立する以外、unsafe修飾子は、型またはメンバーに影響を与えません。Other than establishing an unsafe context, thus permitting the use of pointer types, the unsafe modifier has no effect on a type or a member. In the example

public class A
{
    public unsafe virtual void F() {
        char* p;
        ...
    }
}

public class B: A
{
    public override void F() {
        base.F();
        ...
    }
}

unsafe修飾子をFメソッドAのテキストの範囲が表示されるだけFになる、unsafe コンテキストの言語の安全でない機能を使用できます。the unsafe modifier on the F method in A simply causes the textual extent of F to become an unsafe context in which the unsafe features of the language can be used. オーバーライドでFB、再指定する必要はありません、unsafe修飾子--しない限り、もちろん、FメソッドB安全でない機能にアクセスする必要自体。In the override of F in B, there is no need to re-specify the unsafe modifier -- unless, of course, the F method in B itself needs access to unsafe features.

ポインター型がメソッドのシグネチャの一部である場合は、状況は若干異なりますThe situation is slightly different when a pointer type is part of the method's signature

public unsafe class A
{
    public virtual void F(char* p) {...}
}

public class B: A
{
    public unsafe override void F(char* p) {...}
}

ここでは、ためFの署名には、ポインター型が含まれていることができますのみ記述する必要が、unsafe コンテキストでします。Here, because F's signature includes a pointer type, it can only be written in an unsafe context. ただし、unsafe コンテキストは、いずれかを行うクラス全体、安全でないでは、によって発生する可能性A、またはを含めることによって、unsafeメソッドの宣言に修飾子の場合と同様Bします。However, the unsafe context can be introduced by either making the entire class unsafe, as is the case in A, or by including an unsafe modifier in the method declaration, as is the case in B.

ポインター型Pointer types

Unsafe コンテキストで、() 可能性があります、 pointer_typeと同様に、 value_typeまたはreference_type.In an unsafe context, a type (Types) may be a pointer_type as well as a value_type or a reference_type. ただし、 pointer_typeでも使用できます、typeof式 (匿名オブジェクト作成式)、unsafe コンテキストの外部でそのための使用は安全でないです。However, a pointer_type may also be used in a typeof expression (Anonymous object creation expressions) outside of an unsafe context as such usage is not unsafe.

type_unsafe
    : pointer_type
    ;

A pointer_typeとして書き込まれますが、 unmanaged_typeまたはキーワードvoid、その後に、*トークン。A pointer_type is written as an unmanaged_type or the keyword void, followed by a * token:

pointer_type
    : unmanaged_type '*'
    | 'void' '*'
    ;

unmanaged_type
    : type
    ;

前に指定された型、*ポインターの型が呼び出される、参照先の型ポインター型のです。The type specified before the * in a pointer type is called the referent type of the pointer type. ポインター型の値が指す変数の型を表します。It represents the type of the variable to which a value of the pointer type points.

(参照型の値) の参照とは異なり、ポインターは、ガベージ コレクターによって追跡されません--ガベージ コレクターがポインターをポイントするデータに関する知識を持たない。Unlike references (values of reference types), pointers are not tracked by the garbage collector -- the garbage collector has no knowledge of pointers and the data to which they point. 参照を指すポインターをこの理由は許可されていませんまたは構造体への参照を格納するいると、ポインターの参照先の型である必要があります、 unmanaged_typeします。For this reason a pointer is not permitted to point to a reference or to a struct that contains references, and the referent type of a pointer must be an unmanaged_type.

Unmanaged_typeはない任意の型は、 reference_typeまたは構築された型、およびが含まれていないreference_typeまたは任意のレベルの型フィールドを構築入れ子にします。An unmanaged_type is any type that isn't a reference_type or constructed type, and doesn't contain reference_type or constructed type fields at any level of nesting. つまり、 unmanaged_typeは、次の 1 つです。In other words, an unmanaged_type is one of the following:

  • sbytebyteshortushortintuintlongulongcharfloatdoubledecimal、またはboolします。sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • すべてenum_typeします。Any enum_type.
  • すべてpointer_typeします。Any pointer_type.
  • ユーザー定義struct_type構築された型ではないのフィールドを含むunmanaged_typeにのみです。Any user-defined struct_type that is not a constructed type and contains fields of unmanaged_types only.

直感的なルールを混在ポインターと参照は、ポインターを含む参照 (オブジェクト) の参照が許可されますが、参照を格納するポインターの参照は許可されていませんが。The intuitive rule for mixing of pointers and references is that referents of references (objects) are permitted to contain pointers, but referents of pointers are not permitted to contain references.

ポインター型の例をいくつかは、次の表で与えられます。Some examples of pointer types are given in the table below:

Example 説明Description
byte* ポインター bytePointer to byte
char* ポインター charPointer to char
int** ポインターへのポインター intPointer to pointer to int
int*[] ポインターの 1 次元配列 intSingle-dimensional array of pointers to int
void* 不明な型へのポインターPointer to unknown type

特定の実装では、すべてのポインター型は、同じサイズと形式が必要です。For a given implementation, all pointer types must have the same size and representation.

C および C++、c# では、同じ宣言で複数のポインターが宣言されている場合とは異なり、*は各ポインター名のプレフィックスなどの区切り記号としてではなく基になる型にのみ、と共に書き込まれます。Unlike C and C++, when multiple pointers are declared in the same declaration, in C# the * is written along with the underlying type only, not as a prefix punctuator on each pointer name. 次に例を示します。For example

int* pi, pj;    // NOT as int *pi, *pj;

型のポインターの値T*型の変数のアドレスを表すTします。The value of a pointer having type T* represents the address of a variable of type T. ポインター間接演算子*(ポインターの間接参照) この変数にアクセスするために使用する可能性があります。The pointer indirection operator * (Pointer indirection) may be used to access this variable. たとえば、変数があるP型のint*、式*P表します、int変数に含まれるアドレスにあるPFor example, given a variable P of type int*, the expression *P denotes the int variable found at the address contained in P.

オブジェクト参照のようなポインターである可能性がありますnullします。Like an object reference, a pointer may be null. 間接演算子を適用すること、null実装定義の動作の結果のポインター。Applying the indirection operator to a null pointer results in implementation-defined behavior. 値を持つポインターnullはすべてのビットが 0 で表されます。A pointer with value null is represented by all-bits-zero.

void*型が不明な型へのポインターを表します。The void* type represents a pointer to an unknown type. 型のポインターに間接演算子は適用できません参照先の型が不明なためvoid*もこのようなポインターですべての算術演算を実行することができます。Because the referent type is unknown, the indirection operator cannot be applied to a pointer of type void*, nor can any arithmetic be performed on such a pointer. ただし、型のポインターvoid*他のポインター型 (その逆) にキャストすることができます。However, a pointer of type void* can be cast to any other pointer type (and vice versa).

ポインター型は、別の種類のカテゴリです。Pointer types are a separate category of types. 継承しないポインター型の参照型と値型とは異なりobjectポインター型間で変換が存在しないと、objectします。Unlike reference types and value types, pointer types do not inherit from object and no conversions exist between pointer types and object. 具体的には、ボックス化とボックス化解除 (ボックス化とボックス化解除) ポインターはサポートされていません。In particular, boxing and unboxing (Boxing and unboxing) are not supported for pointers. ただし、変換は、異なるポインター型間で、ポインター型と整数型の間に許可されます。However, conversions are permitted between different pointer types and between pointer types and the integral types. ポインター変換します。This is described in Pointer conversions.

A pointer_type型引数として使用することはできません (構築型)、や型推論 (型推論) であると推論されたジェネリック メソッドの呼び出しが失敗した、引数がポインター型を入力します。A pointer_type cannot be used as a type argument (Constructed types), and type inference (Type inference) fails on generic method calls that would have inferred a type argument to be a pointer type.

A pointer_type volatile フィールドの型として使用することがあります (Volatile フィールド)。A pointer_type may be used as the type of a volatile field (Volatile fields).

としてのポインターを渡すことができますがrefまたはoutパラメーター、呼び出されたメソッドが戻るときに存在しないローカル変数またはその固定オブジェクトを指すポインターがない場合もあるため、未定義の動作が生じることを設定します。ポイントを使用すると、修正が不要になった。Although pointers can be passed as ref or out parameters, doing so can cause undefined behavior, since the pointer may well be set to point to a local variable which no longer exists when the called method returns, or the fixed object to which it used to point, is no longer fixed. 例:For example:

using System;

class Test
{
    static int value = 20;

    unsafe static void F(out int* pi1, ref int* pi2) {
        int i = 10;
        pi1 = &i;

        fixed (int* pj = &value) {
            // ...
            pi2 = pj;
        }
    }

    static void Main() {
        int i = 10;
        unsafe {
            int* px1;
            int* px2 = &i;

            F(out px1, ref px2);

            Console.WriteLine("*px1 = {0}, *px2 = {1}",
                *px1, *px2);    // undefined behavior
        }
    }
}

メソッドは、いくつかの型の値を返すことができ、その型のポインターであることができます。A method can return a value of some type, and that type can be a pointer. たとえば、ポインターの連続したシーケンスに指定されている場合ints、そのシーケンスの要素の数とその他のint値、次のメソッドは、一致が発生した場合、その順序でその値のアドレスを返しますを返しますそれ以外の場合。null:For example, when given a pointer to a contiguous sequence of ints, that sequence's element count, and some other int value, the following method returns the address of that value in that sequence, if a match occurs; otherwise it returns null:

unsafe static int* Find(int* pi, int size, int value) {
    for (int i = 0; i < size; ++i) {
        if (*pi == value) 
            return pi;
        ++pi;
    }
    return null;
}

Unsafe コンテキストでは、いくつかの構成要素はポインターに対する操作のために使用できます。In an unsafe context, several constructs are available for operating on pointers:

固定と移動可能変数Fixed and moveable variables

Address-of 演算子 (address-of 演算子) およびfixedステートメント (fixed ステートメント) 変数を 2 つのカテゴリに分割します。変数を固定移動可能変数します。The address-of operator (The address-of operator) and the fixed statement (The fixed statement) divide variables into two categories: Fixed variables and moveable variables.

固定変数は、ガベージ コレクターの操作によって影響を受けません記憶域の場所に存在します。Fixed variables reside in storage locations that are unaffected by operation of the garbage collector. (固定変数の例は、ローカル変数、パラメーターの値、およびポインターの逆参照によって作成された変数含まれます)。その一方で、移動可能な変数は、再配置やガベージ コレクターによって破棄対象である記憶域の場所に存在します。(Examples of fixed variables include local variables, value parameters, and variables created by dereferencing pointers.) On the other hand, moveable variables reside in storage locations that are subject to relocation or disposal by the garbage collector. (移動可能な変数の例に、オブジェクトと配列の要素にあるフィールドが含める)。(Examples of moveable variables include fields in objects and elements of arrays.)

&演算子 (address-of 演算子) に制限なしに取得する固定の変数のアドレスを許可します。The & operator (The address-of operator) permits the address of a fixed variable to be obtained without restrictions. ただし、移動可能な変数は再配置やガベージ コレクターによって破棄されるため、移動可能な変数のアドレスのみを使って取得できます、fixedステートメント (fixed ステートメント)、およびそのアドレスその期間に対してのみ有効なままfixedステートメント。However, because a moveable variable is subject to relocation or disposal by the garbage collector, the address of a moveable variable can only be obtained using a fixed statement (The fixed statement), and that address remains valid only for the duration of that fixed statement.

正確には、固定の変数は、次のいずれか。In precise terms, a fixed variable is one of the following:

その他のすべての変数は、移動可能な変数として分類されます。All other variables are classified as moveable variables.

静的フィールドが移動可能な変数として分類されることに注意してください。Note that a static field is classified as a moveable variable. また、refまたはoutパラメーターは、パラメーターの指定された引数が固定の変数である場合でも、移動可能変数として分類されます。Also note that a ref or out parameter is classified as a moveable variable, even if the argument given for the parameter is a fixed variable. 最後に、ポインターを逆参照によって生成された変数は常に固定変数として分類することに注意してください。Finally, note that a variable produced by dereferencing a pointer is always classified as a fixed variable.

ポインター変換Pointer conversions

Unsafe コンテキストでは、使用可能な暗黙的な変換のセット (暗黙的な変換) が拡張され、次の暗黙的なポインター変換。In an unsafe context, the set of available implicit conversions (Implicit conversions) is extended to include the following implicit pointer conversions:

  • いずれかからpointer_type型にvoid*します。From any pointer_type to the type void*.
  • nullいずれかにリテラルpointer_typeします。From the null literal to any pointer_type.

さらに、使用できる明示的な変換のセットを unsafe コンテキストで (明示的な変換) が拡張され、次の明示的なポインター変換。Additionally, in an unsafe context, the set of available explicit conversions (Explicit conversions) is extended to include the following explicit pointer conversions:

  • いずれかからpointer_typepointer_typeします。From any pointer_type to any other pointer_type.
  • sbytebyteshortushortintuintlong、またはulongいずれかにpointer_typeします。From sbyte, byte, short, ushort, int, uint, long, or ulong to any pointer_type.
  • いずれかからpointer_typesbytebyteshortushortintuintlong、またはulongします。From any pointer_type to sbyte, byte, short, ushort, int, uint, long, or ulong.

最後に、標準の暗黙的な変換のセットを unsafe コンテキストで (標準の暗黙的な変換) 次のポインター変換が含まれています。Finally, in an unsafe context, the set of standard implicit conversions (Standard implicit conversions) includes the following pointer conversion:

  • いずれかからpointer_type型にvoid*します。From any pointer_type to the type void*.

2 つのポインター型間の変換は、実際のポインター値を変更することはありません。Conversions between two pointer types never change the actual pointer value. つまり、1 つのポインター型から別の変換は、ポインターによって指定された基になるアドレスへの影響を与えません。In other words, a conversion from one pointer type to another has no effect on the underlying address given by the pointer.

Pointed-to 型に対して、結果のポインターが正しくアラインされない場合、1 つのポインター型は、変換するときに、動作がいる場合、結果を逆参照します。When one pointer type is converted to another, if the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined if the result is dereferenced. 一般に、「正しくアライン」という概念は推移的な: 型へのポインターの場合A型へのポインターが正しくアラインBであり、さらの型へのポインターの配置が正しくC、型へのポインター、A型へのポインターが正しくアラインCします。In general, the concept "correctly aligned" is transitive: if a pointer to type A is correctly aligned for a pointer to type B, which, in turn, is correctly aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C.

次のさまざまな型へのポインターを使用して 1 つの型を持つ変数にアクセスする場合を考慮してください。Consider the following case in which a variable having one type is accessed via a pointer to a different type:

char c = 'A';
char* pc = &c;
void* pv = pc;
int* pi = (int*)pv;
int i = *pi;         // undefined
*pi = 123456;        // undefined

ポインター型が変換される場合、バイトへのポインター変数のアドレス指定された最下位バイトを結果のポイント。When a pointer type is converted to a pointer to byte, the result points to the lowest addressed byte of the variable. 一連の変数のサイズまで、結果のインクリメントでは、その変数の残りのバイトへのポインターを生成します。Successive increments of the result, up to the size of the variable, yield pointers to the remaining bytes of that variable. たとえば、次のメソッドは、double 8 バイトの各 16 進数の値として表示します。For example, the following method displays each of the eight bytes in a double as a hexadecimal value:

using System;

class Test
{
    unsafe static void Main() {
      double d = 123.456e23;
        unsafe {
           byte* pb = (byte*)&d;
            for (int i = 0; i < sizeof(double); ++i)
               Console.Write("{0:X2} ", *pb++);
            Console.WriteLine();
        }
    }
}

もちろん、生成される出力はエンディアンに依存します。Of course, the output produced depends on endianness.

ポインターと整数間のマッピングでは、実装定義です。Mappings between pointers and integers are implementation-defined. ただし、32 の * とリニア アドレス空間と 64 ビット CPU アーキテクチャ、整数型のポインターの変換通常動作への変換とまったく同じようにuintまたはulongにそれぞれ、または整数型の値。However, on 32* and 64-bit CPU architectures with a linear address space, conversions of pointers to or from integral types typically behave exactly like conversions of uint or ulong values, respectively, to or from those integral types.

ポインターの配列Pointer arrays

Unsafe コンテキストでは、ポインターの配列を構築することができます。In an unsafe context, arrays of pointers can be constructed. ポインターの配列では、その他の配列型に適用される変換の一部のみ使用できます。Only some of the conversions that apply to other array types are allowed on pointer arrays:

  • 暗黙的な参照変換 (暗黙の参照変換) いずれかからarray_typeSystem.Arrayも実装するインターフェイスは、ポインターの配列に適用されます。The implicit reference conversion (Implicit reference conversions) from any array_type to System.Array and the interfaces it implements also applies to pointer arrays. 配列の要素にアクセスしようとするただし、System.Arrayまたはインターフェイスの実装は、例外が実行時に、ポインター型に変換できないobjectします。However, any attempt to access the array elements through System.Array or the interfaces it implements will result in an exception at run-time, as pointer types are not convertible to object.
  • 暗黙的および明示的な参照変換 (暗黙の参照変換明示的な参照変換) 1 次元配列型からS[]System.Collections.Generic.IList<T>とジェネリックの基本インターフェイスは、型の引数としてポインター型を使用することはできませんし、ポインター型から非ポインター型への変換がないために、ポインターの配列に適用しません。The implicit and explicit reference conversions (Implicit reference conversions, Explicit reference conversions) from a single-dimensional array type S[] to System.Collections.Generic.IList<T> and its generic base interfaces never apply to pointer arrays, since pointer types cannot be used as type arguments, and there are no conversions from pointer types to non-pointer types.
  • 明示的な参照変換 (明示的な参照変換) からSystem.Arrayいずれかに、インターフェイスを実装およびarray_typeポインターの配列に適用されます。The explicit reference conversion (Explicit reference conversions) from System.Array and the interfaces it implements to any array_type applies to pointer arrays.
  • 明示的な参照変換 (明示的な参照変換) からSystem.Collections.Generic.IList<S>1 次元の配列型をその基本インターフェイスとT[]ポインター型にすることはできませんので、ポインターの配列には適用されません使用される引数を入力として、ポインター型から非ポインター型への変換はありません。The explicit reference conversions (Explicit reference conversions) from System.Collections.Generic.IList<S> and its base interfaces to a single-dimensional array type T[] never applies to pointer arrays, since pointer types cannot be used as type arguments, and there are no conversions from pointer types to non-pointer types.

これらの制限を意味するの拡張、foreachで説明されているステートメントで配列に対するforeach ステートメントポインターの配列には適用できません。These restrictions mean that the expansion for the foreach statement over arrays described in The foreach statement cannot be applied to pointer arrays. 代わりに、フォームの foreach ステートメントInstead, a foreach statement of the form

foreach (V v in x) embedded_statement

場所の種類x形式の配列型は、 T[,,...,]Nディメンションから 1 を引いた数とTまたはVポインター型は、次のように for ループで入れ子になったを使用して展開します。where the type of x is an array type of the form T[,,...,], N is the number of dimensions minus 1 and T or V is a pointer type, is expanded using nested for-loops as follows:

{
    T[,,...,] a = x;
    for (int i0 = a.GetLowerBound(0); i0 <= a.GetUpperBound(0); i0++)
    for (int i1 = a.GetLowerBound(1); i1 <= a.GetUpperBound(1); i1++)
    ...
    for (int iN = a.GetLowerBound(N); iN <= a.GetUpperBound(N); iN++) {
        V v = (V)a.GetValue(i0,i1,...,iN);
        embedded_statement
    }
}

変数ai0i1,...,iNを表示またはアクセスではないxまたはembedded_statementまたはプログラムの他のソース コード。The variables a, i0, i1, ..., iN are not visible to or accessible to x or the embedded_statement or any other source code of the program. 変数vは埋め込みステートメントでは読み取り専用です。The variable v is read-only in the embedded statement. 明示的な変換がない場合 (ポインター変換) からT(要素の型) にVあり、エラーが生成されるため、以降の手順は実行されません。If there is not an explicit conversion (Pointer conversions) from T (the element type) to V, an error is produced and no further steps are taken. 場合x、値を持つnullSystem.NullReferenceExceptionが実行時にスローされます。If x has the value null, a System.NullReferenceException is thrown at run-time.

式のポインターPointers in expressions

Unsafe コンテキストでは、式は、ポインター型の結果を生成可能性がありますが、unsafe コンテキストの外側はポインター型の式のコンパイル時エラー。In an unsafe context, an expression may yield a result of a pointer type, but outside an unsafe context it is a compile-time error for an expression to be of a pointer type. 正確には、unsafe コンテキストの外部コンパイル時エラーが発生する場合は、すべてsimple_name (簡易名)、 member_access (メンバーへのアクセス)、 invocation_expression (Invocation 式)、またはelement_access (要素へのアクセス) ポインター型です。In precise terms, outside an unsafe context a compile-time error occurs if any simple_name (Simple names), member_access (Member access), invocation_expression (Invocation expressions), or element_access (Element access) is of a pointer type.

Unsafe コンテキストで、 primary_no_array_creation_expression (一次式) とunary_expression (の単項演算子)運用では、次の追加の構成要素を許可します。In an unsafe context, the primary_no_array_creation_expression (Primary expressions) and unary_expression (Unary operators) productions permit the following additional constructs:

primary_no_array_creation_expression_unsafe
    : pointer_member_access
    | pointer_element_access
    | sizeof_expression
    ;

unary_expression_unsafe
    : pointer_indirection_expression
    | addressof_expression
    ;

これらのコンストラクトは、次のセクションで説明します。These constructs are described in the following sections. 優先順位と安全でない演算子の結合規則を文法で暗黙的に指定するとします。The precedence and associativity of the unsafe operators is implied by the grammar.

ポインターの間接参照Pointer indirection

A pointer_indirection_expressionアスタリスクで構成されます (*) 後に、 unary_expressionします。A pointer_indirection_expression consists of an asterisk (*) followed by a unary_expression.

pointer_indirection_expression
    : '*' unary_expression
    ;

単項*演算子はポインターの間接参照を表し、ポインターが指す変数を取得するために使用します。The unary * operator denotes pointer indirection and is used to obtain the variable to which a pointer points. 評価結果*Pここで、Pポインター型の式は、 T*、型の変数は、Tします。The result of evaluating *P, where P is an expression of a pointer type T*, is a variable of type T. 単項を適用すると、コンパイル時エラー*型の式に演算子void*またはポインター型のない式にします。It is a compile-time error to apply the unary * operator to an expression of type void* or to an expression that isn't of a pointer type.

単項を適用する効果*演算子をnullポインターは、実装定義です。The effect of applying the unary * operator to a null pointer is implementation-defined. 具体的には、この操作でスローされる保証はありません、System.NullReferenceExceptionします。In particular, there is no guarantee that this operation throws a System.NullReferenceException.

無効な値はポインターでは、単項の動作に割り当てられている場合*演算子は定義されていません。If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined. 単項ポインターを逆参照に無効な値の間で*演算子が不適切な配置の種類が指すアドレス (例を参照してくださいポインター変換) と後に変数のアドレス、存続期間が終了します。Among the invalid values for dereferencing a pointer by the unary * operator are an address inappropriately aligned for the type pointed to (see example in Pointer conversions), and the address of a variable after the end of its lifetime.

形式の式を評価することによって確実な代入分析のために、変数が生成される*Pは最初に割り当てられていると見なされます (変数を最初に割り当てられた)。For purposes of definite assignment analysis, a variable produced by evaluating an expression of the form *P is considered initially assigned (Initially assigned variables).

ポインターのメンバー アクセスPointer member access

A pointer_member_accessから成る、 primary_expression、その後に、"->"トークン、その後に、識別子と省略可能なtype_argument_listします。A pointer_member_access consists of a primary_expression, followed by a "->" token, followed by an identifier and an optional type_argument_list.

pointer_member_access
    : primary_expression '->' identifier
    ;

フォームのポインターのメンバー アクセスでP->IP以外のポインター型の式を指定する必要がありますvoid*、およびI型のアクセス可能なメンバーを表す必要がありますPポイント。In a pointer member access of the form P->I, P must be an expression of a pointer type other than void*, and I must denote an accessible member of the type to which P points.

フォームのポインターのメンバー アクセスP->Iとまったく同じ評価(*P).Iします。A pointer member access of the form P->I is evaluated exactly as (*P).I. ポインター間接演算子の説明については (*) を参照してくださいポインターの間接参照します。For a description of the pointer indirection operator (*), see Pointer indirection. メンバー アクセス演算子の説明については (.) を参照してくださいメンバー アクセスFor a description of the member access operator (.), see Member access.

In the example

using System;

struct Point
{
    public int x;
    public int y;

    public override string ToString() {
        return "(" + x + "," + y + ")";
    }
}

class Test
{
    static void Main() {
        Point point;
        unsafe {
            Point* p = &point;
            p->x = 10;
            p->y = 20;
            Console.WriteLine(p->ToString());
        }
    }
}

->フィールドにアクセスし、ポインターを介して構造体のメソッドを呼び出す演算子を使用します。the -> operator is used to access fields and invoke a method of a struct through a pointer. 操作P->Iとまったく同じ(*P).IMainメソッドも同じように記述できます。Because the operation P->I is precisely equivalent to (*P).I, the Main method could equally well have been written:

class Test
{
    static void Main() {
        Point point;
        unsafe {
            Point* p = &point;
            (*p).x = 10;
            (*p).y = 20;
            Console.WriteLine((*p).ToString());
        }
    }
}

ポインターの要素へのアクセスPointer element access

A pointer_element_accessから成る、 primary_no_array_creation_expressionで囲まれた式の後に"[「と」]"。A pointer_element_access consists of a primary_no_array_creation_expression followed by an expression enclosed in "[" and "]".

pointer_element_access
    : primary_no_array_creation_expression '[' expression ']'
    ;

ポインターの要素には、フォームへのアクセスでP[E]P以外のポインター型の式を指定する必要がありますvoid*、およびEに暗黙的に変換できる式を指定する必要がありますintuintlong、またはulongします。In a pointer element access of the form P[E], P must be an expression of a pointer type other than void*, and E must be an expression that can be implicitly converted to int, uint, long, or ulong.

ポインターの要素には、フォームへのアクセスP[E]とまったく同じ評価*(P + E)します。A pointer element access of the form P[E] is evaluated exactly as *(P + E). ポインター間接演算子の説明については (*) を参照してくださいポインターの間接参照します。For a description of the pointer indirection operator (*), see Pointer indirection. ポインターの加算演算子の説明については (+) を参照してくださいポインターの算術演算します。For a description of the pointer addition operator (+), see Pointer arithmetic.

In the example

class Test
{
    static void Main() {
        unsafe {
            char* p = stackalloc char[256];
            for (int i = 0; i < 256; i++) p[i] = (char)i;
        }
    }
}

ポインターの要素へのアクセスが内で文字バッファーを初期化するために使用される、forループします。a pointer element access is used to initialize the character buffer in a for loop. 操作P[E]とまったく同じ*(P + E)例では、同じように記述できます。Because the operation P[E] is precisely equivalent to *(P + E), the example could equally well have been written:

class Test
{
    static void Main() {
        unsafe {
            char* p = stackalloc char[256];
            for (int i = 0; i < 256; i++) *(p + i) = (char)i;
        }
    }
}

ポインターの要素アクセス演算子は範囲外のチェックせずエラーと、動作にアクセスする場合、要素が定義されている範囲外です。The pointer element access operator does not check for out-of-bounds errors and the behavior when accessing an out-of-bounds element is undefined. これは、C および C++ と同じです。This is the same as C and C++.

Address-of 演算子The address-of operator

Addressof_expressionアンパサンドで構成されます (&) 後に、 unary_expressionします。An addressof_expression consists of an ampersand (&) followed by a unary_expression.

addressof_expression
    : '&' unary_expression
    ;

式が指定E一種のT固定変数として分類されます (固定属性と移動可能変数)、コンストラクト&Eによって指定される変数のアドレスを計算E.Given an expression E which is of a type T and is classified as a fixed variable (Fixed and moveable variables), the construct &E computes the address of the variable given by E. 結果の型がT*値として分類されます。The type of the result is T* and is classified as a value. コンパイル時エラーが発生しますE場合、変数として分類されないEは読み取り専用のローカル変数として分類またはE移動可能な変数を表します。A compile-time error occurs if E is not classified as a variable, if E is classified as a read-only local variable, or if E denotes a moveable variable. 最後の場合、fixed ステートメント (fixed ステートメント)、変数のアドレスを取得する前に、変数を一時的に「修正」するために使用できます。In the last case, a fixed statement (The fixed statement) can be used to temporarily "fix" the variable before obtaining its address. 説明したようにメンバー アクセスのインスタンス コンス トラクターまたは構造体を定義するクラスの静的コンス トラクターの外部、readonlyフィールドに、そのフィールドは変数ではなく、値と見なされます。As stated in Member access, outside an instance constructor or static constructor for a struct or class that defines a readonly field, that field is considered a value, not a variable. そのため、そのアドレスを取得することはできません。As such, its address cannot be taken. 同様に、定数のアドレスを取得することはできません。Similarly, the address of a constant cannot be taken.

&演算子が明示的に代入が次の引数を必要としない、&操作、演算子が適用される変数と見なされます、操作が発生する実行パスで明示的に代入します。The & operator does not require its argument to be definitely assigned, but following an & operation, the variable to which the operator is applied is considered definitely assigned in the execution path in which the operation occurs. 変数の適切な初期化を確認するプログラマの責任は実際にこのような状況を実行をお勧めします。It is the responsibility of the programmer to ensure that correct initialization of the variable actually does take place in this situation.

In the example

using System;

class Test
{
    static void Main() {
        int i;
        unsafe {
            int* p = &i;
            *p = 123;
        }
        Console.WriteLine(i);
    }
}

i 明示的に代入と見なされます、&i初期化するために使用される操作pします。i is considered definitely assigned following the &i operation used to initialize p. 代入*p有効な初期化iが、この初期化を含めることは、プログラマの責任と割り当てが削除された場合はコンパイル時エラーが発生しません。The assignment to *p in effect initializes i, but the inclusion of this initialization is the responsibility of the programmer, and no compile-time error would occur if the assignment was removed.

確実な代入ルール、&演算子存在することでローカル変数の冗長な初期化を回避できます。The rules of definite assignment for the & operator exist such that redundant initialization of local variables can be avoided. たとえば、外部 Api の多くはその API によって格納される構造へのポインターです。For example, many external APIs take a pointer to a structure which is filled in by the API. このような Api の呼び出しは通常、構造体のローカル変数のアドレスを渡すし、規則がない構造体変数の冗長な初期化が必要になります。Calls to such APIs typically pass the address of a local struct variable, and without the rule, redundant initialization of the struct variable would be required.

ポインターのインクリメントとデクリメントPointer increment and decrement

Unsafe コンテキストで、++--演算子 (置インクリメント演算子と前置デクリメント演算子前置インクリメントとデクリメント演算子) ポインターに適用できます除くすべての型の変数void*します。In an unsafe context, the ++ and -- operators (Postfix increment and decrement operators and Prefix increment and decrement operators) can be applied to pointer variables of all types except void*. したがって、すべてのポインター型のT*、次の演算子が暗黙的に定義されています。Thus, for every pointer type T*, the following operators are implicitly defined:

T* operator ++(T* x);
T* operator --(T* x);

演算子と同じ結果を生成するx + 1x - 1、それぞれ (ポインターの算術演算)。The operators produce the same results as x + 1 and x - 1, respectively (Pointer arithmetic). つまり、型のポインター変数のT*++演算子を追加sizeof(T)、変数に含まれるアドレスを--演算子が減算sizeof(T)変数に含まれるアドレスから。In other words, for a pointer variable of type T*, the ++ operator adds sizeof(T) to the address contained in the variable, and the -- operator subtracts sizeof(T) from the address contained in the variable.

ポインターのインクリメントまたはデクリメント演算がポインター型のドメインをオーバーフロー、実装定義になりますが、例外は生成されません。If a pointer increment or decrement operation overflows the domain of the pointer type, the result is implementation-defined, but no exceptions are produced.

ポインターの算術演算Pointer arithmetic

Unsafe コンテキストでは、+-演算子 (加算演算子減算演算子) を除くすべてのポインター型の値に適用できるvoid*します。In an unsafe context, the + and - operators (Addition operator and Subtraction operator) can be applied to values of all pointer types except void*. したがって、すべてのポインター型のT*、次の演算子が暗黙的に定義されています。Thus, for every pointer type T*, the following operators are implicitly defined:

T* operator +(T* x, int y);
T* operator +(T* x, uint y);
T* operator +(T* x, long y);
T* operator +(T* x, ulong y);

T* operator +(int x, T* y);
T* operator +(uint x, T* y);
T* operator +(long x, T* y);
T* operator +(ulong x, T* y);

T* operator -(T* x, int y);
T* operator -(T* x, uint y);
T* operator -(T* x, long y);
T* operator -(T* x, ulong y);

long operator -(T* x, T* y);

式が指定Pポインター型のT*と式N型のintuintlong、またはulong、式P + NN + Pコンピューティング、型のポインター値T*を加算した結果をN * sizeof(T)によって指定されたアドレスにPします。Given an expression P of a pointer type T* and an expression N of type int, uint, long, or ulong, the expressions P + N and N + P compute the pointer value of type T* that results from adding N * sizeof(T) to the address given by P. 式では同様に、P - N型のポインター値を計算T*を減算した結果のN * sizeof(T)によって指定されたアドレスからPします。Likewise, the expression P - N computes the pointer value of type T* that results from subtracting N * sizeof(T) from the address given by P.

2 つの式を指定されたPQ、ポインター型のT*、式P - Qで指定されたアドレスの差を計算PQでその違いを除算し、sizeof(T).Given two expressions, P and Q, of a pointer type T*, the expression P - Q computes the difference between the addresses given by P and Q and then divides that difference by sizeof(T). 結果の型は常にlongします。The type of the result is always long. 実際には、P - Qとして計算されます((long)(P) - (long)(Q)) / sizeof(T)します。In effect, P - Q is computed as ((long)(P) - (long)(Q)) / sizeof(T).

例:For example:

using System;

class Test
{
    static void Main() {
        unsafe {
            int* values = stackalloc int[20];
            int* p = &values[1];
            int* q = &values[15];
            Console.WriteLine("p - q = {0}", p - q);
            Console.WriteLine("q - p = {0}", q - p);
        }
    }
}

これには、出力が生成されます。which produces the output:

p - q = -14
q - p = 14

ポインターの算術演算がポインター型のドメインをオーバーフローした場合は、実装で定義された方法で、結果が切り捨てられますが、例外は生成されません。If a pointer arithmetic operation overflows the domain of the pointer type, the result is truncated in an implementation-defined fashion, but no exceptions are produced.

ポインターの比較Pointer comparison

Unsafe コンテキストで、 ==!=<><=、および=>演算子 (関係式と型検査演算子) すべての値に適用できますポインターの型。In an unsafe context, the ==, !=, <, >, <=, and => operators (Relational and type-testing operators) can be applied to values of all pointer types. ポインターの比較演算子は次のとおりです。The pointer comparison operators are:

bool operator ==(void* x, void* y);
bool operator !=(void* x, void* y);
bool operator <(void* x, void* y);
bool operator >(void* x, void* y);
bool operator <=(void* x, void* y);
bool operator >=(void* x, void* y);

ポインター型からの暗黙的な変換が存在するので、void*型、任意のポインター型のオペランドは、これらの演算子を使用して比較できます。Because an implicit conversion exists from any pointer type to the void* type, operands of any pointer type can be compared using these operators. 比較演算子は、符号なし整数として 2 つのオペランドで指定されるアドレスを比較します。The comparison operators compare the addresses given by the two operands as if they were unsigned integers.

Sizeof 演算子The sizeof operator

sizeof演算子は、指定した型の変数によって占有されるバイト数を返します。The sizeof operator returns the number of bytes occupied by a variable of a given type. オペランドとして指定された型sizeof必要があります、 unmanaged_type (ポインター型)。The type specified as an operand to sizeof must be an unmanaged_type (Pointer types).

sizeof_expression
    : 'sizeof' '(' unmanaged_type ')'
    ;

結果、sizeof演算子は、型の値をintします。The result of the sizeof operator is a value of type int. 特定の組み込み型の場合、sizeof演算子は、次の表に示すように、定数値を生成します。For certain predefined types, the sizeof operator yields a constant value as shown in the table below.

Expression 結果Result
sizeof(sbyte) 1
sizeof(byte) 1
sizeof(short) 2
sizeof(ushort) 2
sizeof(int) 4
sizeof(uint) 4
sizeof(long) 8
sizeof(ulong) 8
sizeof(char) 2
sizeof(float) 4
sizeof(double) 8
sizeof(bool) 1

その他の種類の結果をsizeof演算子の実装で定義および定数ではなく、値として分類されます。For all other types, the result of the sizeof operator is implementation-defined and is classified as a value, not a constant.

メンバーが構造体にパックされている順序では、指定されていません。The order in which members are packed into a struct is unspecified.

配置のためにある可能性がありますしない埋め込み構造体、内の構造体の先頭に、構造体の最後に名前付き。For alignment purposes, there may be unnamed padding at the beginning of a struct, within a struct, and at the end of the struct. 埋め込み文字として使用されるビットの内容は不確定です。The contents of the bits used as padding are indeterminate.

構造体型のオペランドに適用する場合の埋め込みを含む、その型の変数のバイト数の合計数になります。When applied to an operand that has struct type, the result is the total number of bytes in a variable of that type, including any padding.

Fixed ステートメントThe fixed statement

Unsafe コンテキストで、 embedded_statement (ステートメント) 運用により、追加の構築、fixedステートメントでは、移動可能な変数を「修正」するために使用するよう、ステートメントの実行中にアドレスが定数のままになります。In an unsafe context, the embedded_statement (Statements) production permits an additional construct, the fixed statement, which is used to "fix" a moveable variable such that its address remains constant for the duration of the statement.

fixed_statement
    : 'fixed' '(' pointer_type fixed_pointer_declarators ')' embedded_statement
    ;

fixed_pointer_declarators
    : fixed_pointer_declarator (','  fixed_pointer_declarator)*
    ;

fixed_pointer_declarator
    : identifier '=' fixed_pointer_initializer
    ;

fixed_pointer_initializer
    : '&' variable_reference
    | expression
    ;

fixed_pointer_declaratorのローカル変数を宣言して、指定されたpointer_type 、対応することによって計算されたアドレスでそのローカル変数を初期化fixed_pointer_initializerします。Each fixed_pointer_declarator declares a local variable of the given pointer_type and initializes that local variable with the address computed by the corresponding fixed_pointer_initializer. 宣言されたローカル変数、fixedステートメントがいずれかでアクセス可能なfixed_pointer_initializer秒と、その変数の宣言の右側に発生している、 embedded_statementfixedステートメント。A local variable declared in a fixed statement is accessible in any fixed_pointer_initializers occurring to the right of that variable's declaration, and in the embedded_statement of the fixed statement. 宣言されたローカル変数、fixedステートメントは読み取り専用と見なされます。A local variable declared by a fixed statement is considered read-only. 埋め込みステートメントをこのローカル変数を変更しようとすると、コンパイル時エラーが発生します (割り当てまたは++--演算子) として渡すか、refまたはoutパラメーター。A compile-time error occurs if the embedded statement attempts to modify this local variable (via assignment or the ++ and -- operators) or pass it as a ref or out parameter.

A fixed_pointer_initializer次のいずれかを指定できます。A fixed_pointer_initializer can be one of the following:

  • トークン"&"続けて、 variable_reference (確実な代入を決定するための正確な規則) 移動可能な変数に (固定属性と移動可能変数)アンマネージ型のT、型指定されたT*指定されたポインター型に暗黙的に変換されて、fixedステートメント。The token "&" followed by a variable_reference (Precise rules for determining definite assignment) to a moveable variable (Fixed and moveable variables) of an unmanaged type T, provided the type T* is implicitly convertible to the pointer type given in the fixed statement. この場合、初期化子は、指定された変数のアドレスを計算し、変数の間の固定アドレスで維持することが保証されます、fixedステートメント。In this case, the initializer computes the address of the given variable, and the variable is guaranteed to remain at a fixed address for the duration of the fixed statement.
  • 式、 array_typeアンマネージ型の要素を含むT、型指定されたT*指定されたポインター型に暗黙的に変換できる、fixedステートメント。An expression of an array_type with elements of an unmanaged type T, provided the type T* is implicitly convertible to the pointer type given in the fixed statement. この場合、初期化子は、配列の最初の要素のアドレスを計算し、配列全体の期間の固定アドレスで維持することが保証されます、fixedステートメント。In this case, the initializer computes the address of the first element in the array, and the entire array is guaranteed to remain at a fixed address for the duration of the fixed statement. 配列式が null の場合、または配列の要素が 0 の場合は、初期化子は、アドレスに値が 0 を計算します。If the array expression is null or if the array has zero elements, the initializer computes an address equal to zero.
  • 型の式string、型指定されたchar*指定されたポインター型に暗黙的に変換されて、fixedステートメント。An expression of type string, provided the type char* is implicitly convertible to the pointer type given in the fixed statement. この場合、初期化子は、文字列の最初の文字のアドレスを計算し、文字列全体の期間の固定アドレスで維持することが保証されます、fixedステートメント。In this case, the initializer computes the address of the first character in the string, and the entire string is guaranteed to remain at a fixed address for the duration of the fixed statement. 動作、fixedステートメントは、実装で定義された文字列式が null の場合。The behavior of the fixed statement is implementation-defined if the string expression is null.
  • A simple_nameまたはmember_access指定された固定サイズ バッファーのメンバーの型が指定されたポインター型に暗黙的に変換、移動可能な変数の固定サイズ バッファーのメンバーを参照します。fixedステートメント。A simple_name or member_access that references a fixed size buffer member of a moveable variable, provided the type of the fixed size buffer member is implicitly convertible to the pointer type given in the fixed statement. この場合、初期化子が固定サイズ バッファーの最初の要素へのポインターを計算 (式で固定サイズ バッファー)、固定サイズ バッファー、の間の固定アドレスで維持することが保証されますfixedステートメント。In this case, the initializer computes a pointer to the first element of the fixed size buffer (Fixed size buffers in expressions), and the fixed size buffer is guaranteed to remain at a fixed address for the duration of the fixed statement.

によって計算された各アドレスに対して、 fixed_pointer_initializerfixedステートメントにより再配置やの間、ガベージ コレクターによって破棄される、アドレスによって参照される変数がありませんが、fixedステートメント。For each address computed by a fixed_pointer_initializer the fixed statement ensures that the variable referenced by the address is not subject to relocation or disposal by the garbage collector for the duration of the fixed statement. によってアドレスが計算された場合など、 fixed_pointer_initializerオブジェクトのフィールドまたは配列インスタンスの要素を参照して、fixedステートメントでは、オブジェクト インスタンスが再配置されないことが保証されますかステートメントの有効期間中に破棄します。For example, if the address computed by a fixed_pointer_initializer references a field of an object or an element of an array instance, the fixed statement guarantees that the containing object instance is not relocated or disposed of during the lifetime of the statement.

ポインターがによって作成されたことを確認するプログラマの役目ですfixedステートメントは、これらのステートメントの実行終了後は保持されません。It is the programmer's responsibility to ensure that pointers created by fixed statements do not survive beyond execution of those statements. ポインターが作成した場合など、fixedステートメントは、外部 Api に渡されるはプログラマの Api がこれらのポインターのメモリを保持しないようにする必要があります。For example, when pointers created by fixed statements are passed to external APIs, it is the programmer's responsibility to ensure that the APIs retain no memory of these pointers.

(ため、それらを移動することはできません) 固定オブジェクト ヒープの断片化が発生する可能性があります。Fixed objects may cause fragmentation of the heap (because they can't be moved). そのため、絶対に必要な場合にのみオブジェクトは固定する必要があり、復元可能なの最短時間に対してのみです。For that reason, objects should be fixed only when absolutely necessary and then only for the shortest amount of time possible.

例では、The example

class Test
{
    static int x;
    int y;

    unsafe static void F(int* p) {
        *p = 1;
    }

    static void Main() {
        Test t = new Test();
        int[] a = new int[10];
        unsafe {
            fixed (int* p = &x) F(p);
            fixed (int* p = &t.y) F(p);
            fixed (int* p = &a[0]) F(p);
            fixed (int* p = a) F(p);
        }
    }
}

いくつかの用途を示します、fixedステートメント。demonstrates several uses of the fixed statement. 最初のステートメントを修正し、静的フィールドのアドレスを取得、2 番目のステートメントを修正インスタンス フィールドのアドレスを取得と 3 番目のステートメントを修正し、配列の要素のアドレスを取得します。The first statement fixes and obtains the address of a static field, the second statement fixes and obtains the address of an instance field, and the third statement fixes and obtains the address of an array element. 各ケースにされていると、正規表現を使用するとエラー&演算子ので、変数がすべて移動可能な変数として分類されます。In each case it would have been an error to use the regular & operator since the variables are all classified as moveable variables.

4 番目fixedステートメント上の例で、3 番目に同様の結果を生成します。The fourth fixed statement in the example above produces a similar result to the third.

この例のfixedステートメント使用string:This example of the fixed statement uses string:

class Test
{
    static string name = "xx";

    unsafe static void F(char* p) {
        for (int i = 0; p[i] != '\0'; ++i)
            Console.WriteLine(p[i]);
    }

    static void Main() {
        unsafe {
            fixed (char* p = name) F(p);
            fixed (char* p = "xx") F(p);
        }
    }
}

インデックスから始まって増加のインデックス順で unsafe コンテキストでは 1 次元配列の配列要素が格納されている0インデックスで終わるLength - 1します。In an unsafe context array elements of single-dimensional arrays are stored in increasing index order, starting with index 0 and ending with index Length - 1. 多次元配列、右端の次元のインデックスを最初に、増加するように要素が格納された配列の次次ディメンション、という具合に左、左。For multi-dimensional arrays, array elements are stored such that the indices of the rightmost dimension are increased first, then the next left dimension, and so on to the left. 内で、fixedポインターを取得するステートメントp配列インスタンスへa、ポインター値からpp + a.Length - 1配列内の要素のアドレスを表します。Within a fixed statement that obtains a pointer p to an array instance a, the pointer values ranging from p to p + a.Length - 1 represent addresses of the elements in the array. 同様に、範囲変数p[0]p[a.Length - 1]実際の配列要素を表します。Likewise, the variables ranging from p[0] to p[a.Length - 1] represent the actual array elements. 配列が格納されている方法を指定するには、扱うことができます任意の次元の配列線形ものとして。Given the way in which arrays are stored, we can treat an array of any dimension as though it were linear.

例:For example:

using System;

class Test
{
    static void Main() {
        int[,,] a = new int[2,3,4];
        unsafe {
            fixed (int* p = a) {
                for (int i = 0; i < a.Length; ++i)    // treat as linear
                    p[i] = i;
            }
        }

        for (int i = 0; i < 2; ++i)
            for (int j = 0; j < 3; ++j) {
                for (int k = 0; k < 4; ++k)
                    Console.Write("[{0},{1},{2}] = {3,2} ", i, j, k, a[i,j,k]);
                Console.WriteLine();
            }
    }
}

これには、出力が生成されます。which produces the output:

[0,0,0] =  0 [0,0,1] =  1 [0,0,2] =  2 [0,0,3] =  3
[0,1,0] =  4 [0,1,1] =  5 [0,1,2] =  6 [0,1,3] =  7
[0,2,0] =  8 [0,2,1] =  9 [0,2,2] = 10 [0,2,3] = 11
[1,0,0] = 12 [1,0,1] = 13 [1,0,2] = 14 [1,0,3] = 15
[1,1,0] = 16 [1,1,1] = 17 [1,1,2] = 18 [1,1,3] = 19
[1,2,0] = 20 [1,2,1] = 21 [1,2,2] = 22 [1,2,3] = 23

In the example

class Test
{
    unsafe static void Fill(int* p, int count, int value) {
        for (; count != 0; count--) *p++ = value;
    }

    static void Main() {
        int[] a = new int[100];
        unsafe {
            fixed (int* p = a) Fill(p, 100, -1);
        }
    }
}

fixedステートメントを使用して配列を修正するため、そのアドレスは、ポインターを受け取るメソッドに渡すことができます。a fixed statement is used to fix an array so its address can be passed to a method that takes a pointer.

次に例を示します。In the example:

unsafe struct Font
{
    public int size;
    public fixed char name[32];
}

class Test
{
    unsafe static void PutString(string s, char* buffer, int bufSize) {
        int len = s.Length;
        if (len > bufSize) len = bufSize;
        for (int i = 0; i < len; i++) buffer[i] = s[i];
        for (int i = len; i < bufSize; i++) buffer[i] = (char)0;
    }

    Font f;

    unsafe static void Main()
    {
        Test test = new Test();
        test.f.size = 10;
        fixed (char* p = test.f.name) {
            PutString("Times New Roman", p, 32);
        }
    }
}

fixed ステートメントは、そのアドレスをポインターとして使用できるように、構造体の固定サイズ バッファーの修正に使用されます。a fixed statement is used to fix a fixed size buffer of a struct so its address can be used as a pointer.

Achar*修正は文字列のインスタンスは常に null で終わる文字列へのポインターによって生成された値。A char* value produced by fixing a string instance always points to a null-terminated string. ポインターを取得する固定ステートメント内でp文字列インスタンスをs、ポインター値からpp + s.Length - 1、文字列、およびポインターの値の文字のアドレスを表すp + s.Length常に null 文字を指します (値を持つ文字'\0')。Within a fixed statement that obtains a pointer p to a string instance s, the pointer values ranging from p to p + s.Length - 1 represent addresses of the characters in the string, and the pointer value p + s.Length always points to a null character (the character with value '\0').

固定されたポインターを通じてマネージ型のオブジェクトを変更するには、未定義の動作の結果ことができます。Modifying objects of managed type through fixed pointers can results in undefined behavior. たとえば、文字列は変更できないため、固定文字列を指すポインターにより参照されている文字が変更されていないことを確認するはプログラマの役目は。For example, because strings are immutable, it is the programmer's responsibility to ensure that the characters referenced by a pointer to a fixed string are not modified.

文字列の null の自動終了は、"C"スタイルの文字列を期待する外部 Api を呼び出すときに便利です。The automatic null-termination of strings is particularly convenient when calling external APIs that expect "C-style" strings. ただし、文字列のインスタンスが null 文字を許可されたことに注意してください。Note, however, that a string instance is permitted to contain null characters. Null で終わるとして扱われた場合に、このような null 文字が存在する場合、文字列は切り詰められて表示されますchar*します。If such null characters are present, the string will appear truncated when treated as a null-terminated char*.

固定サイズ バッファーFixed size buffers

固定サイズ バッファーを使用して、構造体のメンバーとして「C スタイル」行で配列を宣言しは主にアンマネージ Api とのやり取りに使用します。Fixed size buffers are used to declare "C style" in-line arrays as members of structs, and are primarily useful for interfacing with unmanaged APIs.

固定サイズ バッファーの宣言Fixed size buffer declarations

A固定サイズ バッファー記憶域の指定された型の変数の固定長バッファーを表すメンバーします。A fixed size buffer is a member that represents storage for a fixed length buffer of variables of a given type. 固定サイズ バッファーの宣言では、指定された要素型の 1 つまたは複数の固定サイズ バッファーについて説明します。A fixed size buffer declaration introduces one or more fixed size buffers of a given element type. 固定サイズ バッファーは構造体の宣言でのみ許可されますが、unsafe コンテキストでのみ発生することができます (Unsafe コンテキスト)。Fixed size buffers are only permitted in struct declarations and can only occur in unsafe contexts (Unsafe contexts).

struct_member_declaration_unsafe
    : fixed_size_buffer_declaration
    ;

fixed_size_buffer_declaration
    : attributes? fixed_size_buffer_modifier* 'fixed' buffer_element_type fixed_size_buffer_declarator+ ';'
    ;

fixed_size_buffer_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | 'unsafe'
    ;

buffer_element_type
    : type
    ;

fixed_size_buffer_declarator
    : identifier '[' constant_expression ']'
    ;

固定サイズ バッファーの宣言は、一連の属性を含めることができます (属性)、new修飾子 (修飾子)、有効な 4 つのアクセス修飾子の組み合わせ (型パラメーターや制約) およびunsafe修飾子 (Unsafe コンテキスト)。A fixed size buffer declaration may include a set of attributes (Attributes), a new modifier (Modifiers), a valid combination of the four access modifiers (Type parameters and constraints) and an unsafe modifier (Unsafe contexts). 属性と修飾子は、すべての固定サイズ バッファーの宣言で宣言されたメンバーに適用されます。The attributes and modifiers apply to all of the members declared by the fixed size buffer declaration. 同じ修飾子を固定サイズ バッファーの宣言内で複数回のエラーになります。It is an error for the same modifier to appear multiple times in a fixed size buffer declaration.

固定サイズ バッファーの宣言を含めることはできません、static修飾子。A fixed size buffer declaration is not permitted to include the static modifier.

バッファーの固定サイズ バッファーの宣言の要素型では、宣言によって導入された、バッファーの要素の型を指定します。The buffer element type of a fixed size buffer declaration specifies the element type of the buffer(s) introduced by the declaration. バッファー要素の型は、定義済みの型のいずれかを指定する必要がありますsbytebyteshortushortintuintlongulongcharfloatdouble、またはboolします。The buffer element type must be one of the predefined types sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or bool.

バッファー要素の型には、新しいメンバーを追加の固定サイズ バッファーの宣言子のリストが続きます。The buffer element type is followed by a list of fixed size buffer declarators, each of which introduces a new member. 固定サイズ バッファーの宣言子から成る識別子で囲まれた定数式の後に、メンバーの名前を示す[]トークンです。A fixed size buffer declarator consists of an identifier that names the member, followed by a constant expression enclosed in [ and ] tokens. 定数式は、その固定サイズ バッファーの宣言子によって導入されるメンバー内の要素の数を表しています。The constant expression denotes the number of elements in the member introduced by that fixed size buffer declarator. 定数式の型は、型に暗黙的に変換可能である必要がありますint値は 0 以外の正の整数である必要があります。The type of the constant expression must be implicitly convertible to type int, and the value must be a non-zero positive integer.

固定サイズ バッファーの要素にメモリ内で順番にレイアウトすることが保証されます。The elements of a fixed size buffer are guaranteed to be laid out sequentially in memory.

複数の固定サイズ バッファーを宣言する固定サイズ バッファーの宣言は、同じ属性および要素型を持つ 1 つの固定サイズ バッファーの宣言の複数の宣言と同じです。A fixed size buffer declaration that declares multiple fixed size buffers is equivalent to multiple declarations of a single fixed size buffer declaration with the same attributes, and element types. 次に例を示します。For example

unsafe struct A
{
   public fixed int x[5], y[10], z[100];
}

上記の式は、次の式と同じです。is equivalent to

unsafe struct A
{
   public fixed int x[5];
   public fixed int y[10];
   public fixed int z[100];
}

式で固定サイズ バッファーFixed size buffers in expressions

メンバー ルックアップ (演算子) 固定サイズのバッファーのメンバーがフィールドのメンバーの検索とまったく同じように進みます。Member lookup (Operators) of a fixed size buffer member proceeds exactly like member lookup of a field.

固定サイズ バッファーを使用する式で参照できます、 simple_name (型推論) またはmember_access (コンパイル時のチェック動的なオーバー ロードの解決)。A fixed size buffer can be referenced in an expression using a simple_name (Type inference) or a member_access (Compile-time checking of dynamic overload resolution).

形式のメンバー アクセスと同じ効果は、固定サイズ バッファーのメンバーは、簡易名として参照された場合、this.Iここで、Iは固定サイズ バッファーのメンバーです。When a fixed size buffer member is referenced as a simple name, the effect is the same as a member access of the form this.I, where I is the fixed size buffer member.

形式のメンバー アクセスでE.I場合は、Eは構造体の型とメンバーを参照I構造体の型が固定サイズ メンバーを識別にE.Iは分類および次のように評価されます。In a member access of the form E.I, if E is of a struct type and a member lookup of I in that struct type identifies a fixed size member, then E.I is evaluated an classified as follows:

  • 場合、式E.Iは発生しません、unsafe コンテキストでは、コンパイル時エラーが発生します。If the expression E.I does not occur in an unsafe context, a compile-time error occurs.
  • 場合Eコンパイル時エラーが発生した、値として分類されます。If E is classified as a value, a compile-time error occurs.
  • の場合E移動可能な変数は、(固定属性と移動可能変数) と式E.Iでない、 fixed_pointer_initializer (固定ステートメント)、コンパイル時エラーが発生します。Otherwise, if E is a moveable variable (Fixed and moveable variables) and the expression E.I is not a fixed_pointer_initializer (The fixed statement), a compile-time error occurs.
  • それ以外の場合、E固定変数参照式の結果は固定サイズ バッファーのメンバーの最初の要素へのポインターとIEします。Otherwise, E references a fixed variable and the result of the expression is a pointer to the first element of the fixed size buffer member I in E. 結果は型S*ここで、Sの要素の型は、 I、値として分類されます。The result is of type S*, where S is the element type of I, and is classified as a value.

固定サイズ バッファーの後続の要素は、最初の要素からポインター操作を使用してアクセスできます。The subsequent elements of the fixed size buffer can be accessed using pointer operations from the first element. 、配列へのアクセスとは異なり、固定サイズ バッファーの要素へのアクセスは、安全でない操作は、し、範囲のチェックではありません。Unlike access to arrays, access to the elements of a fixed size buffer is an unsafe operation and is not range checked.

次の例では、宣言し、固定サイズ バッファーのメンバーを持つ構造体を使用します。The following example declares and uses a struct with a fixed size buffer member.

unsafe struct Font
{
    public int size;
    public fixed char name[32];
}

class Test
{
    unsafe static void PutString(string s, char* buffer, int bufSize) {
        int len = s.Length;
        if (len > bufSize) len = bufSize;
        for (int i = 0; i < len; i++) buffer[i] = s[i];
        for (int i = len; i < bufSize; i++) buffer[i] = (char)0;
    }

    unsafe static void Main()
    {
        Font f;
        f.size = 10;
        PutString("Times New Roman", f.name, 32);
    }
}

確実な代入をチェックDefinite assignment checking

固定サイズ バッファーは確実な代入をチェック対象になりません (確実な代入) と確実な代入の構造体の型の変数のチェックの目的での固定サイズ バッファーのメンバーは無視されます。Fixed size buffers are not subject to definite assignment checking (Definite assignment), and fixed size buffer members are ignored for purposes of definite assignment checking of struct type variables.

固定サイズ バッファーのメンバーの最も外側にある含む構造体変数は、静的変数、クラスのインスタンス、または配列要素のインスタンス変数が、固定サイズ バッファーの要素は、既定値に自動的に初期化 (既定値)。When the outermost containing struct variable of a fixed size buffer member is a static variable, an instance variable of a class instance, or an array element, the elements of the fixed size buffer are automatically initialized to their default values (Default values). その他のすべての場合は、固定サイズ バッファーの初期コンテンツは定義されません。In all other cases, the initial content of a fixed size buffer is undefined.

スタック割り当てStack allocation

Unsafe コンテキストでは、ローカル変数宣言 (ローカル変数宣言) が、呼び出し履歴からメモリを割り当てられます。 スタック割り当ての初期化子を含めることができます。In an unsafe context, a local variable declaration (Local variable declarations) may include a stack allocation initializer which allocates memory from the call stack.

local_variable_initializer_unsafe
    : stackalloc_initializer
    ;

stackalloc_initializer
    : 'stackalloc' unmanaged_type '[' expression ']'
    ;

Unmanaged_type新しく割り当てられた場所に格納される項目の種類を示す、これらの項目の数を示します。The unmanaged_type 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. として項目の数を指定すると、コンパイル時エラーがスタック割り当てのサイズは、負の値にすることはできません、ため、 constant_expression負の値に評価されます。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.

フォームのスタック割り当ての初期化子stackalloc T[E]必要がありますTアンマネージ型にする (ポインター型) とE型の式を指定するintします。A stack allocation initializer of the form stackalloc T[E] requires T to be an unmanaged type (Pointer types) and E to be an expression of type int. 構造E * sizeof(T)呼び出しからのバイトがスタックが作成され、型のポインターを返しますT*、新しく割り当てられたブロックにします。The construct allocates E * sizeof(T) bytes from the call stack and returns a pointer, of type T*, to the newly allocated block. 場合E負の値が、動作は未定義です。If E is a negative value, then the behavior is undefined. 場合E0、し、割り当てが行われていないと返されるポインターは、実装定義です。If E is zero, then no allocation is made, and the pointer returned is implementation-defined. 特定のサイズのブロックを割り当てることができる十分なメモリがない場合、System.StackOverflowExceptionがスローされます。If there is not enough memory available to allocate a block of the given size, a System.StackOverflowException is thrown.

新しく割り当てられたメモリの内容は定義されません。The content of the newly allocated memory is undefined.

スタック割り当ての初期化子で許可されないcatchまたはfinallyブロック (try ステートメント)。Stack allocation initializers are not permitted in catch or finally blocks (The try statement).

使用して割り当てられたメモリを明示的に解放する方法はありませんstackallocします。There is no way to explicitly free memory allocated using stackalloc. その関数のメンバーが返されるときに、関数メンバーの実行中に作成されたすべてのスタックが割り当てられたメモリ ブロックが自動的に破棄されます。All stack allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. これに対応して、alloca関数では、C および C++ の実装では一般的な拡張機能。This corresponds to the alloca function, an extension commonly found in C and C++ implementations.

In the example

using System;

class Test
{
    static string IntToString(int value) {
        int n = value >= 0? value: -value;
        unsafe {
            char* buffer = stackalloc char[16];
            char* p = buffer + 16;
            do {
                *--p = (char)(n % 10 + '0');
                n /= 10;
            } while (n != 0);
            if (value < 0) *--p = '-';
            return new string(p, 0, (int)(buffer + 16 - p));
        }
    }

    static void Main() {
        Console.WriteLine(IntToString(12345));
        Console.WriteLine(IntToString(-999));
    }
}

stackallocで初期化子が使用される、IntToStringスタックに 16 文字のバッファーを割り当てるためのメソッド。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.

動的メモリの割り当てDynamic memory allocation

を除き、stackalloc演算子 (C#) は用意されていません定義済みの構造には、ガベージ収集されたメモリを管理します。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. たとえば、Memory次に示すクラスは可能性があります、基になるオペレーティング システムのヒープ関数を c# からアクセスする方法を示しています。For example, the Memory class below illustrates how the heap functions of an underlying operating system might be accessed from C#:

using System;
using System.Runtime.InteropServices;

public unsafe class Memory
{
    // Handle for the process heap. This handle is used in all calls to the
    // HeapXXX APIs in the methods below.
    static int ph = GetProcessHeap();

    // Private instance constructor to prevent instantiation.
    private Memory() {}

    // Allocates a memory block of the given size. The allocated memory is
    // automatically initialized to zero.
    public static void* Alloc(int size) {
        void* result = HeapAlloc(ph, HEAP_ZERO_MEMORY, size);
        if (result == null) throw new OutOfMemoryException();
        return result;
    }

    // Copies count bytes from src to dst. The source and destination
    // blocks are permitted to overlap.
    public static void Copy(void* src, void* dst, int count) {
        byte* ps = (byte*)src;
        byte* pd = (byte*)dst;
        if (ps > pd) {
            for (; count != 0; count--) *pd++ = *ps++;
        }
        else if (ps < pd) {
            for (ps += count, pd += count; count != 0; count--) *--pd = *--ps;
        }
    }

    // Frees a memory block.
    public static void Free(void* block) {
        if (!HeapFree(ph, 0, block)) throw new InvalidOperationException();
    }

    // Re-allocates a memory block. If the reallocation request is for a
    // larger size, the additional region of memory is automatically
    // initialized to zero.
    public static void* ReAlloc(void* block, int size) {
        void* result = HeapReAlloc(ph, HEAP_ZERO_MEMORY, block, size);
        if (result == null) throw new OutOfMemoryException();
        return result;
    }

    // Returns the size of a memory block.
    public static int SizeOf(void* block) {
        int result = HeapSize(ph, 0, block);
        if (result == -1) throw new InvalidOperationException();
        return result;
    }

    // Heap API flags
    const int HEAP_ZERO_MEMORY = 0x00000008;

    // Heap API functions
    [DllImport("kernel32")]
    static extern int GetProcessHeap();

    [DllImport("kernel32")]
    static extern void* HeapAlloc(int hHeap, int flags, int size);

    [DllImport("kernel32")]
    static extern bool HeapFree(int hHeap, int flags, void* block);

    [DllImport("kernel32")]
    static extern void* HeapReAlloc(int hHeap, int flags, void* block, int size);

    [DllImport("kernel32")]
    static extern int HeapSize(int hHeap, int flags, void* block);
}

使用する例をMemoryクラスのとおりです。An example that uses the Memory class is given below:

class Test
{
    static void Main() {
        unsafe {
            byte* buffer = (byte*)Memory.Alloc(256);
            try {
                for (int i = 0; i < 256; i++) buffer[i] = (byte)i;
                byte[] array = new byte[256];
                fixed (byte* p = array) Memory.Copy(buffer, p, 256); 
            }
            finally {
                Memory.Free(buffer);
            }
            for (int i = 0; i < 256; i++) Console.WriteLine(array[i]);
        }
    }
}

例では、割り当ての 256 バイトのメモリをMemory.Alloc0 から 255 までの値でメモリ ブロックを初期化します。The example allocates 256 bytes of memory through Memory.Alloc and initializes the memory block with values increasing from 0 to 255. 次に、256 の要素のバイト配列を使用してMemory.Copyバイト配列にメモリ ブロックの内容をコピーします。It then allocates a 256 element byte array and uses Memory.Copy to copy the contents of the memory block into the byte array. メモリ ブロックが解放を使用して最後に、Memory.Freeし、バイト配列の内容は、コンソールに出力します。Finally, the memory block is freed using Memory.Free and the contents of the byte array are output on the console.