Aracılığıyla paylaş


24 Güvenli olmayan kod

24.1 Genel

Bu yan tümcede tanımlanan söz dizimsel kuralların kullanımını tanılamak için güvenli olmayan kodu desteklemeyen bir uygulama gereklidir.

Bu yan tümcenin kalan kısmı, alt tümceleri de dahil olmak üzere koşullu olarak normatiftir.

Not: Önceki yan tümcelerde tanımlandığı gibi çekirdek C# dili, işaretçilerin veri türü olarak atlanmasında özellikle C ve C++ dilinden farklıdır. Bunun yerine, C# referanslar ve çöp toplayıcı tarafından yönetilen nesneler oluşturma olanağı sağlar. Bu tasarım, diğer özelliklerle birlikte C# dilini C veya C++'dan çok daha güvenli bir dil haline getirir. Çekirdek C# dilinde başlatılmamış bir değişkene, "sarkan" işaretçiye veya bir diziyi sınırlarının ötesinde dizine alan bir ifadeye sahip olmak mümkün değildir. Bu nedenle rutin olarak C ve C++ programlarına veba eden hataların tüm kategorileri ortadan kalkar.

C veya C++ içindeki her işaretçi türü yapısının C# dilinde bir başvuru türüne karşılık gelmesine rağmen, işaretçi türlerine erişimin gerekli olduğu durumlar vardır. Örneğin, temel işletim sistemiyle birlikte çalışma, belleğe eşlenen bir cihaza erişme veya zaman açısından kritik bir algoritma uygulama, işaretçilere erişim olmadan mümkün veya pratik olmayabilir. C# bu ihtiyacı gidermek için güvenli olmayan kod yazma olanağı sağlar.

Güvenli olmayan kodda, işaretçileri bildirmek ve üzerinde çalışmak, işaretçiler ve integral türleri arasında dönüştürmeler gerçekleştirmek, değişkenlerin adresini almak vb. mümkündür. Bir anlamda, güvenli olmayan kod yazmak, C# programı içinde C kodu yazmaya çok benzer.

Güvenli olmayan kod aslında hem geliştiriciler hem de kullanıcılar açısından "güvenli" bir özelliktir. Güvenli olmayan kod, değiştirici unsafeile açıkça işaretlenecektir, bu nedenle geliştiriciler yanlışlıkla güvenli olmayan özellikleri kullanamaz ve yürütme altyapısı güvenilmeyen bir ortamda güvenli olmayan kodun yürütülememesini sağlamak için çalışır.

dipnot

24.2 Güvenli olmayan bağlamlar

C# öğesinin güvenli olmayan özellikleri yalnızca güvenli olmayan bağlamlarda kullanılabilir. Güvenli olmayan bir bağlam, bir tür, üye veya yerel işlev bildirimine unsafe değiştirici eklenmesi veya unsafe_statement kullanılmasıyla oluşturulabilir.

  • Sınıf, yapı, arabirim veya temsilci bildirimi bir unsafe değiştirici içerebilir; bu durumda, bu tür bildirimin metinsel kapsamının tamamı (sınıfın, yapının veya arabirimin gövdesi dahil) güvenli olmayan bir bağlam olarak kabul edilir.

    Not: Eğer type_declaration parçalıysa, yalnızca o bölüm güvensiz bir bağlamdır. dipnot

  • Alan, yöntem, özellik, olay, dizin oluşturucu, işleç, örnek oluşturucu, sonlandırıcı, statik oluşturucu veya yerel işlevin bildirimi bir unsafe değiştirici içerebilir; bu durumda, bu üye bildiriminin metinsel kapsamının tamamı güvenli olmayan bir bağlam olarak kabul edilir.
  • unsafe_statement, bir blok içinde güvenli olmayan bir bağlam kullanılmasını sağlar. İlişkili bloğun metinsel kapsamının tamamı güvenli olmayan bir bağlam olarak kabul edilir. Güvenli olmayan bir bağlam içinde bildirilen yerel bir işlev kendisi güvenli değildir.

İlişkili dil bilgisi uzantıları aşağıda ve sonraki alt başlıklarda gösterilir.

unsafe_modifier
    : 'unsafe'
    ;

unsafe_statement
    : 'unsafe' block
    ;

Örnek: Aşağıdaki kodda

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

unsafe struct bildiriminde belirtilen değiştirici, yapı bildiriminin metinsel kapsamının tamamının güvenli olmayan bir bağlam haline gelmesine neden olur. Bu nedenle, Left ve Right alanlarını işaretçi türünde bildirmek mümkündür. Yukarıdaki örnek de yazılabilir

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

Burada, unsafe alan bildirimlerindeki değiştiriciler bu bildirimlerin güvenli olmayan bağlamlar olarak kabul edilmesine neden olur.

son örnek

Güvenli olmayan bir bağlam oluşturmanın dışında, işaretçi türlerinin kullanılmasına izin verilse de, değiştiricinin unsafe bir tür veya üye üzerinde hiçbir etkisi yoktur.

Örnek: Aşağıdaki kodda

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

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

F'deki A yönteminin güvensiz değiştiricisi, metinsel kapsamı F olan kısmı, dilin güvenli olmayan özelliklerinin kullanılabileceği güvensiz bir bağlam haline getirir. F içinde B geçersiz kılmasında, unsafe değiştiriciyi yeniden belirtmeye gerek yoktur; tabii ki F'deki B yöntemin kendisinin güvenli olmayan özelliklere erişmesi gerektiği durumlar hariç.

İşaretçi türü yöntemin imzasının bir parçası olduğunda durum biraz farklıdır

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

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

Burada imzası bir işaretçi türü içerdiği için Fyalnızca güvenli olmayan bir bağlamda yazılabilir. Ancak, güvenli olmayan bağlam, A gibi sınıfın tamamını güvensiz hale getirerek veya yöntemin bildiriminde unsafe gibi bir B değiştirici ekleyerek ortaya çıkarılabilir.

son örnek

unsafe Değiştirici kısmi tür bildiriminde (§15.2.7) kullanıldığında, yalnızca bu bölüm güvenli olmayan bir bağlam olarak kabul edilir.

24.3 İşaretçi türleri

Güvenli olmayan bir bağlamda, bir tür (§8.1) hem pointer_type hem de value_type, reference_type veya type_parameter olabilir. Güvenli olmayan bir bağlamda , pointer_type bir dizinin (§17) öğe türü de olabilir. pointer_type, güvenli olmayan bir bağlam dışında (kullanım güvenli olmadığından) bir typeof ifadesinde (§12.8.18) de kullanılabilir.

pointer_type adlı yapı unmanaged_type (§8.8) veya void anahtar sözcüğü olarak ifade edilir ve ardından bir * belirteç gelir:

