
3. ++、–运算符
这种算术操作常常用在数组或者字符串等值类型集合,比如下面代码:
fixed (int* ptr = new int[3] { 1, 2, 3 }) { }
fixed (char* ptr2 = "abcd") { }
首先ptr默认指向数组在堆上分配的首地址,也就是1的内存地址,当ptr++后会进入到下一个整形元素2的内存地址,再++后又进入下一个int的内存地址,也就是3,很简单吧,我举一个例子:
unsafe
{
fixed (int* ptr = new int[3] { 1, 2, 3 })
{
int* cptr = ptr;
Console.WriteLine(((long)cptr++).ToString("x16"));
Console.WriteLine(((long)cptr++).ToString("x16"));
Console.WriteLine(((long)cptr++).ToString("x16"));
}
}
0:000> !clrstack -l
LOCALS:
0x00000070c15fea50 = 0x000001bcaac82da0
0x00000070c15fea48 = 0x0000000000000000
0x00000070c15fea40 = 0x000001bcaac82dac
0x00000070c15fea38 = 0x000001bcaac82da8

一图胜千言哈,Console中的三个内存地址分别存的值是1,2,3哈, 不过这里要注意的是,C#是托管语言,引用类型是分配在托管堆中,所以堆上地址会存在变动的可能性,这是因为GC会定期回收内存,所以vs编译器需要你用fixed把堆上内存地址固定住来逃过GC的打压,在本例中就是 0x000001bcaac82da0 - (0x000001bcaac82da8 +4)
三:用两个案例帮你理解
古语说的好,一言不中,千言无用,你得拿一些例子活讲活用,好吧,准备两个例子。
1. 使用指针对string中的字符进行替换
我们都知道string中有一个replace方法,用于将指定的字符替换成你想要的字符,可是C#中的string是不可变的,你就是对它吐口痰它都会生成一个新字符串,🐮👃的是用指针就不一样了,你可以先找到替换字符的内存地址,然后将新字符直接赋到这个内存地址上,对不对,我来写一段代码,把abcgef 替换成 abcdef, 也就是将 g 替换为 d。
unsafe
{
//把 'g' 替换成 'd'
string s = "abcgef";
char oldchar = 'g';
char newchar = 'd';
Console.WriteLine($"替换前:{s}");
var len = s.Length;
fixed (char* ptr = s)
{
//当前指针地址
char* cptr = ptr;
for (int i = 0; i < len; i++)
{
if (*cptr == oldchar)
{
*cptr = newchar;
break;
}
cptr++;
}
}
Console.WriteLine($"替换后:{s}");
}
看输出结果没毛病,接下来用windbg去线程栈上找找当前有几个string对象的引用地址,可以在break处抓一个dump文件。










