MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API

本文继续讲解数组、分支、循环、使用不安全代码和如何调用Win32 API

数组

本程序分配一个int 型的数组并给他的元素赋值,然后打印出元素和数组的长度。

命令:

  • newarr type 生成一个元素类型为type 的数组。数组的大小必须在调用该命令前装入堆栈。该命令会把一个数组的引用装入堆栈。
  • stelem.i4 给一个数组成员赋值。数组的引用、下标和值必须在调用该命令前装入堆栈。
  • ldelema type 把数组元素的地址装入堆栈。数组的引用和下标必须在调用该命令前装入堆栈。地址用来调用非静态函数(参见后面)。
  • ldlen 把数组的长度装入堆栈。数组的引用必须在调用该命令前装入堆栈。
  • ldloca.s variable 把变量的地址装入堆栈。
  • ldc.i4.s value 把一个Int32 的常量装入堆栈(用于大于8 位的数)。
  • conv.i4 把堆栈中值转换成Int32 类型。
  • call instance function(arguments) 调用类的非静态函数。在调用一个非静态函数之前,我们必须把某个类的实例的地址和函数的参数装入堆栈。在本例中,地址通过ldelema ldloca 命令装入。

在本例的某些代码片段中,我写了一些注释,以说明堆栈在最后一个变量后的状态。在本例中,我们看到变量由编译器生成,该变量用来调用类的非静态函数。

代码:

.assembly Array1 {}

 

/*

// This program works as C# code:

 

int[] x = new int[5];

x[0] = 10;

x[1] = 20;

 

Console.WriteLine("x[0] = " + x[0].ToString());

Console.WriteLine("x[1] = " + x[1].ToString());

Console.WriteLine("Array length = " + x.Length.ToString());

*/

 

.method static public void main() il managed

{

    .entrypoint

    .maxstack 8

 

    .locals init ([0] int32[] x,

                  [1] int32 tmp)    // 由编译器生成

 

    // *****************************************************

    // x = new int[5];

    // *****************************************************

    ldc.i4.5                     // 把常量装入堆栈。

 

    // 生成数组,并把他的引用压入堆栈

    newarr     [mscorlib]System.Int32

 

    // 把数组从堆栈中取出,存入第0 个局部变量中

    stloc.0

 

    // *****************************************************

    // x[0] = 10;

    // *****************************************************

    ldloc.0           // 把第0 个局部变量装入堆栈(数组)

    ldc.i4.0          // 把常量0 装入堆栈(下标)

    ldc.i4.s   10     // 把常量10 装入堆栈(值)

    stelem.i4         // array[index] = value

 

    // 对数组的其余元素进行同样的操作……

 

    // ***************************************************

    // Console.WriteLine("x[0] = " + x[0].ToString());

    // ***************************************************

    ldstr      "x[0] = "            

                // 堆栈:"x[0] = "  ( 堆栈由局部变量表示)

    ldloc.0                         // 把第0 个变量装入堆栈

    ldc.i4.0                        // 把第1 个变量装入堆栈

                // 堆栈: "x[0] = " -> x -> 0

    // 把元素的地址装入堆栈

    ldelema    [mscorlib]System.Int32

                // 堆栈: "x[0] = " -> 指向一个Int32 的指针

                // 10

    // 调用实例函数System.Int32::ToString().

    call       instance string [mscorlib]System.Int32::ToString()

                // 堆栈: "x[0] = " -> "10"

    // 调用静态函数System.String::Concat(string, string)

    call       string [mscorlib]System.String::Concat

                                               ( string , string )

                // 堆栈: "x[0] = 10"

    // 调用静态函数 System.Console::WriteLine(string)

    call       void [mscorlib]System.Console::WriteLine( string )

                // 堆栈:

 

    // 对数组的其余元素进行同样的操作……

 

    // *****************************************************

    // Console.WriteLine("Array length = " + x.Length.ToString());

    // *****************************************************

    ldstr      "Array length = "  

                // 堆栈: "Array length = "

    ldloc.0     // 把第0 个变量装入堆栈

                // 堆栈: "Array length = " -> x

    Ldlen        // 把数组的长度装入堆栈

                 // 堆栈: "Array length = " -> 5

    conv.i4      // 把栈顶的值转换为Int32 ,并把他装入堆栈

                // 堆栈: "Array length = " -> 5

    stloc.1      // 把刚才的值存入第1 个局部变量(tmp)

                // 堆栈: "Array length = "

    ldloca.s   tmp    // 把变量tmp 的地址装入堆栈

                 // 堆栈: "Array length = " -> &tmp

    call       instance string [mscorlib]System.Int32::ToString()

                // 堆栈: "Array length = " -> "5"

    call       string [mscorlib]System.String::Concat

                                       ( string , string )

                // 堆栈: "Array length = 5"

    call       void [mscorlib]System.Console::WriteLine( string )

                // 堆栈:

    ret

}