pointer_type
    : value_type ('*')+
    | 'void' ('*')+
    ;

İşaretçi türünden * önce belirtilen tür, işaretçi türünün başvuran türü olarak adlandırılır. İşaretçi türündeki bir değerin işaret ettiği değişkenin türünü temsil eder.

pointer_type yalnızca güvenli olmayan bir bağlamda (§24.2) array_type kullanılabilir. non_array_type, kendisi bir array_type olmayan herhangi bir türdür.

Başvurulardan (başvuru türlerinin değerleri) farklı olarak, işaretçiler çöp toplayıcı tarafından izlenmez; çöp toplayıcının işaretçiler ve işaret ettiği veriler hakkında bilgisi yoktur. Bu nedenle, bir işaretçinin bir başvuruya veya başvuru içeren bir yapıya işaret etmesine izin verilmez ve bir işaretçinin başvurduğu tür unmanaged_type olmalıdır. İşaretçi türleri yönetilmeyen türler olduğundan, işaretçi türü başka bir işaretçi türü için başvuru türü olarak kullanılabilir.

İşaretçilerin ve başvuruların karıştırılması için sezgisel kural, başvuru ögelerinin (nesnelerin) işaretçi içermesine izin verilir, ancak işaretçilere başvuranların başvuru içermesine izin verilmez.

Örnek: İşaretçi türlerine bazı örnekler aşağıdaki tabloda verilmiştir:

Örnek Açıklama
byte* byte için işaretçi
char* char için işaretçi
int** İşaretçi işaretçisi int
int*[] Tek boyutlu işaretçi dizisi int
void* Bilinmeyen tür işaretçisi

son örnek

Belirli bir uygulama için tüm işaretçi türleri aynı boyuta ve gösterime sahip olmalıdır.

Not: C ve C++'ın aksine, aynı bildirimde birden çok işaretçi bildirildiğinde, C# * dilinde her işaretçi adında ön ek noktalama işareti olarak değil, yalnızca temel türle birlikte yazılır. Örneğin:

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

dipnot

türüne T*T sahip bir işaretçinin değeri, bir değişkenin adresini temsil eder. İşaretçi dolaylı işleci * (§24.6.2) bu değişkene erişmek için kullanılabilir.

Örnek: türünde Pbir değişken int* verildiğinde, ifade *P içinde bulunan intadreste bulunan değişkeni belirtirP. son örnek

İşaretçi, nesne başvurusu gibi, null olabilir. -değerli bir nullişaretçiye dolaylı işleç uygulanması, uygulama tanımlı davranışa (§24.6.2) neden olur. Değeri null olan bir işaretçi all-bits-zero ile temsil edilir.

türü void* , bilinmeyen bir türe yönelik bir işaretçiyi temsil eder. Başvuran türü bilinmediğinden, dolaylı işleç türündeki void*bir işaretçiye uygulanamaz veya böyle bir işaretçi üzerinde herhangi bir aritmetik gerçekleştirilemez. Ancak, tür void* işaretçisi başka bir işaretçi türüne (veya tersi) atanabilir ve diğer işaretçi türlerinin değerleriyle karşılaştırılabilir (§24.6.8).

İşaretçi türleri ayrı bir tür kategorisidir. Başvuru türlerinden ve değer türlerinden farklı olarak, işaretçi türleri öğesinden object devralılmaz ve işaretçi türleri ile objectarasında hiçbir dönüştürme yoktur. Özellikle kutulama ve kutu açma (§8.3.13) işaretçiler için desteklenmez. Ancak, farklı işaretçi türleri arasında ve işaretçi türleri ile integral türleri arasında dönüştürmelere izin verilir. Bu, §24.5'te açıklanmıştır.

pointer_type tür bağımsız değişkeni (§8.4) olarak kullanılamaz ve tür bağımsız değişkeninin işaretçi türü olarak çıkarılacağı genel yöntem çağrılarında tür çıkarımı (§12.6.3) başarısız olur.

pointer_type, dinamik olarak bağlı bir işlemin (§12.3.3) alt ifade türü olarak kullanılamaz.

pointer_type, bir uzantı yöntemindeki (§15.6.10) ilk parametrenin türü olarak kullanılamaz.

Pointer_type geçici alan türü (§15.5.4) olarak kullanılabilir.

Dinamik silmenin bir türü olan E*, öğesinin dinamik silmenin başvuru türüne sahip işaretçi türüdürE.

İşaretçi türüne sahip bir ifade, anonymous_object_creation_expression içindeki bir member_declarator değerini sağlamak için kullanılamaz (§12.8.17.3).

Herhangi bir işaretçi türü için varsayılan değer (§9,3) şeklindedir null.

Not: İşaretçiler başvuru parametreleri olarak geçirilebilir, ancak bu işlem tanımsız davranışa yol açabilir. Çünkü işaretçi, çağrılan yöntem döndüğünde artık var olmayan bir yerel değişkene veya eskiden işaret ettiği sabit durumdaki nesne artık sabit değilse ona işaret edebilir. Örneğin:

class Test
{
    static int value = 20;

    unsafe static void F(out int* pi1, ref int* pi2) 
    {
        int i = 10;
        pi1 = &i;       // return address of local variable
        fixed (int* pj = &value)
        {
            // ...
            pi2 = pj;   // return address that will soon not be fixed
        }
    }

    static void Main()
    {
        int i = 15;
        unsafe 
        {
            int* px1;
            int* px2 = &i;
            F(out px1, ref px2);
            int v1 = *px1; // undefined
            int v2 = *px2; // undefined
        }
    }
}

dipnot

Bir yöntem bir tür değeri döndürebilir ve bu tür bir işaretçi olabilir.

Örnek: Bitişik bir s dizisine int, bu dizinin öğe sayısına ve başka int bir değere işaretçi verildiğinde, aşağıdaki yöntem bir eşleşme oluşursa bu değerin adresini döndürür; aksi takdirde döndürür 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;
}

son örnek

Güvenli olmayan bir bağlamda, işaretçiler üzerinde çalıştırmak için çeşitli yapılar kullanılabilir:

  • Birli * işleç işaretçi dolaylı işlemi gerçekleştirmek için kullanılabilir (§24.6.2).
  • İşleç -> , bir işaretçi (§24.6.3) aracılığıyla bir yapının üyesine erişmek için kullanılabilir.
  • İşleç [] bir işaretçiyi dizine almak için kullanılabilir (§24.6.4).
  • Birli & işleç bir değişkenin adresini almak için kullanılabilir (§24.6.5).
  • ++ ve -- işleçleri işaretçileri (§24.6.6) artırmak ve azaltmak için kullanılabilir.
  • İkili + ve - işleçler işaretçi aritmetiği (§24.6.7) gerçekleştirmek için kullanılabilir.
  • ==İşaretçileri karşılaştırmak için , !=<, >, , <=ve >= işleçleri kullanılabilir (§24.6.8).
  • işleci stackalloc çağrı yığınından bellek ayırmak için kullanılabilir (§24.9).
  • Deyimi fixed , bir değişkenin adresinin alınabilmesi için geçici olarak düzeltmek için kullanılabilir (§24.7).

