Struct 作为 Pointer Type的使用实例

来自MSDN。(更新:标题不准确,msdn指的pointer type是 “基础类型 *” 格式定义的变量类型,其中基础类型(referent type)可以是非托管类或者只包含非托管类型的自定义struct,因为pointer type不归GC管,所以不允许有reference type参与其中或者属于用户自定义struct。

参考http://books.google.com.hk/books?id=g6axWRRpJZwC&pg=PA712&lpg=PA712&dq=Any+user-defined+struct+type+that+contains+fields+of+unmanaged+types&source=bl&ots=Hy5jfiHo5K&sig=wCPiDy2c0pP4XfSkJcoMbKyHuWs&hl=zh-TW&sa=X&ei=rbwnU7ubCI-tiQezm4HwDw&ved=0CE0Q6AEwAw#v=onepage&q=Any%20user-defined%20struct%20type%20that%20contains%20fields%20of%20unmanaged%20types&f=false

下面的代码里的Node Struct就是满足条件的自定义struct,但是,另一方面,可以说是不相关的一点,我发现即使struct里含有string类型的成员和其他自定义struct, 直接试用时用reflector看到的仍然是根据地址来用的(也就是用ldloca之类的IL),也就是说它也是像pointer一样被使用的。没有用“基础类型 *“的格式就会被直接当作pointer type来用。从这个角度来说,标题又是对的。但是,这种情况下如果struct里含有string的reference,那个string能被GC吗?待验证。更新:string是immutable的特殊类,用weakReference.Alive来判断的话,GC.Collect()不会影响到它。用自己的类做实验,结果是可以被GC掉的。而这种含有managed成员的struct,就无法再用“基础类型 *“的格式来取得它的地址,它已经是作为managed Type的struct,:

Error 2 Cannot take the address of, get the size of, or declare a pointer to a managed type XXXXXX

所以,这种情况下的struct其实也不算可以作为point type。


这可真是c#和c++的一个大不同,这也解决了为什么用reflector看下列的代码(pointer和address操作必须用unsafe)

public   struct Node
   {
       public int Data;
   }
   class TestPointer
   {
       public unsafe Node* getNode(int i)
       {
           Node n = new Node();
           n.Data = i;
           Node* ptr = &n;
           return ptr;
       }

 

会生成c#版本的

[StructLayout(LayoutKind.Sequential)]
public struct Node
{
    public int Data;
}

public unsafe Node* getNode(int i)
{
    Node n;
    Node* ptr;
    Node* CS$1$0000;
    n = new Node();
    &n.Data = i;
    ptr = (IntPtr) &n;
    CS$1$0000 = ptr;
Label_0019:
    return CS$1$0000;
}

在n.Data = i;这句上,生成出的C#代码实际上是加了&(取地址)符号的。而如果你想在源代码里使用&n.Data=i,却不可以编译通过。所以,编译器在你访问struct的成员的时候会帮你标记出这是个指针访问(为什么不是变成n->Data呢,据说是reflector的C#语言反编译的语法bug?)看IL版本,实际还是指针访问

.method public hidebysig instance valuetype TestPointerForStruct.Node* getNode(int32 i) cil managed
{
    .maxstack 2
    .locals init (
        [0] valuetype TestPointerForStruct.Node n,
        [1] valuetype TestPointerForStruct.Node* ptr,
        [2] valuetype TestPointerForStruct.Node* CS$1$0000)
    L_0000: nop
    L_0001: ldloca.s n
    L_0003: initobj TestPointerForStruct.Node
    L_0009: ldloca.s n //把变量n的地址压栈
    L_000b: ldarg.1  //把第一个参数,也就是i,压栈
    L_000c: stfld int32 TestPointerForStruct.Node::Data  //成员赋值
    L_0011: ldloca.s n
    L_0013: conv.u
    L_0014: stloc.1
    L_0015: ldloc.1
    L_0016: stloc.2
    L_0017: br.s L_0019
    L_0019: ldloc.2
    L_001a: ret
}

 

对比类的赋值源代码:

  m = new Myclass();
   m.Name = "23";

生成的IL是:
L_003a: newobj instance void TestPointerForStruct.Myclass::.ctor()
   L_003f: stloc.3
   L_0040: ldloc.3  //把第三个变量,这里也就是m,压栈
   L_0041: ldstr "23" //常量压栈
   L_0046: stfld string TestPointerForStruct.Myclass::Name 。//成员赋值

所以指针类型(值类型)和引用类型的成员访问(偏移访问)都是一样的IL,只是压栈的时候一个是用ldloca.s var,压的是var的地址,另一个直接压第x个变量ldloc.x

注:上面两段IL的主要区别就是ldloc和ldloca的区别,也就是一个a,address的区别。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值