比较

本程序读取2 个数字并打印其最小值。

命令:

  • bge.s label 跳转至label 如果value1value 2. Values 1 2 必须在调用本命令前装入堆栈。
  • br.s label 跳转至label
  • box value type 把一个值类型转成一个Object ,并把该Object 的引用装入堆栈。

本程序的装箱由如下C# 程序引起: Console.WriteLine("{0:d}", z);
用这种形式就不会引起装箱: Console.WriteLine(z.ToString()); .

代码:

.assembly Compare {}

/*

            int x, y, z;

            string s;

 

            Console.WriteLine("Enter x:");

            s = Console.ReadLine();

            x = Int32.Parse(s);

 

            Console.WriteLine("Enter y:");

            s = Console.ReadLine();

            y = Int32.Parse(s);

 

            if ( x < y )

                z = x;

            else

                z = y;

 

            Console.WriteLine("{0:d}", z);

*/

 

.method static public void main() il managed

{

    .entrypoint

    .maxstack 8

 

    .locals init ([0] int32 x,

                  [1] int32 y,

                  [2] int32 z,

                   [3] string s)

 

    // *****************************************************

    // Console.WriteLine("Enter x:");

    // *****************************************************

    ldstr      "Enter x:"                // 把字符串装入堆栈

    call  void [mscorlib]System.Console::WriteLine( string )

 

    // *****************************************************

    // s = Console.ReadLine();

    // *****************************************************

    call       string [mscorlib]System.Console::ReadLine()

    stloc.3                             // 保存到第3 个变量

 

    // *****************************************************

    // x = Int32.Parse(s);

    // *****************************************************

    ldloc.3                             // 把第3 个变量装入堆栈

    call       int32 [mscorlib]System.Int32::Parse( string )

    stloc.0                             // 保存到第0 个变量

 

    // y 进行相同的操作……

 

    // *****************************************************

    // 分支

    // if ( x >= y ) goto L_GR;

    // *****************************************************

     ldloc.0                     // x 装入堆栈(value 1)

    ldloc.1                     // y 装入堆栈(value 2)

    bge.s  L_GR                 // 跳转到 L_GR 如果value1value2

 

    // *****************************************************

    // z = x

    // *****************************************************

    ldloc.0                     // 把第0 个变量装入堆栈

    stloc.2                     // 保存到第2 个变量

 

    br.s       L_CONTINUE       // 跳转至 L_CONTINUE

 

L_GR:

 

    // *****************************************************

    // z = y

    // *****************************************************

    ldloc.1             // 把第1 个变量装入堆栈

    stloc.2             // 保存到第2 个变量

 

L_CONTINUE:

 

    // *****************************************************

    // Console.WriteLine("{0:d}", z);

     // 注意:这一行引起装箱操作

    // *****************************************************

    ldstr      "{0:d}"  // 把字符串装入堆栈

    ldloc.2             // 把第2 个变量装入堆栈 (z)

    box       [mscorlib]System.Int32   // Int32 变为Object

    call  void [mscorlib]System.Console::WriteLine( string , object )

 

    ret

}


数组2( 循环)

本程序用循环填充一个数组并打印其元素。这一次,我们增加一个静态函数ShowNumber(int) , 它在main 函数中调用。

命令:

  • blt.s label 跳转到label 如果value 1 小于 value 2. Values 1 2 必须在调用本命令之前装入堆栈。
  • ldelem.i4 把一个数组元素装入堆栈。数组引用和下标必须在调用本命令之前装入堆栈。
  • ldarga.s argument 把函数参数的地址装入堆栈。