24.4 Sabit ve taşınabilir değişkenler

İşlecin adresi (§24.6.5) ve fixed deyimi (§24.7) değişkenleri iki kategoriye ayırır: Sabit değişkens ve taşınabilir değişkens.

Sabit değişkenler, atık toplayıcının işleminden etkilenmeyen depolama konumlarında bulunur. (Sabit değişkenlere örnek olarak yerel değişkenler, değer parametreleri ve işaretçilerin referansı kaldırılarak oluşturulan değişkenler verilebilir.) Öte yandan, gezer değişkenler, çöp toplayıcı tarafından yeniden konumlandırma veya atma işlemlerine tabi olan depolama alanlarında bulunur. (Taşınabilir değişkenlere örnek olarak nesnelerdeki alanlar ve dizi öğeleri verilebilir.)

& işleci (§24.6.5), sabit bir değişkenin adresinin kısıtlama olmadan alınmasına izin verir. Ancak, taşınabilir bir değişken çöp toplayıcı tarafından yeniden konumlandırmaya veya yok etmeye tabi olduğundan, taşınabilir değişkenin adresi yalnızca bir (fixed statement) kullanılarak elde edilebilir ve bu adres yalnızca bu fixed deyimin süresi boyunca geçerli kalır.

Kesin olarak, sabit değişken aşağıdakilerden biridir:

  • Değişken anonim bir işlev (§12.21.6.2) tarafından yakalanmadığı sürece yerel değişkene, değer parametresine veya parametre dizisine başvuran bir simple_name (§12.8.4) sonucu oluşan değişken.
  • Formun member_access (V.I) sonucu oluşan değişken; burada V bir struct_type sabit değişkenidir.
  • Formun pointer_indirection_expression (§24.6.2) , formun *Ppointer_member_access (§24.6.3) veya formun P->Ipointer_element_access (P[E]) sonucu oluşan değişken.

Diğer tüm değişkenler taşınabilir değişkenler olarak sınıflandırılır.

Statik alan taşınabilir değişken olarak sınıflandırılır. Ayrıca, parametre için verilen bağımsız değişken sabit bir değişken olsa bile, bir başvuru parametresi taşınabilir değişken olarak sınıflandırılır. Son olarak, işaretçi gösterim kaldırılarak oluşturulan bir değişken her zaman sabit değişken olarak sınıflandırılır.

24.5 İşaretçi dönüştürmeleri

24.5.1 Genel

Güvenli olmayan bir bağlamda, kullanılabilir örtük dönüştürmeler (§10.2) kümesi aşağıdaki örtük işaretçi dönüştürmelerini içerecek şekilde genişletilir:

  • Herhangi bir pointer_type türünden void* türüne.
  • Sabit null değerinden (§6.4.5.7) herhangi bir pointer_type'a.

Ayrıca, güvenli olmayan bir bağlamda, kullanılabilir açık dönüştürmeler kümesi (§10.3) aşağıdaki açık işaretçi dönüştürmelerini içerecek şekilde genişletilir:

  • Herhangi bir pointer_type'dan herhangi bir diğer pointer_type'e.
  • sbyte, byte, short, ushort, int, uint, long, veya ulong herhangi bir pointer_type'a.
  • herhangi bir pointer_typesbyte, byte, short, ushort, int, uint, long veya ulong.

Son olarak, güvenli olmayan bir bağlamda, standart örtük dönüştürme kümesi (§10.4.2) aşağıdaki işaretçi dönüştürmelerini içerir:

  • Herhangi bir pointer_type türünden void* türüne.
  • "Literal"dan null herhangi bir pointer_type'a.

İki işaretçi türü arasındaki dönüştürmeler hiçbir zaman gerçek işaretçi değerini değiştirmez. Başka bir deyişle, bir işaretçi türünden diğerine dönüştürmenin, işaretçi tarafından verilen temel adres üzerinde hiçbir etkisi yoktur.

Bir işaretçi türü başka bir işaretçi türüne dönüştürüldüğünde, elde edilen işaretçi işaret ettiği tür için doğru şekilde hizalanmamışsa ve bu sonuç başvurulduğunda, davranış tanımsız olur. Genel olarak, "doğru hizalanmış" kavramı geçişlidir: bir işaretçi türü A için doğru hizalanmışsa, türü B olan bir işaretçi için doğru hizalanmışsa ve türü C olan bir işaretçi için doğru hizalanmışsa, türü A olan bir işaretçi, türü C olan bir işaretçi için doğru hizalanmıştır.

Örnek: Bir türe sahip bir değişkenin farklı bir türe işaretçiyle erişildiği aşağıdaki durumu göz önünde bulundurun:

unsafe static void M()
{
    char c = 'A';
    char* pc = &c;
    void* pv = pc;
    int* pi = (int*)pv; // pretend a 16-bit char is a 32-bit int
    int i = *pi;        // read 32-bit int; undefined
    *pi = 123456;       // write 32-bit int; undefined
}

son örnek

İşaretçi türü byte'a işaretçiye dönüştürüldüğünde, sonuç değişkenin en düşük adresli byte değerine işaret eder. Sonucun art arda artışları, değişkenin boyutuna kadar, bu değişkenin kalan baytlarına işaretçiler verir.

Örnek: Aşağıdaki yöntem, bir içindeki double sekiz baytın her birini onaltılık değer olarak görüntüler:

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

Tabii ki, üretilen çıkış, endianness'e bağlı olarak değişir. Bir olasılık vardır " BA FF 51 A2 90 6C 24 45".

son örnek

İşaretçiler ve tamsayılar arasındaki eşlemeler uygulama tanımlıdır.

Notu: Bununla birlikte, doğrusal adres alanına sahip 32 ve 64 bit CPU mimarilerinde, tam sayı türlerine veya tam sayı türlerinden işaretçilerin dönüştürülmesi genellikle bu uint veya ulong değerlerinin tam sayı türlerine veya tam sayı türlerinden dönüşümleri gibi davranır. dipnot

24.5.2 İşaretçi dizileri

