C# Language
pekare
Sök…
Anmärkningar
Pekare och unsafe
På grund av sin natur producerar pekare overifierbar kod. Således kräver användning av vilken som helst pekartyp ett unsafe
sammanhang.
Typen System.IntPtr
är ett säkert omslag runt ett void*
. Det är avsett som ett mer bekvämt alternativ till void*
när ett osäkra sammanhang inte annars krävs för att utföra uppgiften.
Odefinierat beteende
Liksom i C och C ++ kan felaktig användning av pekare åberopa odefinierat beteende, med möjliga biverkningar är minneskorruption och exekvering av oavsiktlig kod. På grund av att de flesta pekareoperationer inte kan verifieras är korrekt användning av pekare helt ansvarig för programmeraren.
Typer som stöder pekare
Till skillnad från C och C ++ har inte alla C # -typer motsvarande pekartyper. En typ T
kan ha en motsvarande pekartyp om båda följande kriterier gäller:
-
T
är en strukturtyp eller en pekartyp. -
T
innehåller endast medlemmar som uppfyller båda dessa kriterier rekursivt.
Pekare för array-åtkomst
Detta exempel visar hur pekare kan användas för C-liknande åtkomst till C # -matriser.
unsafe
{
var buffer = new int[1024];
fixed (int* p = &buffer[0])
{
for (var i = 0; i < buffer.Length; i++)
{
*(p + i) = i;
}
}
}
Det unsafe
nyckelordet krävs eftersom pekaråtkomst inte släpper ut några gränskontroller som normalt släpps ut när du använder C # -matriser på det vanliga sättet.
Det fixed
nyckelordet berättar för C # -kompileraren att avge instruktioner för att fästa objektet på ett undantagssäkert sätt. Fästning krävs för att säkerställa att avfallssamlaren inte kommer att flytta matrisen i minnet, eftersom det skulle ogiltiga alla pekare som pekar i matrisen.
Pekare aritmetiska
Tillsats och subtraktion i pekare fungerar annorlunda än heltal. När en pekare ökas eller minskas ökar eller minskas adressen den pekar på med referensstorleken.
Exempelvis har typen int
(alias för System.Int32
) en storlek på 4. Om en int
kan lagras i adress 0 kan den efterföljande int
lagras i adress 4 och så vidare. I kod:
var ptr = (int*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 4
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
På liknande sätt har typen long
(alias för System.Int64
) en storlek på 8. Om en long
kan lagras i adress 0, kan den efterföljande long
lagras i adress 8, och så vidare. I kod:
var ptr = (long*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 16
Typen void
är speciell och void
pekare är också speciell och de används som catch-alla pekare när typen inte är känd eller spelar ingen roll. På grund av deras storlek-agnostiska karaktär, kan void
pekare inte ökas eller minskas:
var ptr = (void*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
Asterisken är en del av typen
I C och C ++ är asterisken i deklarationen för en pekvariabel en del av uttrycket som deklareras. I C # är asterisken i deklarationen en del av typen .
I C, C ++ och C # förklarar följande utdrag en int
pekare:
int* a;
I C och C ++ förklarar följande utdrag en int
pekare och en int
variabel. I C # förklarar det två int
pekare:
int* a, b;
I C och C ++ förklarar följande utdrag två int
pekare. I C # är det ogiltigt:
int *a, *b;
tomhet*
C # ärver från C och C ++ användningen av void*
som en typ-agnostisk och storlek-agnostisk pekare.
void* ptr;
Vilken som helst pekartyp kan tilldelas void*
hjälp av en implicit konvertering:
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
Det omvända kräver en uttrycklig konvertering:
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
int* p2 = (int*)ptr;
Medlemsåtkomst med ->
C # ärver från C och C ++ användningen av symbolen ->
som ett sätt att få åtkomst till medlemmarna i en instans genom en typpekare.
Tänk på följande struktur:
struct Vector2
{
public int X;
public int Y;
}
Detta är ett exempel på användningen av ->
att få åtkomst till sina medlemmar:
Vector2 v;
v.X = 5;
v.Y = 10;
Vector2* ptr = &v;
int x = ptr->X;
int y = ptr->Y;
string s = ptr->ToString();
Console.WriteLine(x); // prints 5
Console.WriteLine(y); // prints 10
Console.WriteLine(s); // prints Vector2
Generiska pekare
De kriterier som en typ måste uppfylla för att stödja pekare (se anmärkningar ) kan inte uttryckas i form av generiska begränsningar. Därför misslyckas alla försök att förklara en pekare till en typ som tillhandahålls genom en generisk typparameter.
void P<T>(T obj)
where T : struct
{
T* ptr = &obj; // compile-time error
}