Unsicherer CodeUnsafe code

Die in C# den vorstehenden Kapiteln definierte Kernsprache unterscheidet sich insbesondere von C und C++ durch das Weglassen von Zeigern als Datentyp.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. Stattdessen C# bietet Verweise und die Möglichkeit, Objekte zu erstellen, die von einem Garbage Collector verwaltet werden.Instead, C# provides references and the ability to create objects that are managed by a garbage collector. Dieser Entwurf stellt C# in Verbindung mit anderen Features eine viel sicherere Sprache als C oder C++dar.This design, coupled with other features, makes C# a much safer language than C or C++. In der Kern C# Sprache ist es einfach nicht möglich, eine nicht initialisierte Variable, einen "Verb leibend"-Zeiger oder einen Ausdruck zu haben, der ein Array über seine Begrenzungen hinaus indiziert.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. Alle Fehlerkategorien, die routinemäßig C und C++ Programme Plagen, werden daher vermieden.Whole categories of bugs that routinely plague C and C++ programs are thus eliminated.

Obwohl praktisch jedes Zeigertyp Konstrukt in C C++ oder ein Verweistyp in der C#Entsprechung in ist, gibt es jedoch Situationen, in denen der Zugriff auf Zeiger Typen zu einer Notwendigkeit wird.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. Beispielsweise ist die Schnittstellen mit dem zugrunde liegenden Betriebssystem, der Zugriff auf ein Speicher Abbild oder die Implementierung eines zeitkritischen Algorithmus möglicherweise ohne Zugriff auf Zeiger nicht möglich oder praktikabel.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. Um diese Anforderung zu erfüllen C# , bietet die Möglichkeit, unsicheren Codezu schreiben.To address this need, C# provides the ability to write unsafe code.

Im unsicheren Code ist es möglich, Zeiger zu deklarieren und zu verarbeiten, Konvertierungen zwischen Zeigern und ganzzahligen Typen auszuführen, die Adresse von Variablen zu übernehmen usw.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. In gewisser Weise ist das Schreiben von unsicherem Code ähnlich wie das Schreiben C# von C-Code in einem Programm.In a sense, writing unsafe code is much like writing C code within a C# program.

Unsicherer Code ist tatsächlich eine "sichere" Funktion aus der Perspektive von Entwicklern und Benutzern.Unsafe code is in fact a "safe" feature from the perspective of both developers and users. Unsicherer Code muss eindeutig mit dem-Modifizierer unsafe gekennzeichnet werden, sodass Entwickler nicht möglicherweise unsichere Funktionen versehentlich verwenden können, und die Ausführungs-Engine kann sicherstellen, dass unsicherer Code nicht in einer nicht vertrauenswürdigen Umgebung ausgeführt werden kann.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.

Unsichere KontexteUnsafe contexts

Die unsicheren Funktionen von C# sind nur in unsicheren Kontexten verfügbar.The unsafe features of C# are available only in unsafe contexts. Ein unsicherer Kontext wird durch das Einschließen eines unsafe-Modifizierers in die Deklaration eines Typs oder Members oder durch die Verwendung eines unsafe_statement-Elements eingeführt:An unsafe context is introduced by including an unsafe modifier in the declaration of a type or member, or by employing an unsafe_statement:

  • Eine Deklaration einer Klasse, Struktur, Schnittstelle oder eines Delegaten kann einen unsafe-Modifizierer enthalten. in diesem Fall wird der gesamte Text Block dieser Typdeklaration (einschließlich des Texts der Klasse, Struktur oder Schnittstelle) als unsicherer Kontext betrachtet.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.
  • Eine Deklaration eines Felds, einer Methode, einer Eigenschaft, eines Ereignisses, eines Indexers, eines Operators, eines Instanzkonstruktors, eines Dekonstruktors oder eines statischen Konstruktors kann einen unsafe-Modifizierer enthalten. in diesem Fall wird der gesamte Text Block der Element Deklaration als unsicherer Kontext betrachtet.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.
  • Ein unsafe_statement ermöglicht die Verwendung eines unsicheren Kontexts innerhalb eines- Blocks.An unsafe_statement enables the use of an unsafe context within a block. Der gesamte Text Block des zugeordneten Blocks wird als unsicherer Kontext betrachtet.The entire textual extent of the associated block is considered an unsafe context.

Die zugehörigen Grammatik Produktionen sind unten dargestellt.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
    ;

Im BeispielIn the example

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

der in der Struktur Deklaration angegebene unsafe-Modifizierer bewirkt, dass der gesamte Text Block der Struktur Deklaration zu einem unsicheren Kontext wird.the unsafe modifier specified in the struct declaration causes the entire textual extent of the struct declaration to become an unsafe context. Daher ist es möglich, die Felder "Left" und "Right" als Zeigertyp zu deklarieren.Thus, it is possible to declare the Left and Right fields to be of a pointer type. Das obige Beispiel könnte auch geschrieben werden.The example above could also be written

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

Hier bewirken die unsafe-Modifizierer in den Feld Deklarationen, dass diese Deklarationen als unsichere Kontexte angesehen werden.Here, the unsafe modifiers in the field declarations cause those declarations to be considered unsafe contexts.

Abgesehen von der Einrichtung eines unsicheren Kontexts, der die Verwendung von Zeiger Typen ermöglicht, hat der unsafe-Modifizierer keine Auswirkung auf einen Typ oder einen Member.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. Im BeispielIn the example

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

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

der unsafe-Modifizierer für die F-Methode in A bewirkt, dass der Text Block von F zu einem unsicheren Kontext wird, in dem die unsicheren Funktionen der Sprache verwendet werden können.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. Bei der außer Kraft Setzung von F in B ist es nicht notwendig, den unsafe-Modifizierer erneut anzugeben, es sei denn, die F-Methode in B selbst benötigt Zugriff auf unsichere Features.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.

Die Situation ist etwas anders, wenn ein Zeigertyp Teil der Methoden Signatur ist.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) {...}
}

Da die Signatur von F einen Zeigertyp enthält, kann Sie nur in einem unsicheren Kontext geschrieben werden.Here, because F's signature includes a pointer type, it can only be written in an unsafe context. Allerdings kann der unsichere Kontext eingeführt werden, indem entweder die gesamte Klasse unsicher gemacht wird, wie es bei A der Fall ist, oder indem ein unsafe-Modifizierer in die Methoden Deklaration eingeschlossen wird, wie es in B der Fall ist.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.

ZeigertypenPointer types

In einem unsicheren Kontext kann ein Typ (types) ein pointer_type sowie ein value_type oder ein reference_typesein.In an unsafe context, a type (Types) may be a pointer_type as well as a value_type or a reference_type. Ein pointer_type kann jedoch auch in einem typeof-Ausdruck (Anonyme Objekt Erstellungs Ausdrücke) außerhalb eines unsicheren Kontexts verwendet werden, da diese Verwendung nicht unsicher ist.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
    ;

Ein pointer_type -Wert wird als unmanaged_type oder das-Schlüsselwort void, gefolgt von einem *-Token geschrieben: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
    ;

Der Typ, der vor dem * in einem Zeigertyp angegeben wird, wird als Verweistyp des Zeiger Typs bezeichnet.The type specified before the * in a pointer type is called the referent type of the pointer type. Sie stellt den Typ der Variablen dar, auf die ein Wert des Zeiger Typs zeigt.It represents the type of the variable to which a value of the pointer type points.

Anders als Verweise (Werte von Verweis Typen) werden Zeiger nicht vom Garbage Collector nachverfolgt, und der Garbage Collector hat keine Informationen über Zeiger und die Daten, auf die Sie zeigen.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. Aus diesem Grund kann ein Zeiger nicht auf einen Verweis oder eine Struktur verweisen, die Verweise enthält, und der Verweistyp eines Zeigers muss ein unmanaged_typesein.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.