İşaretçi dizileri , güvenli olmayan bir bağlamda array_creation_expression (§12.8.17.4) kullanılarak oluşturulabilir. İşaretçi dizilerinde yalnızca diğer dizi türlerine uygulanan bazı dönüştürmelere izin verilir:

  • Herhangi bir array_type türünden ve uyguladığı arabirimlere olan örtük başvuru dönüşümü (System.Array), işaretçi dizileri için de geçerlidir. Ancak, System.Array veya uyguladığı arabirimler aracılığıyla dizi öğelerine erişme girişimleri, işaretçi türleri object'ya dönüştürülemediğinden, çalışma zamanında bir özel durumla sonuçlanabilir.
  • Tek boyutlu dizi türü , ve onun genel temel arabirimlerinden örtük ve açık başvuru dönüştürmeleri (S[], System.Collections.Generic.IList<T>) hiçbir zaman işaretçi dizilerine uygulanmaz.
  • açık referans dönüşümü (§10.3.5), System.Array ve onun uyguladığı arabirimlerden herhangi bir array_type'e işaretçi dizileri için geçerlidir.
  • İşaretçi türleri tür bağımsız değişkenleri olarak kullanılamadığından ve işaretçi türlerinden işaretçi olmayan türlere dönüşüm olmadığı için ve temel arabirimlerinden tek boyutlu dizi türü System.Collections.Generic.IList<S>'ye yapılan açık referans dönüştürmeleri (T[]) hiçbir zaman işaretçi dizilerine uygulanmaz.

Bu kısıtlamalar, §9.4.4.17'de açıklanan diziler üzerindeki foreach deyiminin genişlemesinin işaretçi dizilerine uygulanamayacağı anlamına gelir. Bunun yerine, formun bir foreach deyimi

foreach (V v in x) embedded_statement

burada x türü, T[,,...,] şeklindeki bir dizi türüdür; n, boyut sayısının 1 eksiğidir ve T veya V bir işaretçi türüdür; iç içe döngüler kullanılarak aşağıdaki gibi genişletilir:

{
    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[i0,i1,...,in];
                *embedded_statement*
            }
        }
    }
}

değişkenleri a, , i0i1, ... in veya x ya da embedded_statement veya programın başka bir kaynak kodu için görünür ya da erişilebilir değildir. Değişken v, gömülü deyimde salt okunurdur. öğesinden (öğe türü) öğesine açık bir dönüştürme (T) Vyoksa bir hata oluşturulur ve başka bir adım atılmaz. x değeri null ise, çalışma zamanında bir System.NullReferenceException fırlatılır.

Not: tür bağımsız değişkenleri olarak işaretçi türlerine izin verilmiyor olsa da, tür bağımsız değişkenleri olarak işaretçi dizileri kullanılabilir. dipnot

24.6 İfadelerdeki işaretçiler

24.6.1 Genel

Güvenli olmayan bir bağlamda, bir ifade işaretçi türünün sonucunu verebilir, ancak güvenli olmayan bir bağlamın dışında, bir ifadenin işaretçi türünde olması bir derleme zamanı hatasıdır. Kesin olarak, güvensiz bir bağlamın dışında herhangi bir simple_name (§12.8.4), member_access (§12.8.7), invocation_expression (§12.8.10) veya element_access (§12.8.12) işaretçi türündeyse derleme zamanı hatası oluşur.

Güvenli olmayan bir bağlamda , primary_expression (§12.8) ve unary_expression (§12.9) üretimleri, aşağıdaki alt başlıklarda açıklanan ek yapılara izin verir.

Not: Güvenli olmayan işleçlerin önceliği ve ilişkilendirmesi dil bilgisiyle belirtilir. dipnot

24.6.2 İşaretçi dolaylı

pointer_indirection_expression, bir yıldız işareti (*) ve ardından bir tekil ifade ile oluşur.

pointer_indirection_expression
    : '*' unary_expression
    ;

Birli * işleç, işaretçinin dolaylı olduğunu belirtir ve işaretçinin işaret ettiği değişkeni elde etmek için kullanılır. *P bir işaretçi türü olan P ifadesini değerlendirdiğinizde, T* sonucunda türünde T bir değişken elde edilir. Türü * olan bir ifadeye veya işaretçi türünde olmayan bir ifadeye void* işleci uygulamak derleme zamanı hatasıdır.

Unary * işlecinin bir null değerli işaretçiye uygulanmasının etkisi, uygulama tarafından tanımlanır. Özellikle, bu işlemin bir System.NullReferenceExceptionattığının garantisi yoktur.

İşaretçiye geçersiz bir değer atandıysa, birli * işlecin davranışı tanımlanmamış olur.