我们可以看到,在本程序中,for 循环在MSIL 中用标签来实现。

代码:

.assembly Array2 {}

/*

 

            int[] px = new int[100];

            int i;

 

            for ( i = 1; i < 100; i++ )

            {

                px[i] = i + 1;

            }

 

            ShowNumber(px[5]);

            ShowNumber(px[10]);

 

        static void ShowNumber(int n)

        {

            Console.WriteLine(n.ToString());

        }

*/

 

.method static public void main() il managed

{

    .entrypoint

    .maxstack 8

 

    .locals init ([0] int32[] px,

                   [1] int32 i)

 

    // *****************************************************

    // x = new int[100]

    // *****************************************************

    ldc.i4.s   100                      // 把常量装入堆栈

    newarr     [mscorlib]System.Int32   // 分配一个Int32 型的数组

    stloc.0                             // 把它存入第0 个变量

 

    // *****************************************************

    // i = 1

    // *****************************************************

     ldc.i4.1                    // 把常量装入堆栈

    stloc.1                     // 把它存入第1 个变量

 

    br.s       CHECK_COUNTER    // 跳转到 CHECK_COUNTER

 

START_LOOP:

    // *****************************************************

    // px[i] = i + 1;

    // *****************************************************

    ldloc.0                     //   把第0 个变量装入堆栈

                                // 堆栈: px

    ldloc.1                     // 把第1 个变量装入堆栈

                                // 堆栈; px -> i

    ldloc.1                     // 把第1 个变量装入堆栈

                                // 堆栈: px -> i -> i

    ldc.i4.1                    // 把常量装入堆栈

                                // 堆栈: px -> i -> i -> 1.

    add                         // 2 个值相加

                                // 堆栈: px -> i -> i+1

                                 //        (array,index,value)

    stelem.i4                   // 把值存入数组元素

                                // 堆栈[index] = value

                                // 堆栈:

    // *****************************************************

    // i = i + 1

    // *****************************************************

    ldloc.1                     // 把第1 个变量装入堆栈

    ldc.i4.1                    // 把常量装入堆栈

    add                         // 相加

    stloc.1                     // 把值存入把第1 个变量

 

CHECK_COUNTER:

    // *****************************************************

    // 如果 i < 100 跳转到循环开始的地方

    // *****************************************************

    ldloc.1                     // 把第1 个变量装入堆栈

    ldc.i4.s   100              // 把常量装入堆栈

    blt.s      START_LOOP       // 如果value1<value2 调转至START_LOOP

 

    // *****************************************************

    // ShowNumber(px[5]

    // *****************************************************

    ldloc.0                     // 把第0 个变量装入堆栈

                                 // (array)

    ldc.i4.5                    // 把常量装入堆栈

                                // (index)

    ldelem.i4                   // 把数组元素装入堆栈

    call       void ShowNumber(int32)   // 调用 ShowNumber

 

    // *****************************************************

    // ShowNumber(px[10]

    // *****************************************************

    ldloc.0

    ldc.i4.s   10

    ldelem.i4

    call       void ShowNumber(int32)

 

    ret

}

 

.method static public void   ShowNumber(int32 n) il managed

{

  .maxstack  1

  ldarga.s   n          // 把第n 个参数的地址装入堆栈

  call       instance string [mscorlib]System.Int32::ToString()

  call       void [mscorlib]System.Console::WriteLine( string )

 

  ret

}


不安全代码

本程序通过unsafe 指针填充和打印一个int 型数组。

在本程序中,我们将看到新的类型:int32* int32& 。使用关键字pinned 可以阻止GC 移动由局部指针变量指向的对象。

命令:

  • dup 在堆栈上复制一个值。
  • stind.i4 存储值的地址。地址和值必须在调用本命令之前装入堆栈。

Code:

.assembly Unsafe {}

/*

int[] nArray = new int[5];

int i;

int* pCurrent;

 

fixed ( int* pArray = nArray )

{

    pCurrent = pArray;

 

    for ( i = 0; i < 5; i++ )

    {

        *pCurrent++ = i + 1;

    }

}

 

for ( i = 0; i < 5; i++ )

{

    Console.WriteLine(nArray[i].ToString());

}

 

*/

 