Ein unmanaged_type ist ein beliebiger Typ, der kein reference_type oder konstruierter Typ ist und keine reference_type -oder konstruierten Typfelder auf einer beliebigen Schachtelungs Ebene enthält.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. Mit anderen Worten: ein unmanaged_type -Wert ist einer der folgenden:In other words, an unmanaged_type is one of the following:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, 0, 1 oder 2.sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool.
  • Alle enum_type.Any enum_type.
  • Alle pointer_type.Any pointer_type.
  • Alle benutzerdefinierten struct_type , bei denen es sich nicht um einen konstruierten Typ handelt und die nur die Felder unmanaged_types enthalten.Any user-defined struct_type that is not a constructed type and contains fields of unmanaged_types only.

Die intuitive Regel zum Mischen von Zeigern und verweisen ist, dass Verweise von verweisen (Objekten) Zeiger enthalten dürfen, aber Verweise von Zeigern dürfen keine Verweise enthalten.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.

In der folgenden Tabelle sind einige Beispiele für Zeiger Typen angegeben:Some examples of pointer types are given in the table below:

BeispielExample BeschreibungDescription
byte* Zeiger auf bytePointer to byte
char* Zeiger auf charPointer to char
int** Zeiger auf den Zeiger auf intPointer to pointer to int
int*[] Eindimensionales Array von Zeigern auf intSingle-dimensional array of pointers to int
void* Zeiger auf unbekannten TypPointer to unknown type

Für eine bestimmte Implementierung müssen alle Zeiger Typen die gleiche Größe und Darstellung aufweisen.For a given implementation, all pointer types must have the same size and representation.

Im Gegensatz zu C++C und wird in C# der * zusammen mit dem zugrunde liegenden Typ und nicht als Präfix Satzzeichen für jeden Zeiger Namen geschrieben, wenn mehrere Zeiger in der gleichen Deklaration deklariert werden.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. Beispiel:For example

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

Der Wert eines Zeigers mit dem Typ "T*" stellt die Adresse einer Variablen vom Typ "T" dar.The value of a pointer having type T* represents the address of a variable of type T. Der Zeiger Dereferenzierungsoperator * (Zeigerdereferenzierung) kann verwendet werden, um auf diese Variable zuzugreifen.The pointer indirection operator * (Pointer indirection) may be used to access this variable. Wenn z. b. eine Variable P vom Typ int* ist, kennzeichnet der Ausdruck *P die int-Variable, die sich an der in P enthaltenen Adresse befindet.For example, given a variable P of type int*, the expression *P denotes the int variable found at the address contained in P.

Ein Zeiger kann wie ein Objekt Verweis null sein.Like an object reference, a pointer may be null. Das Anwenden des Dereferenzierungsoperators auf einen null-Zeiger führt zu einem von der Implementierung definierten Verhalten.Applying the indirection operator to a null pointer results in implementation-defined behavior. Ein Zeiger mit dem Wert "null" wird durch "All-Bits-Zero" dargestellt.A pointer with value null is represented by all-bits-zero.

Der void*-Typ stellt einen Zeiger auf einen unbekannten Typ dar.The void* type represents a pointer to an unknown type. Da der Verweistyp unbekannt ist, kann der Dereferenzierungsoperator nicht auf einen Zeiger vom Typ "void*" angewendet werden, und es können keine Arithmetik für einen solchen Zeiger ausgeführt werden.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. Ein Zeiger vom Typ "void*" kann jedoch in einen anderen Zeigertyp (und umgekehrt) umgewandelt werden.However, a pointer of type void* can be cast to any other pointer type (and vice versa).

Zeiger Typen sind eine separate Kategorie von Typen.Pointer types are a separate category of types. Anders als bei Verweis Typen und Werttypen erben Zeiger Typen nicht von object und es sind keine Konvertierungen zwischen Zeiger Typen und object vorhanden.Unlike reference types and value types, pointer types do not inherit from object and no conversions exist between pointer types and object. Vor allem werden Boxing und Unboxing (Boxing und Unboxing) für Zeiger nicht unterstützt.In particular, boxing and unboxing (Boxing and unboxing) are not supported for pointers. Allerdings sind Konvertierungen zwischen verschiedenen Zeiger Typen sowie zwischen Zeiger Typen und ganzzahligen Typen zulässig.However, conversions are permitted between different pointer types and between pointer types and the integral types. Dies wird in Zeiger Konvertierungenbeschrieben.This is described in Pointer conversions.

Ein pointer_type kann nicht als Typargument (konstruierte Typen) verwendet werden, und der Typrückschluss (Typrückschluss) schlägt bei generischen Methoden aufrufen fehl, die ein Typargument als Zeigertyp ableiten müssten.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.

Ein pointer_type kann als Typ eines flüchtigen Felds (flüchtige Felder) verwendet werden.A pointer_type may be used as the type of a volatile field (Volatile fields).

Obwohl Zeiger als ref-oder out-Parameter weitergegeben werden können, kann dies zu undefiniertem Verhalten führen, da der Zeiger möglicherweise so festgelegt wird, dass er auf eine lokale Variable verweist, die nicht mehr vorhanden ist, wenn die aufgerufene Methode zurückgibt, oder das fixierte Objekt, auf das verweist. , ist nicht mehr korrigiert.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. Zum Beispiel: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
        }
    }
}

Eine Methode kann einen Wert eines Typs zurückgeben, und dieser Typ kann ein Zeiger sein.A method can return a value of some type, and that type can be a pointer. Wenn beispielsweise ein Zeiger auf eine zusammenhängende Sequenz von ints, der Element Anzahl der Sequenz und einem anderen int-Wert angegeben wird, gibt die folgende Methode die Adresse dieses Werts in dieser Sequenz zurück, wenn eine Entsprechung auftritt. Andernfalls wird null zurückgegeben: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;
}

In einem unsicheren Kontext sind mehrere-Konstrukte zum Ausführen von Zeigern verfügbar:In an unsafe context, several constructs are available for operating on pointers:

Behobene und verschiebbare VariablenFixed and moveable variables

Der Address-of-Operator (der Address-of-Operator) und die fixed-Anweisung (die fixed-Anweisung) dividieren Variablen in zwei Kategorien: Fixierte Variablen und verschiebbare Variablen.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.