Not: bir işaretçiyi birli * işleç tarafından başvurudan kaldırmaya yönelik geçersiz değerler arasında işaret edilen tür için uygun olmayan şekilde hizalanmış bir adres ( bkz. §24.5'teki örnek) ve bir değişkenin yaşam süresi sonundan sonraki adresi.

Kesin atama analizi amacıyla, formun *P bir ifadesi değerlendirilerek üretilen bir değişken başlangıçta atanmış olarak kabul edilir (§9.4.2).

24.6.3 İşaretçi üyesi erişimi

pointer_member_access, bir primary_expression, ardından bir "->" işareti ve ardından bir tanımlayıcı ve isteğe bağlı bir type_argument_list oluşur.

pointer_member_access
    : primary_expression '->' identifier type_argument_list?
    ;

P->I biçimindeki bir işaretçi üyesi erişiminde, P bir işaretçi türünün ifadesi olmalı ve I, P'ün işaret ettiği türün erişilebilir bir üyesini belirtmelidir.

Form P->I'daki bir işaretçi üyesi erişimi, tam olarak (*P).I olarak değerlendirilir. İşaretçi dolaylı işlecinin ()* açıklaması için bkz. §24.6.2. Üye erişim işlecinin (). açıklaması için bkz. §12.8.7.

Örnek: Aşağıdaki kodda

struct Point
{
    public int x;
    public int y;
    public override string ToString() => $"({x},{y})";
}

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

-> işleci, alanlara erişmek ve bir işaretçi aracılığıyla bir yapının yöntemini çağırmak için kullanılır. İşlem P->I tam olarak (*P).I ile eşdeğerdir, bu nedenle Main yöntemi de aynı derecede iyi yazılabilirdi.

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

son örnek

24.6.4 İşaretçi öğesi erişimi

pointer_element_access bir primary_expression ve ardından "[" ve "]" ile çevrilmiş bir ifadeden oluşur.

pointer_element_access
    : primary_expression '[' expression ']'
    ;

Hem element_accesshem depointer_element_access (§24.6.4) alternatiflerinin geçerli olup olmadığını primary_expression tanındığında, katıştırılmış primary_expression işaretçi türündeyse (§24.3) ikinci seçenek seçilir.

Bir işaretçi öğesi erişimi şeklinde P[E], Pvoid* dışındaki bir işaretçi türünün ifadesi olmalıdır ve E, int, uint, long veya ulong öğesine örtük olarak dönüştürülebilecek bir ifade olmalıdır.

P[E] biçimindeki işaretçi öğesine erişim, tam olarak *(P + E) şeklinde değerlendirilir. İşaretçi dolaylı işlecinin ()* açıklaması için bkz. §24.6.2. İşaretçi ekleme işlecinin ()+ açıklaması için bkz. §24.6.7.

Örnek: Aşağıdaki kodda

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

bir döngüdeki karakter arabelleği başlatmak için bir for işaretçi öğesi erişimi kullanılır. İşlem P[E], *(P + E) ile tam olarak eşdeğer olduğundan, örnek aynı şekilde yazılabilirdi.

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

son örnek

İşaretçi öğesi erişim işleci, sınır dışı hataları denetlemez ve bir sınır dışı öğeye erişirken oluşan davranış tanımlanmamıştır.

Not: Bu, C ve C++ ile aynıdır. dipnot

24.6.5 İşlecin adresi

addressof_expression, bir ve işareti (&) ve ardından bir bütünleşik ifade birleşiminden oluşur.

addressof_expression
    : '&' unary_expression
    ;

Türünde E olan ve sabit değişken (T) olarak sınıflandırılan bir ifade verildiğinde, yapısı &E tarafından Everilen değişkenin adresini hesaplar. Sonucun türü T* ve değer olarak sınıflandırılmıştır. Derleme zamanı hatası, E değişken olarak sınıflandırılmadığında, E salt okunur bir yerel değişken olarak sınıflandırıldığında veya E taşınabilir bir değişkeni belirttiğinde oluşur. Son durumda, adresini almadan önce değişkeni geçici olarak "düzeltmek" için sabit bir deyim (§24.7) kullanılabilir.

Not: §12.8.7'de belirtildiği gibi, bir alanı tanımlayan readonly bir yapı veya sınıf için örnek oluşturucu veya statik oluşturucu dışında, bu alan değişken değil değer olarak kabul edilir. Bu nedenle adresi alınamaz. Benzer şekilde, bir sabitin adresi alınamaz. dipnot

& işleci için bağımsız değişkeninin kesin olarak atanması gerekmez, ancak bir & işleminin ardından, işlecin uygulandığı değişken, işlemin gerçekleştiği yürütme yolunda kesin olarak atanmış olarak kabul edilir. Değişkenin doğru şekilde başlatılmasının bu durumda gerçekleşmesini sağlamak programcının sorumluluğundadır.

Örnek: Aşağıdaki kodda

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

i, &i'yi başlatmak için kullanılan p işleminden sonra kesinlikle atanmış olarak kabul edilir. Atama *p işlem olarak i başlatır, ancak bu başlatmanın eklenmesi programcının sorumluluğundadır ve atama kaldırılırsa derleme zamanı hatası oluşmaz.

son örnek

Not: İşleç için & kesin atama kuralları, yerel değişkenlerin yedekli başlatılmasını önleyebilecek şekilde mevcuttur. Örneğin, birçok dış API, API tarafından doldurulan bir yapıya yönelik bir işaretçi alır. Bu tür API'lere yapılan çağrılar genellikle yerel bir yapı değişkeninin adresini geçirir ve kural olmadan yapı değişkeninin yedekli olarak başlatılması gerekir. dipnot

Not: Yerel değişken, değer parametresi veya parametre dizisi anonim bir işlev (§12.8.24) tarafından yakalandığında, bu yerel değişken, parametre veya parametre dizisi artık sabit değişken (§24.7) olarak kabul edilmez, bunun yerine taşınabilir değişken olarak kabul edilir. Bu nedenle, herhangi bir güvenli olmayan kodun anonim bir işlev tarafından yakalanan yerel değişkenin, değer parametresinin veya parametre dizisinin adresini alması bir hatadır. dipnot

24.6.6 İşaretçi artışı ve azaltması

Güvenli olmayan bir bağlamda ve ++-- işleçleri (§12.8.16 ve §12.9.7) dışındaki void*tüm türlerin işaretçi değişkenlerine uygulanabilir. Bu nedenle, T*her işaretçi türü için aşağıdaki işleçler örtük olarak tanımlanır:

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

İşleçler sırasıyla ve x+1ile aynı sonuçları x-1 üretir (§24.6.7). Başka bir deyişle, T* türünde bir işaretçi değişkeni için, ++ işleci değişkende bulunan adrese sizeof(T) ekler ve -- işleci değişkende bulunan adresten sizeof(T) çıkarır.

İşaretçi artırma veya azaltma işlemi işaretçi türünün etki alanını aşıyorsa, sonuç uygulama tanımlıdır, ancak hiçbir özel durum üretilmez.

24.6.7 İşaretçi aritmetiği

Güvenli olmayan bir bağlamda işleç + (§12.12.5) ve - işleç (§12.12.6) dışındaki void*tüm işaretçi türlerinin değerlerine uygulanabilir. Bu nedenle, T*her işaretçi türü için aşağıdaki işleçler örtük olarak tanımlanır:

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);

İşaretçi türündeki bir ifade P ve T* türünde bir ifade N verildiğinde, int, uint veya long türlerinden biri olarak, ifadeler ulong ve P + N, N + P tarafından verilen adrese T* eklenmesi sonucu oluşan N * sizeof(T) türündeki işaretçi değerini hesaplar. Benzer şekilde, P – N ifadesi T*tarafından verilen adresten N * sizeof(T) çıkarılarak elde edilen P türündeki işaretçi değerini hesaplar.

tr-TR: İşaretçi türünde P olan iki ifade Q ve T* verildiğinde, P – Q ifadesi, P ve Q tarafından verilen adresler arasındaki farkı hesaplar ve ardından bu farkı sizeof(T) ile böler. Sonucun türü her zaman longşeklindedir. Aslında, P - Q olarak ((long)(P) - (long)(Q)) / sizeof(T)hesaplanır.

Örnek:

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

çıktıyı üreten:

p - q = -14
q - p = 14

son örnek

İşaretçi aritmetik işlemi işaretçi türünün etki alanını aşıyorsa, sonuç uygulama tanımlı bir şekilde kesilir, ancak hiçbir istisna üretilmez.

24.6.8 İşaretçi karşılaştırması

Güvenli olmayan bir bağlamda, ==, !=, <, >, , <=ve >= işleçleri (§12.14) tüm işaretçi türlerinin değerlerine uygulanabilir. İşaretçi karşılaştırma işleçleri şunlardır:

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);

