RuntimeMapMaker3D-Pro
C#在有限的范围内支持指针。C#的指针只不过是一个持有另一类型内存地址的变量。但是在C#中,指针只能被声明为持有值类型和数组的内存地址。与引用类型不同,指针类型不被默认的垃圾收集机制所跟踪。出于同样的原因,指针不允许指向引用类型,甚至不允许指向包含引用类型的结构类型。我们可以说,指针只能指向非托管类型,包括所有基本数据类型、枚举类型、其他指针类型和只包含非托管类型的结构。
声明指针类型的一般形式如下所示,
type *variable_name;
其中 * 称为取消引用运算符。例如下面的语句
int *x ;
声明一个指针变量x,它可以保存一个int类型的地址。引用操作符(&)可以用来获取一个变量的内存地址。
int x = 100;
&x 给出了变量 x 的内存地址,我们可以将它分配给一个指针变量
int* ptr = &x;.
Console.WriteLine((int)ptr) // Displays the memory address
Console.WriteLine(*ptr) // Displays the value at the memory address.
C#语句可以在安全或不安全的情况下执行。通过使用关键字unsafe标记为不安全的语句在垃圾收集器的控制之外运行。记住,在C#中,任何涉及指针的代码都需要一个不安全的上下文。
我们可以通过两种不同的方式使用 unsafe 关键字。它可以用作方法、属性和构造函数等的修饰符。例如
// Author: [email protected]
using System;
class MyClass
{
public unsafe void Method()
{
int x = 10;
int y = 20;
int* ptr1 = &x;
int* ptr2 = &y;
Console.WriteLine((int)ptr1);
Console.WriteLine((int)ptr2);
Console.WriteLine(*ptr1);
Console.WriteLine(*ptr2);
}
}
class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc.Method();
}
}
关键字unsafe也可以用来标记一组语句为不安全,如下图所示。
// Author: [email protected]
//unsafe blocks
using System;
class MyClass
{
public void Method()
{
unsafe
{
int x = 10;
int y = 20;
int* ptr1 = &x;
int* ptr2 = &y;
Console.WriteLine((int)ptr1);
Console.WriteLine((int)ptr2);
Console.WriteLine(*ptr1);
Console.WriteLine(*ptr2);
}
}
}
class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc.Method();
}
}
在垃圾收集过程中,C#垃圾收集器可以随意移动内存中的对象。C#提供了一个特殊的关键字fixed来告诉垃圾收集器不要移动一个对象。这意味着这在内存中固定了值类型所指向的位置。这就是C#中所谓的固定住。
固定语句通常通过生成向垃圾收集器描述的表来实现,哪些对象将在可执行代码的哪些区域中保持固定。因此,只要在执行固定语句期间实际上没有发生垃圾收集器进程,与此相关的成本就非常低。但是,当垃圾收集器进程确实发生时,固定对象可能会导致堆碎片化。因此,只有在绝对必要时才应该Fixed对象,并且只能在最短的时间内Fixed。
这些点可以作为参数传递给一个方法,如下所示。这些方法也可以返回一个指针。
// Author: [email protected]
using System;
class MyClass
{
public unsafe void Method()
{
int x = 10;
int y = 20;
int* sum = swap(&x, &y);
Console.WriteLine(*sum);
}
public unsafe int* swap(int* x, int* y)
{
int sum;
sum = *x + *y;
return ∑
}
}
class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc.Method();
}
}
在C#中,指针类型不继承于对象,指针类型和对象之间不存在转换。这意味着指针不支持装箱和拆箱。但是C#支持不同的指针类型和指针类型与整型之间的转换。
C# 支持不安全上下文中的隐式和显式指针转换。隐式转换是
任何显式类型转换都需要强制转换运算符 (())。显式类型转换是
例如
char c = 'R';
char* pc = &c;
void* pv = pc; // Implicit conversion
int* pi = (int*) pv; // Explicit conversion using casting operator
在非安全的上下文中,++ 和 - 运算符可以应用于除 void * 类型之外的所有类型的指针变量。因此,对于每个指针类型 T*,以下运算符都被隐式重载。
T* operator ++ (T *x);
T* operator -- (T *x);
对于 T* 类型的指针变量,++ 运算符将 sizeof(T) 添加到变量中包含的地址,- 运算符从变量中包含的地址中减去 sizeof(–)。
在非安全的上下文中,可以从指针变量中添加或减去常量。类似地,可以从另一个指针变量中减去一个指针变量。但是在 C# 中不能添加两个指针变量。
在非安全的上下文中 ==, ! =、<、>、< =、> = 运算符可以应用于所有指针类型的值类型。指针变量与常量或另一个指针变量的乘法和除法在 C# 中是不可能的。
在非安全的上下文中,局部声明可能包括栈分配初始化程序,它从调用栈分配内存。
stackalloc T[E] 要求 T 是非托管类型,E 是 int 类型的表达式。上面的构造从调用栈中分配 E * sizeof(T) 个字节,并生成一个指向新分配块的 T* 类型的指针。 E 为负,抛出 System.OverFlowException。如果没有足够的内存可供分配,则会引发 System.StackOverflowException。
新分配内存的内容未定义。无法隐式释放使用 stackalloc 分配的内存。相反,一旦函数返回,所有栈分配的内存块都会自动丢弃。
class Test
{
char *buffer =
}
在 C# 中,可以使用指针符号访问数组元素。
// Author: [email protected]
using System;
class MyClass
{
public unsafe void Method()
{
int[] iArray = new int[10];
for (int count = 0; count < 10; count++)
{
iArray[count] = count * count;
}
fixed (int* ptr = iArray)
Display(ptr);
//Console.WriteLine(*(ptr+2));
//Console.WriteLine((int)ptr);
}
public unsafe void Display(int* pt)
{
for (int i = 0; i < 14; i++)
{
Console.WriteLine(*(pt + i));
}
}
}
class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc.Method();
}
}
C# 中的结构是值类型。如果结构仅包含值类型作为其成员,则指针可以与结构一起使用。例如
// Author: [email protected]
using System;
struct MyStruct
{
public int x;
public int y;
public void SetXY(int i, int j)
{
x = i;
y = j;
}
public void ShowXY()
{
Console.WriteLine(x);
Console.WriteLine(y);
}
}
class MyClient
{
public unsafe static void Main()
{
MyStruct ms = new MyStruct();
MyStruct* ms1 = &ms;
ms1->SetXY(10, 20);
ms1->ShowXY();
}
}