Fixierte Variablen befinden sich in Speicherorten, die von der Garbage Collector nicht betroffen sind.Fixed variables reside in storage locations that are unaffected by operation of the garbage collector. (Beispiele für fixierte Variablen sind lokale Variablen, Wert Parameter und Variablen, die durch dereferenzierende Zeiger erstellt werden.) Auf der anderen Seite befinden sich verschiebbare Variablen in Speicherorten, die von der Garbage Collector verlagert oder verfügbar gemacht werden.(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. (Beispiele für verschiebbare Variablen sind Felder in Objekten und Elementen von Arrays.)(Examples of moveable variables include fields in objects and elements of arrays.)

Der &-Operator (der Address-of-Operator) gestattet, dass die Adresse einer Variablen mit fester Größe ohne Einschränkungen abgerufen wird.The & operator (The address-of operator) permits the address of a fixed variable to be obtained without restrictions. Da eine verschiebbare Variable jedoch vom Garbage Collector verschoben oder aufgehoben werden kann, kann die Adresse einer verschiebbaren Variablen nur mit einer fixed-Anweisung (fixed-Anweisung) abgerufen werden, und diese Adresse bleibt nur für die die Dauer dieser fixed-Anweisung.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.

Eine Variable mit fester Genauigkeit ist eine der folgenden:In precise terms, a fixed variable is one of the following:

  • Eine Variable, die sich aus einem Simple_name (simple names) ergibt, der auf eine lokale Variable oder einen value-Parameter verweist, es sei denn, die Variable wird von einer anonymen Funktion aufgezeichnet.A variable resulting from a simple_name (Simple names) that refers to a local variable or a value parameter, unless the variable is captured by an anonymous function.
  • Eine Variable, die sich aus einem member_access (Member Access) der Form V.I ergibt, wobei "V" eine festgelegte Variable eines struct_typeist.A variable resulting from a member_access (Member access) of the form V.I, where V is a fixed variable of a struct_type.
  • Eine Variable, die sich aus einer pointer_indirection_expression (Zeigerdereferenzierung) der Form *P, eines pointer_member_access (Zeiger Element Zugriffs) der Form P->I oder einer pointer_element_access ( Zeiger Element Zugriff) der Form P[E].A variable resulting from a pointer_indirection_expression (Pointer indirection) of the form *P, a pointer_member_access (Pointer member access) of the form P->I, or a pointer_element_access (Pointer element access) of the form P[E].

Alle anderen Variablen werden als verschiebbare Variablen klassifiziert.All other variables are classified as moveable variables.

Beachten Sie, dass ein statisches Feld als eine verschiebbare Variable klassifiziert ist.Note that a static field is classified as a moveable variable. Beachten Sie außerdem, dass der Parameter "ref" oder "out" als eine verschiebbare Variable klassifiziert ist, auch wenn das für den-Parameter angegebene Argument eine Fixed-Variable ist.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. Beachten Sie schließlich, dass eine Variable, die durch dereferenzieren eines Zeigers erzeugt wird, immer als eine festgelegte Variable klassifiziert wird.Finally, note that a variable produced by dereferencing a pointer is always classified as a fixed variable.

ZeigerkonvertierungenPointer conversions

In einem unsicheren Kontext wird der Satz der verfügbaren impliziten Konvertierungen (implizite Konvertierungen) um die folgenden impliziten Zeiger Konvertierungen erweitert:In an unsafe context, the set of available implicit conversions (Implicit conversions) is extended to include the following implicit pointer conversions:

  • Von allen pointer_type bis zum Typ void*.From any pointer_type to the type void*.
  • Vom null-literalen zu beliebigen pointer_type.From the null literal to any pointer_type.

Außerdem wird in einem unsicheren Kontext der Satz der verfügbaren expliziten Konvertierungen (explizite Konvertierungen) erweitert, um die folgenden expliziten Zeiger Konvertierungen einzubeziehen:Additionally, in an unsafe context, the set of available explicit conversions (Explicit conversions) is extended to include the following explicit pointer conversions:

  • Von allen pointer_type bis hin zu anderen pointer_type.From any pointer_type to any other pointer_type.
  • Von sbyte, byte, short, ushort, int, uint, long oder ulong in beliebige pointer_type.From sbyte, byte, short, ushort, int, uint, long, or ulong to any pointer_type.
  • Von jeder pointer_type zu sbyte, byte, short, ushort, int, uint, long oder ulong.From any pointer_type to sbyte, byte, short, ushort, int, uint, long, or ulong.

Schließlich umfasst der Satz von impliziten Standard Konvertierungen (standardmäßige implizite Konvertierungen) in einem unsicheren Kontext die folgende Zeiger Konvertierung:Finally, in an unsafe context, the set of standard implicit conversions (Standard implicit conversions) includes the following pointer conversion:

  • Von allen pointer_type bis zum Typ void*.From any pointer_type to the type void*.

Konvertierungen zwischen zwei Zeiger Typen ändern niemals den tatsächlichen Zeiger Wert.Conversions between two pointer types never change the actual pointer value. Anders ausgedrückt: eine Konvertierung von einem Zeigertyp in einen anderen hat keine Auswirkungen auf die zugrunde liegende Adresse, die durch den Zeiger angegeben wird.In other words, a conversion from one pointer type to another has no effect on the underlying address given by the pointer.

Wenn ein Zeigertyp in einen anderen konvertiert wird, wenn der resultierende Zeiger nicht ordnungsgemäß für den Verweis auf den Typ ausgerichtet ist, ist das Verhalten nicht definiert, wenn das Ergebnis dereferenziert wird.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. Im Allgemeinen ist das Konzept "ordnungsgemäß ausgerichtet" transitiv: Wenn ein Zeiger auf den Typ "A" ordnungsgemäß für einen Zeiger auf den Typ "B" ausgerichtet ist, der wiederum ordnungsgemäß für einen Zeiger auf den Typ "C" ausgerichtet ist, wird ein Zeiger auf den Typ "A" ordnungsgemäß für einen Zeiger auf den Typ 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.

Beachten Sie den folgenden Fall, in dem auf eine Variable mit einem Typ über einen Zeiger auf einen anderen Typ zugegriffen wird: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

Wenn ein Zeigertyp in einen Zeiger auf ein Byte konvertiert wird, zeigt das Ergebnis auf das niedrigste adressierte Byte der Variablen.When a pointer type is converted to a pointer to byte, the result points to the lowest addressed byte of the variable. Aufeinanderfolgende Inkremente des Ergebnisses bis zur Größe der Variablen ergeben Zeiger auf die restlichen Bytes dieser Variablen.Successive increments of the result, up to the size of the variable, yield pointers to the remaining bytes of that variable. Die folgende Methode zeigt beispielsweise alle acht Bytes in einem Double-Wert als Hexadezimalwert an: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();
        }
    }
}

Die erstellte Ausgabe hängt natürlich von der-Unterscheidung ab.Of course, the output produced depends on endianness.

Zuordnungen zwischen Zeigern und ganzen Zahlen sind Implementierungs definiert.Mappings between pointers and integers are implementation-defined. Allerdings Verhalten sich Konvertierungen von Zeigern auf oder von ganzzahligen Typen auf 32 *-und 64-Bit-CPU-Architekturen mit einem linearen Adressraum in der Regel genauso wie Konvertierungen von uint-oder ulong-Werten in bzw. aus diesen ganzzahligen Typen.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.

Zeiger ArraysPointer arrays

In einem unsicheren Kontext können Arrays von Zeigern erstellt werden.In an unsafe context, arrays of pointers can be constructed. Nur einige der Konvertierungen, die für andere Array Typen gelten, sind in Zeiger Arrays zulässig:Only some of the conversions that apply to other array types are allowed on pointer arrays:

  • Die implizite Verweis Konvertierung (implizite Verweis Konvertierungen) von array_type in System.Array und die implementierten Schnittstellen gelten auch für Zeiger Arrays.The implicit reference conversion (Implicit reference conversions) from any array_type to System.Array and the interfaces it implements also applies to pointer arrays. Allerdings wird bei jedem Versuch, auf die Array Elemente über System.Array oder die von ihm implementierten Schnittstelle zuzugreifen, zur Laufzeit eine Ausnahme ausgelöst, da Zeiger Typen nicht in object konvertierbar sind.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.
  • Die impliziten und expliziten Verweis Konvertierungen (implizite VerweisKonvertierungen, explizite Verweis Konvertierungen) aus einem eindimensionalen Arraytyp S[] in System.Collections.Generic.IList<T> und seine generischen Basis Schnittstellen werden nie auf Zeiger Arrays angewendet. Zeiger Typen können nicht als Typargumente verwendet werden, und es sind keine Konvertierungen von Zeiger Typen zu nicht-Zeiger Typen vorhanden.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.
  • Die explizite Verweis Konvertierung (explizite Verweis Konvertierungen) von System.Array und die Schnittstellen, die Sie für beliebige array_type implementiert, gelten für Zeiger Arrays.The explicit reference conversion (Explicit reference conversions) from System.Array and the interfaces it implements to any array_type applies to pointer arrays.
  • Die expliziten Verweis Konvertierungen (explizite Verweis Konvertierungen) von System.Collections.Generic.IList<S> und deren Basis Schnittstellen in einen eindimensionalen Arraytyp T[] gelten nie für Zeiger Arrays, da Zeiger Typen nicht als Typargumente verwendet werden können und es keine Konvertierungen von Zeiger Typen in nicht-Zeiger Typen.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.