Herhangi bir işaretçi türünden void* türüne örtük bir dönüştürme mevcut olduğu için, bu işleçler kullanılarak herhangi bir işaretçi türünün operandları karşılaştırılabilir. Karşılaştırma işleçleri, iki işlecin verdiği adresleri işaretsiz tamsayılar olarak karşılaştırır.

24.6.9 Sizeof işleci

Belirli önceden tanımlanmış türler için (§12.8.19), sizeof işleç sabit int bir değer verir. Diğer tüm türler için, sizeof işlecin sonucu uygulama tarafından tanımlanır ve sabit olarak değil, değer olarak sınıflandırılır.

Üyelerin bir yapıya paketlenme sırası belirtilmemiştir.

Hizalama amaçlı yapısal bir düzenleme için, bir yapının başında, içinde ve sonunda adsız doldurma alanları bulunabilir. Doldurma olarak kullanılan bitlerin içeriği belirsizdir.

Yapı türüne sahip bir işlenene uygulandığında sonuç, doldurma dahil olmak üzere bu türdeki bir değişkendeki toplam bayt sayısıdır.

24.7 Sabit deyim

Güvenli olmayan bir bağlamda , embedded_statement (§13.1) üretimi, adresinin deyimin süresi boyunca sabit kalması için taşınabilir bir değişkeni "düzeltmek" için kullanılan sabit deyimi olan ek bir yapıya izin verir.

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
    ;

Her bir fixed_pointer_declarator, verilen pointer_type için bir yerel değişkeni bildirir ve bu yerel değişkeni, ilgili fixed_pointer_initializer tarafından hesaplanan adresle başlatır. Sabit bir deyimde bildirilen yerel değişkene, bu değişkenin bildiriminin sağında meydana gelen herhangi bir fixed_pointer_initializer içinde ve sabit deyiminin embedded_statement içinde erişilebilir. Sabit ifadesiyle bildirilen yerel değişken, salt okunur olarak kabul edilir. Katıştırılmış deyim, bu yerel değişkeni atama ya da ++ ve -- işleçleri aracılığıyla değiştirmeye çalışırsa veya onu bir başvuru ya da çıkış parametresi olarak geçirmeye çalışırsa derleme zamanı hatası oluşur.

Yakalanan bir yerel değişkenin (§12.21.6.2), değer parametresinin veya parametre dizisinin bir fixed_pointer_initializer kullanılması hatadır. fixed_pointer_initializer aşağıdakilerden biri olabilir:

  • "&" belirteci ve ardından yönetilmeyen türde bir taşınabilir değişkene (§24,4) variable_reference (§9,5) gelir. TürünTT* deyimde verilen işaretçi türüne örtük olarak dönüştürülebilir olması koşuluyla.fixed Bu durumda, başlatıcı verilen değişkenin adresini hesaplar ve değişkenin sabit deyiminin süresi boyunca sabit bir adreste kalacağı garanti edilir.
  • Bir yönetilmeyen türde öğeleri olan bir T ifadesi, tür T* sabit deyimde verilen işaretçi türüne örtük olarak dönüştürülebilir olduğu takdirde kullanılabilir. Bu durumda, başlatıcı dizideki ilk öğenin adresini hesaplar ve tüm dizinin deyiminin süresi fixed boyunca sabit bir adreste kalacağı garanti edilir. Dizi ifadesi ise null veya dizi sıfır öğeye sahipse, başlatıcı sıfıra eşit bir adres hesaplar.
  • Tür string, char* bildiriminde verilen işaretçi türüne örtük olarak dönüştürülebilir olmak koşuluyla, türü fixed olan bir ifade. Bu durumda, başlatıcı dizedeki ilk karakterin adresini hesaplar ve dizenin tamamının deyiminin fixed süresi boyunca sabit bir adreste kalacağı garanti edilir. Dize ifadesi fixed olduğunda, null deyiminin davranışı uygulama tanımlıdır.
  • array_type veya string dışında bir tür ifadesi, eğer imzası ref [readonly] T GetPinnableReference() ile uyumlu erişilebilir bir yöntem veya uzantı yöntemi varsa, burada T bir yönetilmeyen_tür ve T* ifadesi fixed ifadesinde verilen işaretçi türüne örtük olarak dönüştürülebilir. Bu durumda başlatıcı, döndürülen değişkenin adresini hesaplar ve bu değişkenin deyiminin fixed süresi boyunca sabit bir adreste kalması garanti edilir. Bir GetPinnableReference() yöntem, fixed deyimi tarafından kullanılabilir, aşırı yükleme çözümlemesi (§12.6.4) tam olarak bir fonksiyon elemanı ürettiğinde ve bu eleman önceki koşulları karşıladığında. GetPinnableReference yöntemi, sabitlenmesi gereken veri olmadığında System.Runtime.CompilerServices.Unsafe.NullRef<T>() tarafından döndürülen gibi sıfıra eşit bir adrese başvuru döndürmelidir.
  • Sabit boyutlu arabellek üyesinin türü, deyiminde verilen işaretçi türüne örtük olarak dönüştürülebilir olması koşuluyla, taşınabilir bir değişkenin sabit boyutlu arabellek üyesine başvuran bir simple_name veya fixed olabilir. Bu durumda başlatıcı, sabit boyutlu arabelleğin (§24.8.3) ilk öğesine yönelik bir işaretçi hesaplar ve sabit boyutlu arabelleğin deyimin fixed süresi boyunca sabit bir adreste kalması garanti edilir.

bir fixed_pointer_initializerfixed tarafından hesaplanan her adres için, ifade süresince adresle başvurulan değişkenin çöp toplayıcı tarafından yeniden konumlandırmaya veya yok edilmeye tabi olmamasını fixed sağlar.

Örnek: bir fixed_pointer_initializer tarafından hesaplanan adres bir nesnenin alanına veya dizi örneğinin bir öğesine başvuruda bulunursa, sabit deyim, içeren nesne örneğinin deyimin ömrü boyunca yeniden konumlandırılmaması veya atılmaması garanti eder. son örnek

Sabit deyimler tarafından oluşturulan işaretçilerin bu deyimlerin yürütülmesinden sonra hayatta kalmamasını sağlamak programcının sorumluluğundadır.

Örnek: fixed deyimleri ile oluşturulan işaretçiler dış API'lere geçtiğinde, bu API'lerin bu işaretçileri belleklerinde saklamadıklarından emin olmak programcının sorumluluğundadır. son örnek

Sabit nesneler yığının parçalanmasına neden olabilir (taşınamadıklarından). Bu nedenle, nesneler yalnızca kesinlikle gerekli olduğunda ve yalnızca mümkün olan en kısa süre boyunca düzeltilmelidir.

Örnek: Örnek

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