.method static public void main() il managed

{

    .entrypoint

    .maxstack 8

 

    .locals ([0] int32[] nArray,

             [1] int32 i,

             [2] int32* pCurrent,

             [3] int32& pinned pArray)  // GC 不会移动该指针指向的对象

 

    // *****************************************************

    // nArray = new int[5];

    // *****************************************************

    ldc.i4.5                            // 把常量5 装入堆栈                                        

    newarr     [mscorlib]System.Int32   // 生成数组 Int32[5]

    stloc.0                             // 存入第0 个变量

 

    // *****************************************************

    // pArray = nArray    (pArray = &nArray[0])

    // *****************************************************

    ldloc.0

               // 把第0 个变量装入堆栈(array)

    ldc.i4.0

               // 把常量0 装入堆栈(index)

    ldelema    [mscorlib]System.Int32

               // array[index] 装入堆栈

    stloc.3

               // 存入第3 个局部变量

 

    // *****************************************************

    // pCurrent = pArray;

    // *****************************************************

    ldloc.3                     // 把第3 个变量装入堆栈

    conv.i                      // 转变为int

    stloc.2                     // 存入第2 个变量

 

    // *****************************************************

    // i = 0

    // *****************************************************

    ldc.i4.0                    // 把常量0 装入堆栈

    stloc.1                     // 存入第1 个变量

 

    // *****************************************************

    // 跳转到 CHECK_COUNTER

    // *****************************************************

    br.s       CHECK_COUNTER

 

START_LOOP:

    // *****************************************************

    // *pCurrent++ = i + 1                              

    // *****************************************************

    // 1) 保存pCurrent 到堆栈,然后累加pCurrent

    ldloc.2

          // 把第2 个变量装入堆栈              [pCurrent]

    dup

          // 复制栈顶的值

          //                                [pCurrent pCurrent]

    ldc.i4.4

          // 把常量4 装入堆栈                [pCurrent pCurrent 4]

    add

          // 相加                           [pCurrent pCurrent + 4]

    stloc.2

          // 存入第2 个变量                 [pCurrent]

         // 译注:因为int 型指针是4 位的,所以加pCurrent+4==*pCurrent++

 

    // 2) (i+1) 保存到pCurrent

    ldloc.1

          // 把第1 个变量装入堆栈              [pCurrent i]

    ldc.i4.1

          // 把常量1 装入堆栈                    [pCurrent i 1]

    add   // 相加                            [pCurrent i+1]

   //                                   地址     

    stind.i4

          // i+1 的值的地址存入pCurrent   [empty]

 

    // *****************************************************

    // i = i + 1

    // *****************************************************

    ldloc.1             // 把第1 个变量装入堆栈

    ldc.i4.1             // 把常量1 装入堆栈

    add                 // 相加

    stloc.1             // 存入第1 个变量

 

CHECK_COUNTER:

 

    // *****************************************************

    // 如果i < 5 跳转至 START_LOOP;

    // *****************************************************

    ldloc.1                     // 把第1 个变量装入堆栈

    ldc.i4.5                    // 把常量5 装入堆栈

    blt.s      START_LOOP       // 如果i<5 跳转至START _ LOOP

 

    // *****************************************************

    // pArray = 0               fixed 块结束

    // *****************************************************

    ldc.i4.0                    // 把常量0 装入堆栈

    conv.u                      // 转变为unsigned int ,并压入堆栈

    stloc.3                     // 存入第3 个变量

 

    // 打印数组元素……

 

    ret

}


PInvoke

本程序使用Win32 API GetComputerName MessageBox 显示计算机的名字。APIMSIL 声明形式如下:

.method public hidebysig static pinvokeimpl("kernel32.dll"

                                             autochar winapi)

        int32  GetComputerName(

               class [mscorlib]System.Text.StringBuilder

                                      marshal( lptstr) buffer,

               int32& size) cil managed preservesig

{

}

 

.method public hidebysig static pinvokeimpl("User32.dll"

                                             autochar winapi)

        int32   MessageBox( native int hWnd,

                          string   marshal( lptstr) lpText,

                          string   marshal( lptstr) lpCaption,

                          int32 uType) cil managed preservesig

{

}

其调用规则与其他函数一致。

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值