Diese Einschränkungen bedeuten, dass die Erweiterung für die foreach-Anweisung über Arrays, die in der foreach-Anweisung beschrieben werden, nicht auf Zeiger Arrays angewendet werden kann.These restrictions mean that the expansion for the foreach statement over arrays described in The foreach statement cannot be applied to pointer arrays. Stattdessen wird eine foreach-Anweisung der FormInstead, a foreach statement of the form

foreach (V v in x) embedded_statement

Wenn der x-Typ ein Arraytyp der Form ist T[,,...,], N die Anzahl der Dimensionen minus 1 und T oder V ein Zeigertyp ist, wird wie folgt mithilfe von schsted for-Schleifen erweitert: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
    }
}

Die Variablen "a", "i0", "i1", "...", "iN" sind für x oder den embedded_statement oder einen beliebigen anderen Quellcode des Programms nicht sichtbar oder können darauf zugegriffen werden.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. Die Variable v ist in der eingebetteten Anweisung schreibgeschützt.The variable v is read-only in the embedded statement. Wenn keine explizite Konvertierung (Zeiger Konvertierungen) von T (dem Elementtyp) zu V vorhanden ist, wird ein Fehler erzeugt, und es werden keine weiteren Schritte ausgeführt.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. Wenn x den Wert null aufweist, wird zur Laufzeit ein System.NullReferenceException ausgelöst.If x has the value null, a System.NullReferenceException is thrown at run-time.

Zeiger in AusdrückenPointers in expressions

In einem unsicheren Kontext kann ein Ausdruck das Ergebnis eines Zeiger Typs ergeben, aber außerhalb eines unsicheren Kontexts ist es ein Kompilierzeitfehler für einen Ausdruck, der einen Zeigertyp aufweisen soll.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. Genau gesagt: außerhalb eines unsicheren Kontexts tritt ein Kompilierzeitfehler auf, Wenn Simple_name (simple names), member_access (Member Access), invocation_expression (Aufruf Ausdrücke) oder element_access (Element Zugriff) ist ein Zeigertyp.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.

In einem unsicheren Kontext ermöglichen die primary_no_array_creation_expression (Primary Expressions) und unary_expression (unäre Operatoren) die folgenden zusätzlichen Konstrukte: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
    ;

Diese Konstrukte werden in den folgenden Abschnitten beschrieben.These constructs are described in the following sections. Die-Grammatik ist die Rangfolge und Assoziativität der unsicheren Operatoren.The precedence and associativity of the unsafe operators is implied by the grammar.

Zeiger DereferenzierungPointer indirection

Ein pointer_indirection_expression besteht aus einem Sternchen (*), gefolgt von einem unary_expression.A pointer_indirection_expression consists of an asterisk (*) followed by a unary_expression.

pointer_indirection_expression
    : '*' unary_expression
    ;

Der unäre *-Operator bezeichnet die Zeiger Dereferenzierung und wird zum Abrufen der Variablen verwendet, auf die ein Zeiger zeigt.The unary * operator denotes pointer indirection and is used to obtain the variable to which a pointer points. Das Ergebnis der Auswertung von "*P", wobei "P" ein Ausdruck eines Zeiger Typs T* ist, ist eine Variable vom Typ "T".The result of evaluating *P, where P is an expression of a pointer type T*, is a variable of type T. Es handelt sich um einen Kompilierzeitfehler, mit dem der unäre *-Operator auf einen Ausdruck vom Typ void* oder auf einen Ausdruck angewendet wird, der kein Zeigertyp ist.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.

Die Auswirkung der Anwendung des unären *-Operators auf einen null-Zeiger ist Implementierungs definiert.The effect of applying the unary * operator to a null pointer is implementation-defined. Insbesondere gibt es keine Garantie, dass dieser Vorgang eine System.NullReferenceException auslöst.In particular, there is no guarantee that this operation throws a System.NullReferenceException.

Wenn dem Zeiger ein ungültiger Wert zugewiesen wurde, ist das Verhalten des unären *-Operators nicht definiert.If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined. Unter den ungültigen Werten für die Dereferenzierung eines Zeigers durch den unären *-Operator handelt es sich um eine Adresse, die für den Typ, auf den verwiesen wird (siehe Beispiel in Zeiger Konvertierungen), und die Adresse einer Variablen nach dem Ende ihrer Lebensdauer nicht ordnungsgemäß ausgerichtet ist.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.

Zum Zweck der eindeutigen Zuweisungs Analyse wird eine Variable, die durch Auswerten eines Ausdrucks der Form *P erstellt wird, als anfänglich zugewiesen (ursprünglich zugewiesene Variablen).For purposes of definite assignment analysis, a variable produced by evaluating an expression of the form *P is considered initially assigned (Initially assigned variables).

Zugriff auf Zeiger ElementePointer member access

Ein pointer_member_access besteht aus einem primary_expression, gefolgt von einem "->"-Token, gefolgt von einem Bezeichner und einem optionalen 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
    ;

In einem Zeiger Element Zugriff auf das Formular P->I muss P ein Ausdruck eines anderen Zeiger Typs als void* sein, und I muss einen zugänglichen Member des Typs angeben, zu dem P-Punkte gehören.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.

Ein Zeiger Element Zugriff auf das Formular P->I wird genau wie (*P).I ausgewertet.A pointer member access of the form P->I is evaluated exactly as (*P).I. Eine Beschreibung des Zeigerdereferenzierungsoperators (*) finden Sie unter Zeiger Dereferenzierung.For a description of the pointer indirection operator (*), see Pointer indirection. Eine Beschreibung des Mitglieds Zugriffs Operators (.) finden Sie unter Member Access.For a description of the member access operator (.), see Member access.

Im BeispielIn 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());
        }
    }
}

der ->-Operator wird verwendet, um auf Felder zuzugreifen und eine Methode einer Struktur mithilfe eines Zeigers aufzurufen.the -> operator is used to access fields and invoke a method of a struct through a pointer. Da der Vorgang P->I exakt dem (*P).I entspricht, könnte die Main-Methode gleichermaßen gut geschrieben worden sein: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());
        }
    }
}

Zeiger Element ZugriffPointer element access

Ein pointer_element_access besteht aus einem primary_no_array_creation_expression , gefolgt von einem Ausdruck, der in "[" und "]" eingeschlossen ist.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 ']'
    ;

Bei einem Zeiger Element Zugriff auf das Formular P[E] muss P ein Ausdruck eines anderen Zeiger Typs als void* sein, und E muss ein Ausdruck sein, der implizit in int, uint, long oder ulong konvertiert werden kann.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.

Ein Zeiger Element Zugriff auf das Formular P[E] wird genau wie *(P + E) ausgewertet.A pointer element access of the form P[E] is evaluated exactly as *(P + E). Eine Beschreibung des Zeigerdereferenzierungsoperators (*) finden Sie unter Zeiger Dereferenzierung.For a description of the pointer indirection operator (*), see Pointer indirection. Eine Beschreibung des Zeiger Additions Operators (+) finden Sie unter Zeigerarithmetik.For a description of the pointer addition operator (+), see Pointer arithmetic.

Im BeispielIn the example

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

Ein Zeiger Element Zugriff wird verwendet, um den Zeichen Puffer in einer for-Schleife zu initialisieren.a pointer element access is used to initialize the character buffer in a for loop. Da der Vorgang P[E] genau mit *(P + E) übereinstimmt, könnte das Beispiel gleich gut geschrieben worden sein: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;
        }
    }
}

Der Zeiger Element-Zugriffs Operator prüft nicht auf Fehler aufgrund von Fehlern, und das Verhalten beim Zugriff auf ein Out-of-Bounds-Element ist nicht definiert.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. Dies entspricht dem C und C++.This is the same as C and C++.

Der Address-of-OperatorThe address-of operator

Ein addressof_expression besteht aus einem kaufmännischen und-(&), gefolgt von einem unary_expression.An addressof_expression consists of an ampersand (&) followed by a unary_expression.