deyiminin fixed çeşitli kullanımlarını gösterir. İlk deyim statik bir alanın adresini düzeltir ve alır, ikinci deyim bir örnek alanının adresini düzeltir ve alır ve üçüncü deyim bir dizi öğesinin adresini düzeltir ve alır. Her durumda, değişkenlerin tümü taşınabilir değişkenler olarak sınıflandırıldığından normal & işleci kullanmak bir hata olurdu.

Yukarıdaki örnekteki üçüncü ve dördüncü fixed deyimler aynı sonuçları üretir. Genel olarak, bir dizi örneği a için, a[0] ifadesinde fixed belirtmek, yalnızca a belirtmekle aynıdır.

son örnek

Güvenli olmayan bir bağlamda, tek boyutlu dizilerin dizi öğeleri, dizin 0 ile başlayıp dizin Length – 1 ile biten artan sıralı dizin halinde depolanır. Çok boyutlu dizilerde, dizi öğeleri, önce en sağdaki boyutun dizinleri artırılarak, ardından bir sonraki soldaki boyutun dizinleri artırılarak ve bu şekilde sola doğru devam ederek depolanır.

Bir dizi örneğine işaretçi fixed elde eden bir p deyim içinde, a ile p arasında değişen işaretçi p + a.Length - 1 değerleri, dizideki öğelerin adreslerini temsil eder. Benzer şekilde, ile arasında değişen p[0]p[a.Length - 1] değişkenler gerçek dizi öğelerini temsil eder. Dizilerin depolanma şekli göz önüne alındığında, herhangi bir boyuttaki bir dizi doğrusalmış gibi işlenebilir.

Örnek:

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($"[{i},{j},{k}] = {a[i,j,k],2} ");
                }
                Console.WriteLine();
            }
        }
    }
}

çıktıyı üreten:

[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

son örnek

Örnek: Aşağıdaki kodda

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

bir fixed deyimi, bir diziyi sabitlemek amacıyla kullanılır, böylece adresi parametre olarak bir işaretçi alan bir yönteme geçirilebilir.

son örnek

Bir char* dize örneği düzeltilerek oluşturulan bir değer her zaman null olarak sonlandırılan bir dizeye işaret ediyor. ** Sabit bir ifade p için bir dize örneğine işaretçi s alır, işaretçi değerleri p ile p + s.Length ‑ 1 arasında değişir ve dizedeki karakterlerin adreslerini temsil ederken, işaretçi değeri p + s.Length her zaman bir null karaktere ('\0' değerine sahip karakter) işaret eder.

Örnek:

class Test
{
    static string name = "xx";

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

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

son örnek

Örnek: Aşağıdaki kod, fixed_pointer_initializer ile array_type veya string dışında bir ifade gösterir:

public class C
{
    private int _value;
    public C(int value) => _value = value;
    public ref int GetPinnableReference() => ref _value;
}

public class Test
{
    unsafe private static void Main()
    {
        C c = new C(10);
        fixed (int* p = c)
        {
            // ...
        }
    }
}

Tür C'nin doğru imzaya sahip erişilebilir bir GetPinnableReference yöntemi vardır. fixed ifadesinde, ref intc üzerinde çağrıldığında bu yöntemden döndürülen değer, int* işaretçisini p başlatmak için kullanılır. son örnek

Yönetilen türdeki nesnelerin sabit işaretçiler aracılığıyla değiştirilmesi tanımsız davranışa neden olabilir.

Not: Örneğin, dizeler sabit olduğundan, sabit bir dize işaretçisi tarafından başvuruda bulunan karakterlerin değiştirilmediğinden emin olmak programcının sorumluluğundadır. dipnot

Not: Dizelerin otomatik null sonlandırması, "C stili" dizeler bekleyen dış API'leri çağırırken özellikle kullanışlıdır. Ancak, bir dize örneğinin null karakterler içermesine izin verildiğini unutmayın. Böyle null karakterler varsa, dize null olarak sonlandırılan char*olarak kabul edildiğinde kesilmiş olarak görünür. dipnot

24.8 Sabit boyutlu arabellekler

24.8.1 Genel

Sabit boyutlu arabellekler, "C stili" satır içi dizileri yapıların üyesi olarak bildirmek için kullanılır ve yönetilmeyen API'lerle birlikte çalışmada öncelikli olarak yararlıdır.

24.8.2 Sabit boyutlu arabellek bildirimleri

Sabit boyutlu arabellek, belirli bir türe sahip değişkenlerin sabit uzunlukta arabelleği için depolamayı temsil eden bir üyedir. Sabit boyutlu arabellek bildirimi, belirli bir öğe türünün bir veya daha fazla sabit boyutlu arabelleğine neden olur.

Not: Dizi gibi, sabit boyutlu arabellek de öğeleri içeren olarak düşünülebilir. Bu nedenle, bir dizi için tanımlanan öğe türü terimi sabit boyutlu bir arabellekle de kullanılır. dipnot

Sabit boyutlu arabelleklere yalnızca yapı bildirimlerinde izin verilir ve yalnızca güvenli olmayan bağlamlarda (§24.2) oluşabilir.

fixed_size_buffer_declaration
    : attributes? fixed_size_buffer_modifier* 'fixed' buffer_element_type
      fixed_size_buffer_declarators ';'
    ;

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

buffer_element_type
    : type
    ;

fixed_size_buffer_declarators
    : fixed_size_buffer_declarator (',' fixed_size_buffer_declarator)*
    ;

fixed_size_buffer_declarator
    : identifier '[' constant_expression ']'
    ;

Sabit boyutlu arabellek bildirimi bir öznitelik kümesi (§23), new değiştirici (§15.3.5), yapı üyeleri için izin verilen bildirilen erişim izinlerinden herhangi birine karşılık gelen erişilebilirlik değiştiricileri (§16.4.3) ve değiştiriciyi unsafe (§24.2) içerebilir. Sabit boyutlu arabellek tanımlamasıyla bildirilen tüm üyelere öznitelikler ve değiştiriciler uygulanır. Aynı değiştiricinin sabit boyutlu arabellek bildiriminde birden çok kez görünmesi bir hatadır.

Sabit boyutlu bir arabellek bildirimine static değiştiricisinin dahil edilmesine izin verilmez.

Sabit boyutlu arabellek bildiriminin arabellek öğesi türü, bildirim tarafından tanıtılan arabelleklerin öğe türünü belirtir. Arabellek öğesi türü, önceden tanımlanmış türlerden sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double veya bool biri olmalıdır.

Arabellek öğesi türü, her biri yeni bir üyeyi tanıtır sabit boyutlu arabellek bildirimcilerinin bir listesi tarafından takip edilir. Sabit boyutlu arabellek bildirimcisi, üyeyi adlandıran bir tanımlayıcıdan, ardından [ ve ] belirteçleri içine alınmış sabit bir ifadeden oluşur. Sabit ifade, bu sabit boyutlu arabellek bildirimcisi tarafından tanıtılan üyedeki öğelerin sayısını belirtir. Sabit ifadenin türü örtük olarak türüne intdönüştürülebilir ve değer sıfır olmayan pozitif bir tamsayı olmalıdır.

Sabit boyutlu bir arabelleğin öğeleri, belleğe sıralı olarak yerleştirilmelidir.

Birden çok sabit boyutlu arabellek bildiren sabit boyutlu arabellek bildirimi, aynı özniteliklere ve öğe türlerine sahip tek bir sabit boyutlu arabellek bildiriminin birden çok bildirimine eşdeğerdir.

Örnek:

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

ile eşdeğer

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

son örnek

24.8.3 İfadelerde sabit boyutlu arabellekler

Sabit boyutlu bir arabellek üyesinin üye bulma işlemi (§12.5), tam olarak bir alanın üye bulma işlemi gibi gerçekleşir.

Sabit boyutlu arabelleğe simple_name (§12.8.4), member_access (§12.8.7) veya element_access (§12.8.12) kullanılarak ifadede başvurulabilir.

Sabit boyutlu bir arabellek üyesine basit bir ad olarak başvurulduğunda, etkisi formun this.Iüye erişimiyle aynıdır; burada I sabit boyutlu arabellek üyesidir.

Bir üye erişimi olan E.I formunda ve E. örtük this. olabilecek şekilde, eğer E bir yapı türündeyse ve bu yapı türündeki bir üye araması I sabit boyutlu bir üyeyi tanımlıyorsa, ardından E.I değerlendirilir ve aşağıdaki gibi sınıflandırılır:

  • İfade E.I güvenli olmayan bir bağlamda oluşmazsa derleme zamanı hatası oluşur.
  • E Değer olarak sınıflandırılırsa derleme zamanı hatası oluşur.
  • Aksi takdirde, taşınabilir bir değişken (E) ise:
    • İfade E.I bir fixed_pointer_initializer (§24.7) ise, ifadenin sonucu içindeki Isabit boyutlu arabellek üyesinin E ilk öğesinin işaretçisidir.
    • Aksi takdirde, ifade E.I formun element_access (§12.8.12.12) içinde bir primary_expression (E.I[J]) ise sonucuE.I, içindeki Psabit boyutlu arabellek üyesinin I ilk öğesi için bir işaretçidir Eve kapsayan element_accesspointer_element_access (§24.6.4) P[J]olarak değerlendirilir.
    • Aksi takdirde bir derleme zamanı hatası oluşur.
  • Aksi takdirde, E sabit bir değişkene başvurur ve ifadenin sonucu, içindeki Isabit boyutlu arabellek üyesinin E ilk öğesine yönelik bir işaretçidir. Sonuç S* türündedir; burada S, I'nin öğe türüdür ve bir değer olarak sınıflandırılır.

Sabit boyutlu arabelleğin sonraki öğelerine ilk öğeden işaretçi işlemleri kullanılarak erişilebilir. Dizilere erişimin aksine, sabit boyutlu arabellek öğelerine erişim güvenli olmayan bir işlemdir ve aralık işaretli değildir.

Örnek: Aşağıdakiler sabit boyutlu arabellek üyesine sahip bir yapı bildirir ve kullanır.

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

son örnek

24.8.4 Kesin atama denetimi

Sabit boyutlu arabellekler kesin atama denetimine (§9.4) tabi değildir ve sabit boyutlu arabellek üyeleri, yapı türü değişkenlerinin kesin atama denetimi amacıyla yoksayılır.

Sabit boyutlu arabellek üyesinin en dıştaki yapı değişkeni statik değişken, sınıf örneğinin örnek değişkeni veya dizi öğesi olduğunda, sabit boyutlu arabelleğin öğeleri otomatik olarak varsayılan değerlerine (§9.3) başlatılır. Diğer tüm durumlarda, sabit boyutlu bir arabelleğin ilk içeriği tanımlanmamıştır.

24.9 Yığın ayırma

İşleci hakkında genel bilgi için bkzstackalloc . Burada, bu işlecin bir işaretçiye neden olma özelliği tartışılır.

bir stackalloc_expression bir local_variable_declaration (§13.6.2) başlatma ifadesi olarak gerçekleştiğinde, burada local_variable_type bir işaretçi türü (§24.3) veya çıkarılır (var ), stackalloc_expression sonucu türündeki T*bir işaretçidir; burada Tstackalloc_expression unmanaged_type. Bu durumda sonuç, ayrılan bloğun başlangıcına işaret eden bir işaretçidir.

Örnek:

unsafe 
{
    // Memory uninitialized
    int* p1 = stackalloc int[3];
    // Memory initialized
    int* p2 = stackalloc int[3] { -10, -15, -30 };
    // Type int is inferred
    int* p3 = stackalloc[] { 11, 12, 13 };
    // Can't infer context, so pointer result assumed
    var p4 = stackalloc[] { 11, 12, 13 };
    // Error; no conversion exists
    long* p5 = stackalloc[] { 11, 12, 13 };
    // Converts 11 and 13, and returns long*
    long* p6 = stackalloc[] { 11, 12L, 13 };
    // Converts all and returns long*
    long* p7 = stackalloc long[] { 11, 12, 13 };
}

son örnek

Array'lere veya stackalloc'e türündeki bloklara erişimden farklı olarak, işaretçi türündeki Span<T>'e blokunun öğelerine erişim güvenli olmayan bir işlemdir ve aralık kontrolü yapılmaz.

Örnek: Aşağıdaki kodda

class Test
{
    static string IntToString(int value)
    {
        if (value == int.MinValue)
        {
            return "-2147483648";
        }
        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));
    }
}

Bir stackalloc ifadesi, yığında 16 karakterlik bir arabellek ayırmak için IntToString yönteminde kullanılır. Yöntem döndüğünde arabellek otomatik olarak silinir.

Ancak, IntToString güvenli modda; yani işaretçi kullanmadan aşağıdaki gibi yeniden yazılabilir:

class Test
{
    static string IntToString(int value)
    {
        if (value == int.MinValue)
        {
            return "-2147483648";
        }
        int n = value >= 0 ? value : -value;
        Span<char> buffer = stackalloc char[16];
        int idx = 16;
        do
        {
            buffer[--idx] = (char)(n % 10 + '0');
            n /= 10;
        } while (n != 0);
        if (value < 0)
        {
            buffer[--idx] = '-';
        }
        return buffer.Slice(idx).ToString();
    }
}

son örnek

Koşullu normatif metnin sonu.