Na wstępie należy powiedzieć, że używanie wskaźników w języku C# wymaga użycia tzn. unsafe context. Polecenia takie
wykonywane są poza kontrolą Garbage Collector.
C# pozwala na używanie wskaźników kiedy bloku kodu jest oznaczony jako unsafe. Kod niezabezpieczony lub też kod
niekontrolowany to blok kodu używający wskaźników.
Wskaźniki
Wskaźnik jest zmienną, której wartość to adres innej zmiennej, np. bezpośredni adres w pamięci. Podobnie do innych zmiennych lub stałych, wskaźnik
należy zadeklarować zanim zaczniemy przechowywać w nim adres danej zmiennej.
Poniżej przykładowa deklaracja wskaźników:
namespace Wskazniki
{
class Program
{
// oznaczamy blok kodu jako unsafe
// dodatkowo we właściwościach projektu, w zakładce Build
// należy zaznaczyć: "Allow unsafe code"
unsafe static void Main(string[] args)
{
int* ip;
double* dp;
float* fp;
char* chp;
}
}
}
Poniżej przykład użycia wskaźników wewnątrz bloku unsafe:
using System;
namespace Wskazniki
{
class Program
{
// oznaczamy blok kodu jako unsafe
// dodatkowo we właściwościach projektu, w zakładce Build
// należy zaznaczyć: "Allow unsafe code"
unsafe static void Main(string[] args)
{
int* ip;
//double* dp;
//float* fp;
//char* chp;
// Deklaracja zmiennej całkowitej
int number = 20;
// przypisanie do wskaźnika adresu pamięci zmiennej
// Wyrażenie &nazwa_zmiennej zwaraca nam adres pamięci tej zmiennej
ip = &number;
Console.WriteLine("Wartość liczby: {0}", number);
Console.WriteLine("Adres w pamięci: {0}", (int)ip);
Console.ReadKey();
// Wynik działania programu
// Wartosc liczby: 20
// Adres w pamieci: 119991236
}
}
}
Pobieranie wartości przy użyciu wskaźników
Dane przechowywane w lokalizacji wskazywanej przez zmienną wskaźnikową możesz pobrać używając metody ToString().
Poniższy przykład pokazuje taką możliwość:
using System;
namespace Wskazniki
{
class Program
{
// oznaczamy blok kodu jako unsafe
// dodatkowo we właściwościach projektu, w zakładce Build
// należy zaznaczyć: "Allow unsafe code"
unsafe static void Main(string[] args)
{
int* ip;
//double* dp;
//float* fp;
//char* chp;
// Deklaracja zmiennej całkowitej
int number = 20;
// przypisanie do wskaźnika adresu pamięci zmiennej
// Wyrażenie &nazwa_zmiennej zwaraca nam adres pamięci tej zmiennej
ip = &number;
Console.WriteLine("Wartość liczby: {0}", number);
// Pobieranie danych bezpośrednio ze wskaźnika
Console.WriteLine("Wartość liczby: {0}", ip->ToString());
Console.WriteLine("Adres w pamięci: {0}", (int)ip);
Console.ReadKey();
// Wynik działania programu
// Wartosc liczby: 20
// Wartosc liczby: 20
// Adres w pamieci: 119991236
}
}
}
Przekazywanie parametrów jako parametry metody
Można również przekazać wskaźnik do metody jako jej parametr.
Poniższy przykład pokazuje taką możliwość:
using System;
namespace WskaznikiParametrMetody
{
class Program
{
public unsafe void Swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
unsafe static void Main(string[] args)
{
Program pr = new Program();
int a = 10;
int b = 20;
int* ap = &a;
int* bp = &b;
Console.WriteLine("Przed zmianą: a = {0}, b = {1}", a, b);
pr.Swap(ap, bp);
Console.WriteLine("Po zmianie: a = {0}, b = {1}", a, b);
Console.ReadKey();
}
}
}
Dostęp do elementów tablicy przy użyciu wkaźników
W języku C#, nazwa tablicy oraz wskaźnik do takiego samego typu danych jak dane w tablicy, nie jest tym samym typem. Dla przykładu,
int* p oraz int[] p, nie są tego samego typu. Możesz zwiększać wartość wskaźnika
p ponieważ nie jest on stałą wartością w pamięci podczas gdy adres tablicy w pamięci jest wartością stałą i nie
może być zwiększony.
Dlatego, jeżeli chcesz uzyskać dostęp do danych tablicy przy użyciu wskaźników, należy ustalić wskaźnik w pamięci używając do tego słowa kluczowego
fixed.
Poniżej przykład ułatwiający zrozumienie problemu:
using System;
namespace WskaznikiArray
{
class Program
{
// Pamiętajcie o zakładce Build we właściwościach projektu
// i ustawieniu: "Allow unsafe code"
unsafe static void Main(string[] args)
{
// deklaracja tablicy
int[] tablica = { 100, 200, 300 };
// ustalamy wskaźnik naszej tablicy
fixed(int* ptr = tablica)
// adres tablicy przechowujemy we wskaźniku
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Adres tablicy[{0}] = {1}", i, (int)(ptr + i));
Console.WriteLine("Wartość tablicy[{0}] = {1}", i, *(ptr + i));
Console.WriteLine("Wartość/kolejny sposób: {0}", (ptr + i)->ToString());
}
Console.ReadKey();
// Wynik działania programu
// Adres tablicy[0] = 33420824
// Wartosc tablicy[0] = 100
// Wartosc/kolejny sposób: 100
// Adres tablicy[1] = 33420828
// Wartosc tablicy[1] = 200
// Wartosc/kolejny sposób: 200
// Adres tablicy[2] = 33420832
// Wartosc tablicy[2] = 300
// Wartosc/kolejny sposób: 300
}
}
}