addressof_expression
    : '&' unary_expression
    ;

Wenn ein Ausdruck E ist, der vom Typ "T" ist und als eine Variable mit fester Größe (Fixed und verschiebbare Variablen) klassifiziert ist, berechnet das Konstrukt "&E" die Adresse der Variablen, die von E angegeben wird.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. Der Ergebnistyp ist T* und wird als Wert klassifiziert.The type of the result is T* and is classified as a value. Ein Kompilierzeitfehler tritt auf, wenn E nicht als Variable klassifiziert ist, wenn E als schreibgeschützte lokale Variable klassifiziert ist, oder wenn E eine verschiebbare Variable bezeichnet.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. Im letzten Fall kann eine fixed-Anweisung (die fixed-Anweisung) verwendet werden, um die Variable temporär zu korrigieren, bevor Sie Ihre Adresse erhält.In the last case, a fixed statement (The fixed statement) can be used to temporarily "fix" the variable before obtaining its address. Wie in Member Accessangegeben, wird das Feld außerhalb eines Instanzkonstruktors oder statischen Konstruktors für eine Struktur oder Klasse, die ein readonly-Feld definiert, als Wert, nicht als Variable angesehen.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. Daher kann die Adresse nicht übernommen werden.As such, its address cannot be taken. Ebenso kann die Adresse einer Konstante nicht übernommen werden.Similarly, the address of a constant cannot be taken.

Der &-Operator erfordert nicht, dass sein Argument definitiv zugewiesen wird, aber nach einem &-Vorgang wird die Variable, auf die der Operator angewendet wird, definitiv in dem Ausführungs Pfad zugewiesen, in dem der Vorgang stattfindet.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. Es liegt in der Verantwortung des Programmierers, sicherzustellen, dass die richtige Initialisierung der Variablen in dieser Situation tatsächlich stattfindet.It is the responsibility of the programmer to ensure that correct initialization of the variable actually does take place in this situation.

Im BeispielIn the example

using System;

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

i wird nach dem &i-Vorgang, der zum Initialisieren von p verwendet wird, definitiv zugewiesen.i is considered definitely assigned following the &i operation used to initialize p. Die Zuweisung zu "*p" initialisiert i, aber die Einbindung dieser Initialisierung liegt in der Verantwortung des Programmierers, und es tritt kein Kompilierzeitfehler auf, wenn die Zuweisung entfernt wurde.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.

Die Regeln der eindeutigen Zuweisung für den &-Operator sind so vorhanden, dass die redundante Initialisierung lokaler Variablen vermieden werden kann.The rules of definite assignment for the & operator exist such that redundant initialization of local variables can be avoided. Viele externe APIs nehmen z. b. einen Zeiger auf eine Struktur, die von der API ausgefüllt wird.For example, many external APIs take a pointer to a structure which is filled in by the API. Aufrufe dieser APIs übergeben in der Regel die Adresse einer lokalen Struktur Variablen, und ohne die Regel wäre eine redundante Initialisierung der Struktur Variablen erforderlich.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.

Inkrementieren und Dekrementieren von ZeigernPointer increment and decrement

In einem unsicheren Kontext können die Operatoren "++" und "--" (postfix-Inkrement-und Dekrementoperatoren und Präfix Inkrement-und Dekrementoperatoren) auf Zeiger Variablen aller Typen außer void* angewendet werden.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*. Daher sind für jeden Zeigertyp T* die folgenden Operatoren implizit definiert:Thus, for every pointer type T*, the following operators are implicitly defined:

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

Die Operatoren erzielen dieselben Ergebnisse wie x + 1 und x - 1 bzw. (Zeigerarithmetik).The operators produce the same results as x + 1 and x - 1, respectively (Pointer arithmetic). Anders ausgedrückt: für eine Zeiger Variable vom Typ T* fügt der ++-Operator der in der Variablen enthaltenen Adresse sizeof(T) hinzu, und der ---Operator subtrahiert sizeof(T) von der Adresse, die in der Variablen enthalten ist.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.

Wenn ein Zeiger Inkrement-oder Dekrement-Vorgang die Domäne des Zeiger Typs überschreitet, wird das Ergebnis durch die Implementierung definiert, es werden jedoch keine Ausnahmen erzeugt.If a pointer increment or decrement operation overflows the domain of the pointer type, the result is implementation-defined, but no exceptions are produced.

ZeigerarithmetikPointer arithmetic

In einem unsicheren Kontext können die Operatoren "+" und "-" (AdditionsOperator und Subtraktions Operator) auf Werte aller Zeiger Typen außer void* angewendet werden.In an unsafe context, the + and - operators (Addition operator and Subtraction operator) can be applied to values of all pointer types except void*. Daher sind für jeden Zeigertyp T* die folgenden Operatoren implizit definiert: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);

Wenn ein Ausdruck P eines Zeiger Typs ist T* und ein Ausdruck N vom Typ int, uint, long oder ulong, berechnen die Ausdrücke P + N und N + P den Zeiger Wert des Typs T*, der sich aus dem Hinzufügen von 0 zur Adresse ergibt. wird von 1 angegeben.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. Entsprechend berechnet der Ausdruck P - N den Zeiger Wert des Typs T*, der sich aus der Subtraktion von N * sizeof(T) von der Adresse ergibt, die von P angegeben wird.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.

Wenn zwei Ausdrücke, P und Q, eines Zeiger Typs T*, berechnet der Ausdruck P - Q den Unterschied zwischen den Adressen, die von P und Q angegeben werden, und dividiert diese Differenz durch 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). Der Ergebnistyp ist immer long.The type of the result is always long. In der Tat wird P - Q als ((long)(P) - (long)(Q)) / sizeof(T) berechnet.In effect, P - Q is computed as ((long)(P) - (long)(Q)) / sizeof(T).

Zum Beispiel: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);
        }
    }
}

der die Ausgabe erzeugt:which produces the output:

p - q = -14
q - p = 14

Wenn eine Zeiger arithmetische Operation die Domäne des Zeiger Typs überschreitet, wird das Ergebnis in einer durch die Implementierung definierten Weise abgeschnitten, es werden jedoch keine Ausnahmen erzeugt.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.

Zeiger VergleichPointer comparison

In einem unsicheren Kontext können die Operatoren "==", "!=", "<", ">", "<=" und "=>" (relationale und Typtest Operatoren) auf Werte aller Zeiger Typen angewendet werden.In an unsafe context, the ==, !=, <, >, <=, and => operators (Relational and type-testing operators) can be applied to values of all pointer types. Die Zeiger Vergleichs Operatoren lauten wie folgt: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);

Da eine implizite Konvertierung von einem Zeigertyp in den void*-Typ vorhanden ist, können die Operanden beliebiger Zeigertyp mithilfe dieser Operatoren verglichen werden.Because an implicit conversion exists from any pointer type to the void* type, operands of any pointer type can be compared using these operators. Die Vergleichs Operatoren vergleichen die Adressen, die von den beiden Operanden angegeben werden, so, als wären Sie ganze Zahlen ohne Vorzeichen.The comparison operators compare the addresses given by the two operands as if they were unsigned integers.

Der sizeof-OperatorThe sizeof operator

Der sizeof-Operator gibt die Anzahl der Bytes zurück, die von einer Variablen eines bestimmten Typs belegt werden.The sizeof operator returns the number of bytes occupied by a variable of a given type. Der als Operand angegebene Typ für sizeof muss ein unmanaged_type (Zeiger Typen) sein.The type specified as an operand to sizeof must be an unmanaged_type (Pointer types).

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

Das Ergebnis des sizeof-Operators ist ein Wert vom Typ int.The result of the sizeof operator is a value of type int. Für bestimmte vordefinierte Typen ergibt der sizeof-Operator einen konstanten Wert, wie in der folgenden Tabelle dargestellt.For certain predefined types, the sizeof operator yields a constant value as shown in the table below.

