本课将简单介绍指针以及它在 C #中的使用,不过本课程仅仅会涉及到一些指针方面的浅显知识,如果你对指针不是十分的熟悉,而你又偏偏希望在你的代码中使用指针,我们建议你更深入的了解它。幸运的是,在 C #中只有当程序运行速度是极其重要的时候才需要使用到指针。(大多数情况下,我们可以不去理会指针。)
** ** ** 指针符号 ** ** **
指针是一个保存其他类型数据存储地址的变量。在 C #中,指针只能指向值类型数据以及数组。
指针以如下的方式使用 * 隐式定义:
int *p;
有些程序员可能会把 * 直接放在类型名的后面,例如: int* p ,这种定义指针的方式和前一种方式工作情况完全相同。
上面的定义将创建一个指针 p 。 p 将保存一个整数(占用 4 字节)的初始内存地址。
*p (在 p 前加上 * 前缀)这种复合语法元素可以用来代表由 p 指向的内存中存放的实际数据。因此如果给出声明后, *p 能够出现在赋值号的左边,如下所示:
*p = 5 ;
这行代码将 5 赋值给由上面的声明语句初始化的整数。不过,对于如下的赋值语句你可能会感到迷惑:
p = 5 ;
这条语句的效果是改变 p 保存的内存地址,它并不会改变由初始化声明语句初始化的整数值。这仅仅意味着 p 将不再指向这个整数。事实上, p 现在将指向由内存地址 5 开始的连续的 4 个字节( int 类型占用的内存位数)。
使用指针的另一个重要的符号是地址运算符 & ,它会返回变量存储的内存地址。下面是使用这个符号的一个例子,这个例子创建了一个指向整型变量 i 的指针 p :
int i = 5;
int *p;
p = &i
在上面的代码的基础上,代码
*p = 10 ;
将改变变量 i 的值为 10 。因为 *p 能被理解为保存在由 p 指向的内存中的整数。
对于指向结构类型的指针还有另外一个重要的符号 -> 。我们可以声明一个指向结构类型的指针,如下所示:(下面的例子中使用到了 ’Coords’ 结构,后面的代码中还会使用到)
Coords x = new Coords();
Coords *y = &x
然后我们可以使用定义的指针 y 访问 x 的一个公共成员(例如 z )。我么可以使用如下的两种方式:
(*y).z ;
y -> x ; // 使用到符号 ->
** 不安全代码 ** ** **
在 C #中使用指针的一个主要问题是 C #管理环境垃圾收集。在清理内存空间时,垃圾收集器可能在不加警告的情况下改变一个现存类型的存储位置。这时以前指向这个类型的指针将不能再指向它了。这样一种情况将会导致两种潜在的问题。其一,它可能危及到这个 C #程序的运行;其二,它可能影响到其他程序的完整性。
正因为上述问题, C #限制指针只能使用在由程序员明确声明为 ’unsafe’ 的代码中。因为不安全代码潜在的恶意使用问题,包含不安全代码的程序仅仅当他们能被完全信任时才能运行。
针对垃圾收集器移动数据存储位置的问题,你可以使用 ’fixed’ 表达式声明一个指针。这将固定指针指向的数据的位置,这样这个数据的存储位置将保持固定不变,不受垃圾收集器的影响。不过, ’fixed’ 语句只能使用在不安全代码中。
另外值得注意的是,任何在不安全代码中声明的值类型自动变为 ’fixed’ ,而且如果你对他们使用了 ’fixed’ 表达式,将会产生运行时错误。不过,这条规则并不适用于引用类型(数组)。
下面的代码给出了一个标记为 ’unsafe’ 的方法的例子,从前面的讲述中,我们知道第 9 行的指针 p 不能使用 ’fixed’ 语句声明,因为 p 指向的是一个已经在不安全代码中声明的结构类型 c 。
| |
1.
|
using System;
---|---
2.
|
public struct Coords
3.
|
{
4.
|
int x;
5.
|
int y;
6.
|
unsafe public static void Main()
7.
|
{
8.
|
Coords c = new Coords();
9.
|
Coords *p = &c
10.
|
{
11.
|
p->y = 6;
12.
|
(*p).x = 5;
13.
|
}
14.
|
Console.WriteLine(c.y);
15.
|
Console.WriteLine(c.x);
16.
|
}
17.
|
}
我们再看看下面的代码,在第 8 行的指针 p 必须使用 ’fixed’ 语句声明,因为它指向了一个不是在不安全代码段中声明的类型。
| |
1.
|
using System;
---|---
2.
|
public struct Coords
3.
|
{
4.
|
int x;
5.
|
int y;
6.
|
unsafe public static void notMain(ref Coords c)
7.
|
{
8.
|
fixed (Coords *p = &c)
9.
|
{
10.
|
p->y = 6;
11.
|
(*p).x = 5;
12.
|
}
13.
|
Console.WriteLine(c.y);
14.
|
Console.WriteLine(c.x);
15.
|
}
16.
|
}
在上面给出的例子中, ’unsafe’ 都是作为方法的修饰符,不过你也可以在一个代码段中使用它,下面的例子显示了这种用法:
| |
1.
|
using System;
---|---
2.
|
public static void Main()
3.
|
{
4.
|
unsafe
5.
|
{
6.
|
Coords c = new Coords();
7.
|
[...]
8.
|
}
9.
|
}
** 指针、方法和数组 ** ** **
虽然在上面的讲述中我们使用的指针都是指向一个值类型,但是指针也可以指向数组(引用类型)。(也有一些作者认为指针也可以指向字符串)
指针可以像下面的示例那样声明为指向一个数组:
int[] a = {4, 5};
int *b = a;
在这种情况下, b 保存的内存地址是数组 a 的第一个元素的位置。正如前所述,这个元素必须是一个值类型。下面的代码显示了如何使用一个指针改变数组的各个元素的值,对此的解释不在本文讨论范围之列。
| |
1.
|
using System;
---|---
2.
|
public class Tester
3.
|
{
4.
|
public static void Main()
5.
|
{
6.
|
int[] a = {4, 5};
7.
|
changeVal(a);
8.
|
Console.WriteLine(a[0]);
9.
|
Console.WriteLine(a[1]);
10.
|
}
11.
|
12.
|
public unsafe static void changeVal(int[] a)
13.
|
{
14.
|
fixed (int *b = a)
15.
|
{
16.
|
*b = 5;
17.
|
*(b + 1) = 7;
18.
|
}
19.
|
}
20.
|
<SPA