我想在c#中尝试指针运算。我使用了一个asp.net网络应用程序,期望无论是这个还是控制台应用都没关系。
这是我试过的:
public class memorytest
{
public class Sample
{
public int A;
public int B;
public int C;
public int D;
}
public static unsafe void Main()
{
Sample s = new Sample {A = 1, B = 2, C = 3, D = 4};
int a = 1;
int b = 2;
int* pA = &a;
int* pB = &b;
Debug.WriteLine("{0:x16}",* pB);
Debug.WriteLine("{0:x16}",*(pB - 1));
Debug.WriteLine(*pA);
Debug.WriteLine("{0:x16}",*(pB - 2));
}
}
结果是这样的:
0000000000000002, 0000000004b5ca00, 1, 0000000004b5c9fc,
我显然希望第二个是数字1.有人理解这个吗?
答案 0 :(得分:10)
首先,如果你想了解堆栈是如何布局的,那么启动调试器并查看调试器中的堆栈似乎要容易得多。
假设由于某种原因你想继续编写程序来检查自己的堆栈状态:你过早地停止了试验。你应该写的程序是:
Debug.WriteLine("{0:x16}",*(pB - 1));
Debug.WriteLine("{0:x16}",*(pB + 0));
Debug.WriteLine("{0:x16}",*(pB + 1));
然后你就得到了输出
00000badf00dd00d <-- some pointer value
0000000000000002 <-- contents of b
0000000000000001 <-- contents of a
你会得知堆栈的增长方向与你认为的相反。在许多架构中,将某些东西推入堆栈减少了堆栈指针。
当然,正如其他人所指出的那样,我们不保证堆栈上的东西是如何布置的,或者即使东西首先进入堆栈也是如此;在某些情况下,局部变量可以放在堆上,并且可以注册其地址永远不会被占用的局部变量。例如,如果你不采用“a”的地址会发生什么?它是否仍然在堆栈中?也许不吧!
答案 1 :(得分:6)
此问题与ASP.net无关。你对指针算法的使用是完全错误的。
局部变量如何在堆栈上布局(或者如果它们甚至在堆栈上)几乎没有任何编程语言。
存储逻辑局部变量的一些复杂情况:
async
方法中的变量都在堆对象上。指针运算通常只能在一个分配的内存块中使用。
因此*(pB - 1)
取消引用b
前面的内存的结果是未指定的行为。
在.net中,您通常可以在数组内部使用指针算法或手动分配内存块。
答案 2 :(得分:4)
您的第二个引用是取消引用pB - 1的地址,而不是取消引用pB并减去1。
对于第二行返回值为1,需要读取如下:
Debug.WriteLine("{0:x16}",*(pB) - 1);
答案 3 :(得分:1)
我认为你不能或不应该对内存中的WHERE进行任何假设.NET会放置你的变量。在您的情况下,*pB
会正确解析为2
,因为这是b
的值。我认为您不能认为a
将在b
之前分配,*(pB - 1)
指向int
。您在堆栈上看到b
之前的任何一个{{1}}的整数值。
答案 4 :(得分:0)
当你写:
Debug.WriteLine("{0:x16}",*(pB - 1));
您正在获取pB - 1
的内存位置值。不是pB
减去1的值。
尝试使用:
Debug.WriteLine("{0:x16}",(*(pB) - 1));