expressionExpression ErgebnisResult
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

Für alle anderen Typen ist das Ergebnis des sizeof-Operators Implementierungs definiert und wird als Wert, nicht als Konstante klassifiziert.For all other types, the result of the sizeof operator is implementation-defined and is classified as a value, not a constant.

Die Reihenfolge, in der Elemente in einer Struktur verpackt werden, ist nicht angegeben.The order in which members are packed into a struct is unspecified.

Zu Ausrichtungs Zwecken gibt es möglicherweise unbenannte Auffüll Zeichen am Anfang einer Struktur, innerhalb einer Struktur und am Ende der Struktur.For alignment purposes, there may be unnamed padding at the beginning of a struct, within a struct, and at the end of the struct. Der Inhalt der als Auffüllung verwendeten Bits ist unbestimmt.The contents of the bits used as padding are indeterminate.

Wenn ein Operand mit einem Strukturtyp angewendet wird, ist das Ergebnis die Gesamtzahl der Bytes in einer Variablen dieses Typs, einschließlich aller Auffüll Zeichen.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-AnweisungThe fixed statement

In einem unsicheren Kontext gestattet die embedded_statement (Statements) Production ein zusätzliches Konstrukt, die fixed-Anweisung, die verwendet wird, um eine verschiebbare Variable zu "korrigieren", sodass die Adresse für die Dauer der Anweisung konstant bleibt. .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
    ;

Jede fixed_pointer_declarator deklariert eine lokale Variable des angegebenen pointer_type und initialisiert diese lokale Variable mit der Adresse, die von der entsprechenden fixed_pointer_initializerberechnet wird.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. Auf eine lokale Variable, die in einer fixed-Anweisung deklariert ist, kann in allen fixed_pointer_initializers, die rechts neben der Deklaration der Variablen auftreten, und in der embedded_statement -Anweisung der fixed-Anweisung zugegriffen werden.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. Eine lokale Variable, die von einer fixed-Anweisung deklariert wird, wird als schreibgeschützt betrachtet.A local variable declared by a fixed statement is considered read-only. Ein Kompilierzeitfehler tritt auf, wenn die eingebettete Anweisung versucht, diese lokale Variable (überzuweisung oder den ++-und ---Operatoren) zu ändern oder Sie als ref-oder out-Parameter zu übergeben.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.

Ein fixed_pointer_initializer kann eines der folgenden sein:A fixed_pointer_initializer can be one of the following:

  • Das Token "&" gefolgt von einem variable_reference (genaue Regeln zum Bestimmen der eindeutigen Zuweisung) für eine verschiebbare Variable (Feste und verschiebbare Variablen) eines nicht verwalteten Typs T, sofern der Typ T* ist implizit in den Zeigertyp konvertierbar, der in der fixed-Anweisung angegeben ist.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. In diesem Fall berechnet der Initialisierer die Adresse der angegebenen Variablen, und die Variable bleibt für die Dauer der fixed-Anweisung garantiert an einer bestimmten Adresse.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.
  • Ein Ausdruck eines array_type mit Elementen eines nicht verwalteten Typs T, sofern der Typ T* implizit in den Zeigertyp konvertiert werden kann, der in der fixed-Anweisung angegeben ist.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. In diesem Fall berechnet der Initialisierer die Adresse des ersten Elements im Array, und das gesamte Array bleibt für die Dauer der fixed-Anweisung garantiert an einer Fixed-Adresse.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. Wenn der Array Ausdruck NULL ist oder das Array über keine Elemente verfügt, berechnet der Initialisierer eine Adresse, die gleich 0 (null) ist.If the array expression is null or if the array has zero elements, the initializer computes an address equal to zero.
  • Ein Ausdruck vom Typ "string", wenn der Typ "char*" implizit in den Zeigertyp konvertiert werden kann, der in der fixed-Anweisung angegeben ist.An expression of type string, provided the type char* is implicitly convertible to the pointer type given in the fixed statement. In diesem Fall berechnet der Initialisierer die Adresse des ersten Zeichens in der Zeichenfolge, und die gesamte Zeichenfolge bleibt für die Dauer der fixed-Anweisung garantiert an einer Fixed-Adresse.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. Das Verhalten der fixed-Anweisung ist Implementierungs definiert, wenn der Zeichen folgen Ausdruck NULL ist.The behavior of the fixed statement is implementation-defined if the string expression is null.
  • Ein Simple_name -oder member_access -Element, das auf einen Puffer mit fester Größe einer verschiebbaren Variablen verweist, sofern der Typ des Puffer Elements mit fester Größe implizit in den Zeigertyp konvertiert werden kann, der in der fixed-Anweisung angegeben ist.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. In diesem Fall berechnet der Initialisierer einen Zeiger auf das erste Element des Puffers mit fester Größe (Puffer fester Größe in Ausdrücken), und der Puffer mit fester Größe bleibt für die Dauer der fixed-Anweisung garantiert an einer Fixed-Adresse.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.

Für jede Adresse, die von einem fixed_pointer_initializer berechnet wird, stellt die fixed-Anweisung sicher, dass die Variable, auf die die Adresse verweist, nicht für die Dauer der fixed-Anweisung von der Garbage Collector verlagert oder nicht zur Verfügung gestellt wird.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. Wenn die von einem fixed_pointer_initializer berechnete Adresse beispielsweise auf ein Feld eines Objekts oder ein Element einer Array Instanz verweist, stellt die fixed-Anweisung sicher, dass die enthaltende Objektinstanz während des die Lebensdauer der Anweisung.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.

Es ist Aufgabe des Programmierers, sicherzustellen, dass die von fixed-Anweisungen erstellten Zeiger nicht über die Ausführung dieser Anweisungen hinausgehen.It is the programmer's responsibility to ensure that pointers created by fixed statements do not survive beyond execution of those statements. Wenn beispielsweise Zeiger, die von fixed-Anweisungen erstellt werden, an externe APIs übermittelt werden, liegt es in der Verantwortung des Programmierers sicherzustellen, dass die APIs keinen Arbeitsspeicher für diese Zeiger erhalten.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-Objekte können die Fragmentierung des Heaps verursachen (da Sie nicht verschoben werden können).Fixed objects may cause fragmentation of the heap (because they can't be moved). Aus diesem Grund sollten Objekte nur dann korrigiert werden, wenn Sie unbedingt erforderlich sind, und dann nur für die kürzeste benötigte Zeit.For that reason, objects should be fixed only when absolutely necessary and then only for the shortest amount of time possible.

Das BeispielThe 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);
        }
    }
}

veranschaulicht verschiedene Verwendungen der fixed-Anweisung.demonstrates several uses of the fixed statement. Mit der ersten Anweisung wird die Adresse eines statischen Felds korrigiert und abgerufen, mit der zweiten Anweisung wird die Adresse eines Instanzfelds korrigiert und abgerufen, und die dritte Anweisung korrigiert und ruft die Adresse eines Array Elements ab.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 jedem Fall wäre es ein Fehler, den regulären &-Operator zu verwenden, da die Variablen alle als verschiebbare Variablen klassifiziert werden.In each case it would have been an error to use the regular & operator since the variables are all classified as moveable variables.

Die vierte fixed-Anweisung im obigen Beispiel führt zu einem ähnlichen Ergebnis wie das dritte.The fourth fixed statement in the example above produces a similar result to the third.

In diesem Beispiel für die fixed-Anweisung wird string verwendet: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);
        }
    }
}

In einem unsicheren Kontext Array werden Elemente von eindimensionalen Arrays in zunehmender Index Reihenfolge gespeichert, beginnend mit Index 0 und endende mit Index 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. Bei mehrdimensionalen Arrays werden Array Elemente so gespeichert, dass die Indizes der äußersten rechten Dimension zuerst, dann die nächste linke Dimension usw. nach links angehoben werden.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. Innerhalb einer fixed-Anweisung, die einen Zeiger p auf eine Array Instanz a abruft, stellen die Zeiger Werte zwischen p und p + a.Length - 1 Adressen der Elemente im Array dar.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. Ebenso stellen die Variablen, von p[0] bis p[a.Length - 1], die eigentlichen Array Elemente dar.Likewise, the variables ranging from p[0] to p[a.Length - 1] represent the actual array elements. Angesichts der Art und Weise, in der Arrays gespeichert werden, können wir ein Array einer beliebigen Dimension so behandeln, als wäre es linear.Given the way in which arrays are stored, we can treat an array of any dimension as though it were linear.

Zum Beispiel: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();
            }
    }
}

der die Ausgabe erzeugt: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

Im BeispielIn 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);
        }
    }
}

eine fixed-Anweisung wird verwendet, um ein Array zu korrigieren, sodass die Adresse an eine Methode, die einen Zeiger annimmt, übermittelt werden kann.a fixed statement is used to fix an array so its address can be passed to a method that takes a pointer.

Im folgenden BeispielIn 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);
        }
    }
}

eine fixed-Anweisung wird verwendet, um einen Puffer fester Größe einer Struktur zu korrigieren, sodass die Adresse als Zeiger verwendet werden kann.a fixed statement is used to fix a fixed size buffer of a struct so its address can be used as a pointer.

Ein char*-Wert, der durch das Reparieren einer Zeichen folgen Instanz erzeugt wird, verweist immer auf eine mit NULL endenden ZeichenfolgeA char* value produced by fixing a string instance always points to a null-terminated string. Innerhalb einer fixed-Anweisung, die einen Zeiger p auf eine Zeichen folgen Instanz s abruft, stellen die Zeiger Werte von p bis p + s.Length - 1 Adressen der Zeichen in der Zeichenfolge dar, und der Zeiger Wert p + s.Length zeigt immer auf ein NULL-Zeichen (das Zeichen mit dem Wert '\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').

Das Ändern von Objekten des verwalteten Typs durch Fixed-Zeiger kann zu undefiniertem Verhalten führen.Modifying objects of managed type through fixed pointers can results in undefined behavior. Da z. b. Zeichen folgen unveränderlich sind, ist es die Aufgabe des Programmierers sicherzustellen, dass die Zeichen, auf die von einem Zeiger auf eine festgelegte Zeichenfolge verwiesen wird, nicht geändert werden.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.

Die automatische NULL-Beendigung von Zeichen folgen ist besonders praktisch, wenn externe APIs aufgerufen werden, die "C-Style"-Zeichen folgen erwarten.The automatic null-termination of strings is particularly convenient when calling external APIs that expect "C-style" strings. Beachten Sie jedoch, dass eine Zeichen folgen Instanz NULL-Zeichen enthalten darf.Note, however, that a string instance is permitted to contain null characters. Wenn solche NULL-Zeichen vorhanden sind, wird die Zeichenfolge abgeschnitten, wenn Sie als NULL-terminierte char* behandelt wird.If such null characters are present, the string will appear truncated when treated as a null-terminated char*.

Puffer fester GrößeFixed size buffers

Puffer fester Größe werden verwendet, um in-Line-Arrays im C-Format als Member von Strukturen zu deklarieren und sind hauptsächlich für die Schnittstellen mit nicht verwalteten APIs nützlich.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.

Puffer Deklarationen fester GrößeFixed size buffer declarations

Ein Puffer fester Größe ist ein Member, der den Speicher für einen Puffer fester Länge von Variablen eines bestimmten Typs darstellt.A fixed size buffer is a member that represents storage for a fixed length buffer of variables of a given type. Eine Puffer Deklaration mit fester Größe führt einen oder mehrere Puffer fester Größe eines bestimmten Elementtyps ein.A fixed size buffer declaration introduces one or more fixed size buffers of a given element type. Puffer fester Größe sind nur in Struktur Deklarationen zulässig und können nur in unsicheren Kontexten (unsichere Kontexte) vorkommen.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 ']'
    ;

Eine Puffer Deklaration fester Größe kann einen Satz von Attributen (Attribute), einen new-Modifizierer (Modifizierer), eine gültige Kombination der vier Zugriffsmodifizierer (Typparameter und Einschränkungen) und einen unsafe-Modifizierer (unsicher) enthalten. Kontexte).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). Die Attribute und Modifizierer gelten für alle Member, die von der Puffer Deklaration mit fester Größe deklariert werden.The attributes and modifiers apply to all of the members declared by the fixed size buffer declaration. Es ist ein Fehler, dass derselbe Modifizierer mehrmals in einer Puffer Deklaration fester Größe angezeigt wird.It is an error for the same modifier to appear multiple times in a fixed size buffer declaration.

Eine Puffer Deklaration mit fester Größe darf den static-Modifizierer nicht enthalten.A fixed size buffer declaration is not permitted to include the static modifier.

Der Puffer Elementtyp einer Puffer Deklaration mit fester Größe gibt den Elementtyp der Puffer an, die von der Deklaration eingeführt wurden.The buffer element type of a fixed size buffer declaration specifies the element type of the buffer(s) introduced by the declaration. Beim Puffer Elementtyp muss es sich um einen der vordefinierten Typen sbyte, byte, short, ushort, int, uint, long, ulong, char, float, 0 oder 1 handeln.The buffer element type must be one of the predefined types sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or bool.

Auf den Puffer Elementtyp folgt eine Liste von Puffer Deklaratoren fester Größe, von denen jeder einen neuen Member einführt.The buffer element type is followed by a list of fixed size buffer declarators, each of which introduces a new member. Ein Puffer mit fester Größe besteht aus einem Bezeichner, der den Member benennt, gefolgt von einem konstanten Ausdruck, der in [-und ]-Token eingeschlossen ist.A fixed size buffer declarator consists of an identifier that names the member, followed by a constant expression enclosed in [ and ] tokens. Der Konstante Ausdruck gibt die Anzahl der Elemente in dem Element an, das von diesem Puffer Deklarator mit fester Größe eingeführt wurde.The constant expression denotes the number of elements in the member introduced by that fixed size buffer declarator. Der Typ des konstanten Ausdrucks muss implizit in den Typ "int" konvertiert werden, und der Wert muss eine positive ganze Zahl ungleich NULL sein.The type of the constant expression must be implicitly convertible to type int, and the value must be a non-zero positive integer.

Es ist sichergestellt, dass die Elemente eines Puffers mit fester Größe sequenziell im Arbeitsspeicher angeordnet werden.The elements of a fixed size buffer are guaranteed to be laid out sequentially in memory.

Eine Puffer Deklaration fester Größe, die mehrere Puffer fester Größe deklariert, entspricht mehreren Deklarationen einer einzelnen Puffer Deklaration mit fester Größe mit denselben Attributen und Elementtypen.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. Beispiel:For example

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

für die folgende Syntax:is equivalent to

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

Puffer fester Größe in AusdrückenFixed size buffers in expressions

Die Member-Suche (Operatoren) eines Puffer Elements fester Größe verläuft genau wie die Element Suche eines Felds.Member lookup (Operators) of a fixed size buffer member proceeds exactly like member lookup of a field.

Ein Puffer fester Größe kann in einem Ausdruck mithilfe eines Simple_name (Typrückschlusses) oder eines member_access (Kompilierzeit Überprüfung der dynamischen Überladungs Auflösung) referenziert werden.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).

Wenn ein Puffer Element mit fester Größe als einfacher Name referenziert wird, entspricht der Effekt dem Element Zugriff im Formular this.I, wobei I das Puffer Element mit fester Größe ist.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.

Wenn ein Element Zugriff auf das Formular E.I, wenn E einen Strukturtyp und eine Element Suche von I in diesem Strukturtyp einen Member mit fester Größe identifiziert, wird E.I wie folgt als klassifiziert ausgewertet: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:

  • Wenn der Ausdruck E.I nicht in einem unsicheren Kontext auftritt, tritt ein Kompilierzeitfehler auf.If the expression E.I does not occur in an unsafe context, a compile-time error occurs.
  • Wenn E als Wert klassifiziert wird, tritt ein Kompilierzeitfehler auf.If E is classified as a value, a compile-time error occurs.
  • Andernfalls tritt ein Kompilierzeitfehler auf, wenn E eine verschiebbare Variable (Fixed und verschiebbare Variablen) und der Ausdruck E.I kein fixed_pointer_initializer (die fixed-Anweisung) ist.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.
  • Andernfalls verweist E auf eine Variable mit fester Größe, und das Ergebnis des Ausdrucks ist ein Zeiger auf das erste Element des Puffer Elements mit fester Größe I in E.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. Das Ergebnis ist vom Typ "S*", wobei "S" der Elementtyp "I" ist und als Wert klassifiziert wird.The result is of type S*, where S is the element type of I, and is classified as a value.

Auf die nachfolgenden Elemente des Puffers mit fester Größe kann mithilfe von Zeiger Vorgängen aus dem ersten Element zugegriffen werden.The subsequent elements of the fixed size buffer can be accessed using pointer operations from the first element. Im Gegensatz zum Zugriff auf Arrays ist der Zugriff auf die Elemente eines Puffers fester Größe ein unsicherer Vorgang und ist nicht Bereichs geprüft.Unlike access to arrays, access to the elements of a fixed size buffer is an unsafe operation and is not range checked.

Im folgenden Beispiel wird eine Struktur mit einem Puffer Element fester Größe deklariert und verwendet.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);
    }
}

Definitive Zuweisungs ÜberprüfungDefinite assignment checking

Puffer fester Größe unterliegen nicht der eindeutigen Zuweisungs Überprüfung (definitive Zuweisung), und Puffer Elemente fester Größe werden ignoriert, um eine definitive Zuweisungs Überprüfung von Strukturtyp Variablen zu bestimmen.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.

Wenn die äußerste enthaltende Struktur Variable eines Puffer Elements mit fester Größe eine statische Variable, eine Instanzvariable einer Klasseninstanz oder ein Array Element ist, werden die Elemente des Puffers fester Größe automatisch mit ihren Standardwerten initialisiert (Standardeinstellung: Werte).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 allen anderen Fällen ist der anfängliche Inhalt eines Puffers mit fester Größe nicht definiert.In all other cases, the initial content of a fixed size buffer is undefined.

Stapel ZuordnungStack allocation

In einem unsicheren Kontext kann eine lokale Variablen Deklaration (Deklarationen von lokalen Variablen) einen Stapel Zuordnungs Initialisierer enthalten, der Arbeitsspeicher aus der-aufrufsliste zuweist.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 ']'
    ;

Der unmanaged_type gibt den Typ der Elemente an, die im neu zugewiesenen Speicherort gespeichert werden, und der Ausdruck gibt die Anzahl dieser Elemente an.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. Diese geben die erforderliche Zuordnungs Größe an.Taken together, these specify the required allocation size. Da die Größe einer Stapel Zuordnung nicht negativ sein kann, handelt es sich um einen Kompilierzeitfehler, der die Anzahl der Elemente als constant_expression angibt, die einen negativen Wert ergibt.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.

Ein Stapel Zuordnungs Initialisierer der Form stackalloc T[E] erfordert, dass T ein nicht verwalteter Typ (Zeiger Typen) und E ein Ausdruck des Typs int ist.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. Das-Konstrukt ordnet E * sizeof(T) Bytes aus der-aufrufsstapel zu und gibt einen Zeiger vom Typ T* an den neu belegten Block zurück.The construct allocates E * sizeof(T) bytes from the call stack and returns a pointer, of type T*, to the newly allocated block. Wenn E ein negativer Wert ist, ist das Verhalten nicht definiert.If E is a negative value, then the behavior is undefined. Wenn E 0 (null) ist, wird keine Zuweisung durchgeführt, und der zurückgegebene Zeiger ist Implementierungs definiert.If E is zero, then no allocation is made, and the pointer returned is implementation-defined. Wenn nicht genügend Arbeitsspeicher verfügbar ist, um einen Block mit der angegebenen Größe zuzuordnen, wird eine System.StackOverflowException ausgelöst.If there is not enough memory available to allocate a block of the given size, a System.StackOverflowException is thrown.

Der Inhalt des neu zugeordneten Speichers ist undefiniert.The content of the newly allocated memory is undefined.

Stapel Zuordnungs Initialisierer sind in catch-oder finally-Blöcken (try-Anweisung) nicht zulässig.Stack allocation initializers are not permitted in catch or finally blocks (The try statement).

Es gibt keine Möglichkeit, Arbeitsspeicher, der mit stackalloc belegt wird, explizit freizugeben.There is no way to explicitly free memory allocated using stackalloc. Alle Stapel zugeordneten Speicherblöcke, die während der Ausführung eines Funktionsmembers erstellt werden, werden automatisch verworfen, wenn der Funktions Member zurückgegeben wird.All stack allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns. Dies entspricht der Funktion "alloca", eine Erweiterung, die häufig in C++ C und Implementierungen gefunden wird.This corresponds to the alloca function, an extension commonly found in C and C++ implementations.

Im BeispielIn 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));
    }
}

ein stackalloc-Initialisierer wird in der IntToString-Methode verwendet, um einen Puffer mit 16 Zeichen im Stapel zuzuordnen.a stackalloc initializer is used in the IntToString method to allocate a buffer of 16 characters on the stack. Der Puffer wird automatisch verworfen, wenn die Methode zurückgibt.The buffer is automatically discarded when the method returns.

Dynamische Speicher BelegungDynamic memory allocation

Mit Ausnahme des stackalloc-Operators C# werden von keine vordefinierten Konstrukte zum Verwalten von nicht Garbage Collection-Speicher bereitgestellt.Except for the stackalloc operator, C# provides no predefined constructs for managing non-garbage collected memory. Solche Dienste werden in der Regel durch Unterstützung von Klassenbibliotheken oder direkt aus dem zugrunde liegenden Betriebssystem bereitgestellt.Such services are typically provided by supporting class libraries or imported directly from the underlying operating system. Beispielsweise wird in der Memory-Klasse unten veranschaulicht, wie auf die Heap Funktionen eines zugrunde liegenden Betriebssystems C#zugegriffen werden kann: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 static unsafe class Memory
{
    // Handle for the process heap. This handle is used in all calls to the
    // HeapXXX APIs in the methods below.
    private static readonly IntPtr s_heap = GetProcessHeap();

    // 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(s_heap, HEAP_ZERO_MEMORY, (UIntPtr)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(s_heap, 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(s_heap, HEAP_ZERO_MEMORY, block, (UIntPtr)size);
        if (result == null) throw new OutOfMemoryException();
        return result;
    }

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

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

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

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

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

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

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

Im folgenden finden Sie ein Beispiel, in dem die Klasse Memory verwendet wird:An example that uses the Memory class is given below:

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

Im Beispiel werden 256 Bytes Arbeitsspeicher über Memory.Alloc zugewiesen und der Speicherblock mit Werten, die von 0 bis 255 steigen, initialisiert.The example allocates 256 bytes of memory through Memory.Alloc and initializes the memory block with values increasing from 0 to 255. Anschließend ordnet es ein 256-Element-Bytearray zu und verwendet Memory.Copy, um den Inhalt des Speicherblocks in das Bytearray zu kopieren.It then allocates a 256 element byte array and uses Memory.Copy to copy the contents of the memory block into the byte array. Schließlich wird der Speicherblock mithilfe von Memory.Free freigegeben, und der Inhalt des Bytearrays wird in der Konsole ausgegeben.Finally, the memory block is freed using Memory.Free and the contents of the byte array are output on the console.