谁能帮我代码签名个名?我这是VB程序源码

用VB 做一个抽签程序,在显示出数字的同时怎么数字的后面怎么跟上名字!求解!!!_百度知道
用VB 做一个抽签程序,在显示出数字的同时怎么数字的后面怎么跟上名字!求解!!!
我有更好的答案
Private Sub Command1_Click()Dim a(0 To 2) As Stringa(0) = &1、张三&a(1) = &2、李四&a(2) = &3、王五&Randomize Timer '改变随机数种子MsgBox a(Int((UBound(a) + 1) * Rnd))End Sub数组你自己定义,注意数组上下界限。如果要读取文本的话可以用for循环来循环赋值给数组。
采纳率:53%
什么名字msgbox cstr(i) & &名字&
你可以把名字存一个数组里,然后抽到哪个序号对应数组里的名字就可以了
为您推荐:
其他类似问题
您可能关注的内容
名字的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。期末秘籍|VB、C语言、C++、知识太多太复杂?编程大神带你划重点啦
VB、C语言、C++考试纷纷来临
面对浩如烟海的编程知识
从哪里着手复习一度令人头大
于是团子们邀请到了两位编程大佬
来为大家讲解编程类课程最重要的考点
快快拿出小本本
记下有用的复习知识吧
##大佬一号
7年C++经验,某安全厂商信息安全工程师
给初学者的建议(青铜段位)
针对广大的初学者,我个人并不建议抱着太多的教材一起阅读,而是应该找到一本入门级好书。在这里,我推荐C++ Primer,这是一本对初学者非常友好的教材。然鹅,只买了教材然后放在宿舍积灰也是不行的,在青铜段位最重要的就是练习。从最简单的Hello World,再到循环、判断、数组…必须要在学习一章之后做足够数量的编程练习。在练习的过程中,可能你就会发现,nani?!这章内容我看书明明都看会了,但为什么还是不会编呢??这个时候就要回到相应的地方再看。
还有一点,上述说的编程练习都是指在电脑上写真正的程序,要是手写代码的话……这个可不算。
在说完了学习的经验之后,还要给初学者提醒三点建议:
第一,注意代码格式。大家一定要参考书上的样例程序,注意不同层级的代码前面要加上不同数量的空格,这样整段代码是非常清晰的,如果有问题也能很快找到相应的地方。针对初学者来说,可能你会觉得打不打空格都一样,但如果你进阶到需要写1000行代码的时候,那你如果还习惯代码不分层,那我只能唱一首祝你平安……
第二,函数的大括号要一起打出来,然后再填中间的程序内容。跟上面一条异曲同工,这个看似奇葩的做法其实也是为了保证代码的可读性,同时也是为了让你在最后不落下任何一个大括号。如果初学阶段养成了这个习惯,以后的代码之路真的就畅通无阻惹。在这里要感谢我的信息学竞赛老师,当年在被老师无数次diss中终于把这件事当成了本能…
第三,先想想算法,再开始猛敲键盘。其实所谓的编程过程,就是要先想出来一个最佳的算法,再利用C艹课所学的语句把这个算法实现。因此,在你写完int main()之后,停下来先想想要用什么样的算法,或者可以在纸上先画个图,遇到嵌套的循环时可以在纸上画个表格,列举i=*、j=*时候程序里面的变量值都是多少。确定了正确的思路,就很难写不出来程序了。
在代码的道路上渐行渐远(白银段位)
到了这个段位,不掌握算法那是显然不行的。因此就一定要重视数据结构和算法原理这两方面的学习。尽管程序设计的题目千变万化,但用到的算法其实就这么几套。在这个阶段,除了看书之外,我建议大家要熟练运用网络,有的问题可能书上写得很复杂,但网上一查就发现有人用非常搞笑/直观/可爱的方式解答了相同的问题。不管你是不是单身,这个阶段在编写C++代码的时候都要考虑面向对象编程了,大家要熟悉对象的基本思想和三大特征,这是C++的灵魂所在。
不写代码是不可能的 这辈子都不可能的(黄金段位)
针对更高阶的同学,我首先建议大家选好自己的发展方向,也就是你到底要用C++来干什么?如果你以后对游戏比较感兴趣,那你就要试试用C++配合DirectX或者OpenGL来做个小游戏,平常看看计算机图形学什么的。如果你以后想要走研发路线,那你还得再深入学学更多的算法,看更厚的算法书。在这个阶段,必须要找一个方向深入的学下去,这样才能把这个具体的方向研究明白。
不过对于废铁段位的我们来说
眼前最重要的
就是如何顺利度过期末考试了
这不,团子们请到了第二位大神
帮大家梳理一些基础的C语言知识点~
##大佬二号
程序媛,大三在读,计算机二级C语言科目90+
在C语言的学习中,最令大家头痛的莫过于指针、数组、函数这几方面的知识了,面对马上要到来的期末考试,需要熟记以下知识点。
指针变量的本质是用来放地址,而一般的变量是放数值的。
1、int *p 中 *p和p的差别:简单说*p是数值,p是地址
*p可以当做变量来用;*的作用是取后面地址p里面的数值
p是当作地址来使用。可以用在scanf函数中:scanf(“%d”,p);
2、*p++ 和 (*p)++的之间的差别
*p++是 地址会变化。
口诀:取当前值,然后再移动地址。
(*p)++ 是数值会要变化。
口诀:取当前值,然后再使数值增加1。
例如:int *p,a[]={1,3,5,7,9};
请问*p++和(*p)++的数值分别为多少?
*p++: 这个本身的数值为1。由于是地址会增加一,所以指针指向数值3了。
(*p)++ 这个本身的数值为1。由于有个++表示数值会增加,指针不移动,但数值1由于自加了一次变成了2。
3、二级指针:
*p:一级指针:存放变量的地址。
**q:二级指针:存放一级指针的地址。
常考题目: int x=7;
int*p=&x,**q=p;
问:*p为多少?*q为多少?**q为多少?
答 : 7 , p , 7
再问 :**q=&x的写法可以吗?
再答:不可以,因为二级指针只能存放一级指针的地址。
4、三名主义:
数组名:表示第一个元素的地址。数组名不可以自加,他是地址常量名。
函数名:表示该函数的入口地址。
字符串常量名:表示第一个字符的地址。
5、移动指针
char *s=“meikanshu”
while(*s){printf(“%c”,*s);s++;}
这个s首先会指向第一个字母m然后通过循环会一次打印出一个字符,s++是地址移动,打印了一个字母后,就会移动到下一个字母!
6、指针变量两种初始化(一定要看懂)
方法一:int a=2,*p=&a;(定义的同时初始化)
方法二:int a=2,*p;(定义之后初始化) p=&a;
数组: 存放的类型是一致的。多个数组元素的地址是连续的。
1、一维数组的初始化:
int a[5]={1,2,3,4,5}; 合法
int a[5]={1,2,3, }; 合法
int a[]={1,2,3,4,5}; 合法,常考,后面决定前面的大小!
int a[5]={1,2,3,4,5,6}; 不合法,赋值的个数多余数组的个数了
2、一维数组的定义;
int a[5];注意这个地方有一个重要考点,定义时数组的个数不是变量一定是常量。
int a[5] 合法,最正常的数组
int a[1+1] 合法,个数是常量2,是个算术表达式
int a[1/2+4] 合法,同样是算术表达式
int x=5,int a[x];
不合法,因为个数是x,是个变量,非法的,
define P 5 int a[P]
合法,define 后的的P是符号常量,只是长得像变量
3、二维数组的初始化
int a[2][3]={1,2,3,4,5,6};
合法,很标准的二维的赋值。
int a[2][3]={1,2,3,4,5, };
合法,后面一个默认为0。
int a[2][3]={{1,2,3,} {4,5,6}};
合法,每行三个。
int a[2][3]={{1,2,}{3,4,5}};
合法,第一行最后一个默认为0。
int a[2][3]={1,2,3,4,5,6,7};
不合法,赋值的个数多余数组的个数了。
int a[][3]={1,2,3,4,5,6};
不合法,不可以缺省行的个数。
int a[2][]={1,2,3,4,5,6};
合法,可以缺省列的个数。
1)一维数组的重要概念:
对a[10]这个数组的讨论。
1、a表示数组名,是第一个元素的地址,也就是元素a[0]的地址。(等价于&a)
2、a是地址常量,所以只要出现a++,或者是a=a+2赋值的都是错误的。
3、a是一维数组名,所以它是列指针,也就是说a+1是跳一列。 
对a[3][3]的讨论。
1、a表示数组名,是第一个元素的地址,也就是元素a[0][0]的地址。
2、a是地址常量,所以只要出现a++,或者是a=a+2赋值的都是错误的。
3、a是二维数组名,所以它是行指针,也就是说a+1是跳一行。
4、a[0]、a[1]、a[2]也都是地址常量,不可以对它进行赋值操作,同时它们都是列指针,a[0]+1,a[1]+1,a[2]+1都是跳一列。
5、注意a和a[0] 、a[1]、a[2]是不同的,它们的基类型是不同的。前者是一行元素,后三者是一列元素。
2) 二维数组做题目的技巧:
如果有a[3][3]={1,2,3,4,5,6,7,8,9}这样的题目。
步骤一:把他们写成:      
第一列 第二列 第三列  
a[0]  1    2    3 一&第一行
a[1] 4   5    6  —&第二行
a[2] 7   8    9  一&第三行
步骤二:这样作题目间很简单:    
*(a[0]+1)我们就知道是第一行的第一个元素往后面跳一列,那么这里就是a[0][1]元素,所以是1。
*(a[1]+2)我们就知道是第二行的第一个元素往后面跳二列。那么这里就是a[1][2]元素,所以是6。
一定记住:只要是二维数组的题目,一定是写成如上的格式,再去做题目,这样会比较简单。
3) 数组的初始化,一维和二维的,一维可以不写,二维第二个一定要写
int a[]={1,2} 合法。
int a[][4]={2,3,4}合法。
但int a[4][]={2,3,4}非法。
4) 二维数组中的行指针
int a[1][2];
其中a现在就是一个行指针,a+1跳一行数组元素。
搭配(*)p[2]指针
a[0],a[1]现在就是一个列指针。
a[0]+1 跳一个数组元素。搭配*p[2]指针数组使用
5) 还有记住脱衣服法则:超级无敌重要
a[2] 变成 *(a+2)
a[2][3]变成 *(a+2)[3]再可以变成 *(*(a+2)+3)。
1、函数:是具有一定功能的一个程序块,是C语言的基本组成单位。
2、函数不可以嵌套定义。但是可以嵌套调用。
3、函数名缺省返回值类型,默认为 int。
4、C语言由函数组成,但有且仅有一个main函数,是程序运行的开始
5、如何判断a是否为质数:
void iszhishu( int a )
for(i=2;i&a/2;i++)
if(a%i==0) printf(“不是质数”);
printf(“是质数!”);
6、如何求阶乘:n!
int fun(int n)
for(i=1;i&=n;i++) p=p*i;
7、函数的参数可以是常量,变量,表达式,甚至是函数调用。
add(int x,int y){return x+y;}
{ int sum;
sum=add(add(7,8),9);
请问sum的结果是多少? 结果为24
8、一定要注意参数之间的传递
实参和形参之间 传数值,和传地址的差别。
(考试的重点)
传数值的话,形参的变化不会改变实参的变化。
传地址的话,形参的变化就会有可能改变实参的变化。
9、函数声明的考查:
一定要有:函数名,函数的返回类型,函数的参数类型。不一定要有:形参的名称。
int *fun(int a[] , int b[])
…………..
已经知道函数是这样。这个函数的正确的函数声明怎么写?
int *fun(int *a , int *b)
这里是函数声明的写法,注意数组就是指针
int *fun(int a[] , int b[])
这种写法也是正确的
int *fun(int b[] , int c[])
这种写法也是正确的,参数的名称可以随便写
int *fun(int * , int *)
这种写法也是正确的,参数的名称可以不写
10、局部变量和全局变量
①局部变量:在函数内部定义的变量,只在此函数内有效,函数外不饿能使用
②全局变量:在函数外定义的为外部变量,也成为全局变量,在函数外也可以使用。
了解了以上编程知识,
你对考试是否有了信心?
不管是VB、C语言、C++
还是Java、Python
团子祝大家全部逢考必过
早日成为轻松carry上千行代码的编程大神
头发茂密的那种!
特别感谢:侯绍博 张子莹
记者&编辑:刘玥 朱婧哲
责编:陈珍
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
今日搜狐热点&&&&& 我们用C#、VB.NET语言编写的代码最终都会被编译成程序集或IL。因此用VB.NET编写的代码可以在C#中修改,随后在COBOL中使用。因此,理解IL是非常有必要的。
&&&&& 一旦熟悉了IL,理解.NET技术就不会有障碍了,因为所有的.NET语言都会编译为IL。IL是一门中性语言。IL是先发明的,随后才有了C#、VB.NET等语言。
&&&&& 我们将在一个短而精辟的程序中展示IL。我们还假设读者至少熟悉一门.NET语言。
.method&void&vijay()&{}
&&&&& 随后,我们用IL编写了一个非常短小的IL程序——它显然是不能工作的,并将它命名为a.il。那么我们怎么才能把它编译为一个可执行程序呢?不需要为此而焦急,Microsoft提供了一个ilasm程序,它的唯一任务就是从IL文件中创建可执行文件。
&&&&& 在允许这个命令之前,要确保你的变量路径被设置为framework中的bin子目录。如果不是,请输入命令如下:
&&&&&&&&&&&&set path=c:\progra~1\microsoft.net\frameworksdk\%PATH%
&&&&& 现在,我们使用如下命令:
&&&&&&&&&&&&c:\il&ilasm /nologo /quiet a.il
&&&&& 这样做会生成下面的错误:
&&&&&&&&&&&&Source file is ANSI
&&&&&&&&&&&&Error: No entry point declared for executable
&&&&&&&&&&&&***** FAILURE *****
&&&&& 将来,我们将不会显示由ilasm生成的输出的第一行和最后一行。我们还将移除非空白行之间的空白行。
&&&&& 在IL中,允许我们使用句点.作为一行的开始,这是一条指令,要求编译器执行某个功能,如创建一个函数或类,等等。任何开始于句点的语句都是一条实际俄编译器指令。
&&&&& .method表示创建一个名为vijay的函数(或方法),并且这个函数返回void,即它不返回任何值。因为缺少较好的命名法则,函数名称vijay显得很随意。
&&&&& 汇编器显然理解不了这个程序,从而会显示“no entry point”的消息。这个错误信息的生成是因为IL文件能够包括无数的函数,而汇编器无法区分哪个会被首先被执行。
&&&&& 在IL中,首先被执行的函数被称为进入点(entrypoint)函数。在C#中,这个函数是Main。函数的语法是,名称之后是一对圆括号()。函数代码的开始和结束用花括号{}来表示。
.method&void&vijay()&{&&&&&&.entrypoint}
&&&&&&c:\il&ilasm /nologo /quiet a.il
&&&&&&Source file is ANSI
&&&&&&Creating PE file
&&&&&&Emitting members:
&&&&&&Global Methods: 1;
&&&&&&Writing PE file
&&&&&&Operation completed successfully
&&&&& 现在不会生成任何错误了。伪指令(directive)entrypoint表示程序执行必须开始于这个函数。在这个例子中,我们不得不使用这个伪指令,虽然事实上这个程序只有一个函数。当在DOS提示符中给出dir命令后,我们看到有3个文件会被创建。a.exe是一个可执行文件,现在可以执行它来看到程序的输出。
&&&&&&C:\il&a
&&&&&&Exception occurred: System.BadImageFormatException: Exception from HRESULT: 0x8007000B. Failed to load C:\IL\A.EXE.
&&&&& 当我们试图执行上面的程序时,我们的运气似乎不太好,因为会生成上面的运行时错误。一个可能的原因是,这个函数是不完整的,每个函数都应当具有一个“函数结束”指令在函数体中。我们匆忙之中显然没有注意到这个事实。
.method&void&vijay()&{&&&&&&.entrypoint&&&&&&ret}
&&&&& “函数结束”指令被称为ret。前面所有的函数都必须以这个指令作为结束。
&&&&&&Exception occurred: System.BadImageFormatException: Exception from HRESULT: 0x8007000B. Failed to load C:\IL\A.EXE.
&&&&& 在执行这个程序时,我们再次得到了相同的错误。这次我们的问题又在哪里呢?
.assembly&mukhi&{}.method&void&vijay()&{&&&&&&.entrypoint&&&&&&ret}
&&&&& 错误在于我们忘记在名称后面使用必不可少的伪指令assembly。我们将其合成在上面的代码中,并在一对空的花括号之后使用了名称mukhi。这个程序集伪指令用于给出程序的名称。它又被称为一个部署单元。
&&&&& 上面的代码是可以汇编而没有任何错误的最小的程序,虽然它在执行时并没有做什么有用的事情。它没有任何名为Main的函数。它只有一个带有entrypoint伪指令的函数vijay。现在汇编这个程序并运行而根本不会有任何错误。
&&&&& 在.NET中,程序集的概念是极其重要的,应该对其有彻底的认识。我们将在本章后半部分使用这个伪指令。
.assembly&mukhi&{}.method&void&vijay()&{&&&&&&.entrypoint&&&&&&ret}.method&void&vijay1()&{&&&&&&.entrypoint&&&&&&ret}
&&&&&&***** FAILURE *****
&&&&& 上面错误信息的原因是,上面的程序有2个函数,vijay和vijay1,每个函数都包括了.entrypoint伪指令。正如前面提到的那样,这个指令指定了关于那个函数会被首先执行。
&&&&& 因此,在功能上,它类似于C#中的Main函数。当C#代码被转换为IL代码时,在Main函数中包含的代码会被转换为IL中的函数中并包括.entrypoint伪指令。例如,如果在COBOL程序中执行的第一个函数被称为abc,那么在IL中生成的代码就会在这个函数中插入.entrypoint伪指令。
&&&&& 在常规的程序语言中,首先被执行的函数必须有一个特定的名称,例如Main,但是在IL中,只需要一个.entrypoint伪指令。因此,因为一个程序只能由一个开始点,所以在IL代码中只允许一个函数包括.entrypoint伪指令。
&&&&& 迫切地看到,没有生成任何错误消息编号或说明,使得调试这个错误非常困难。
.assembly&mukhi&{}.method&void&vijay()&{&&&&&&ret&&&&&&.entrypoint}
&&&&& .entrypoint伪指令需要被定位为函数中的第一个指令或最后一个指令。它仅出现在函数体中,从而将它的状态宣布为第一个被执行的函数。伪指令不是程序集指令,甚至可以被放置在任何ret指令之后。提醒你一下,ret表示函数代码的结束。
.assembly&mukhi&{}.method&void&vijay()&{&&&&&&.entrypoint&&&&&&call&void&System.Console::WriteLine()&&&&&&ret}
&&&&& 我们可能有一个用C#、VB.NET编写的函数,但是在IL中执行这个函数的机制是相同的。如下所示:
&&&&& 我们必须使用汇编指令调用。调用指令之后,按照给定的顺序,为以下详细内容:
函数的返回类型(void)命名空间(System)类 (Console)函数名称 (WriteLine())
&&&&& 函数被调用但不会生成任何输出。因为,我们传递一个参数到WriteLine函数中。
.assembly&mukhi&{}.method&void&vijay()&{&&&&&&.entrypoint&&&&&&call&void&System.Console::WriteLine(class&System.String)&&&&&&ret}
&&&&& 上面的代码有一处“闪光点”。当一个函数在IL中被调用时,除了它的返回类型之外,被传递的参数的数据类型,也必须被指定。我们将Writeline设置为——希望得到一个System.String类型作为参数,但是由于没有字符串被传递到这个函数中,所以它会生成一个运行时错误。
&&&&& 因此,在调用一个函数时,在IL和其他程序语言之间有一个明显的区别。在IL中,当我们调用一个函数,我们必须指定关于该函数我们所知道的任何内容,包括它的返回类型和它的参数的数据类型。通过在运行期间进行恰当的检查,保证了汇编器能够在语法上验证代码的有效性。
&&&&& 现在我们将看到如何将参数传递到一个函数中。
.assembly&mukhi&{}.method&void&vijay()&{&&&&&&.entrypoint&&&&&&ldstr&"hell"&&&&&&call&void&System.Console::WriteLine(class&System.String)&&&&&&ret}
&&&&&&hell
&&&&& 汇编器指令ldstr把字符串放到栈上。Ldstr的名称是文本"load a string on the stack"的缩写版本。栈是一块内存区域,它用来传递参数到函数中。所有的函数从栈上接收它们的参数。因此,像ldstr这样的指令是必不可少的。
.assembly&mukhi&{}.method&public&hidebysig&static&void&vijay()il&managed{&&&&&&.entrypoint&&&&&&ldstr&"hell"&&&&&&call&void&System.Console::WriteLine(class&System.String)&&&&&&ret}
&&&&&&hell
&&&&& 我们在方法vijay上添加了一些特性。接下来我们将逐个讲解它们。
&&&&& public:被称为可访问特性,它决定了都有谁可以访问一个方法。public意味着这个方法可以被程序的其他任何部分所访问。
&&&&& hidebysig:类可以从其它多个类中派生。hidebysig特性保证了父类中的函数在具有相同名称或签名的派生类中会被隐藏。在这个例子中,它保证了如果函数vijay出现在基类中,那么它在派生类中就是不可见的。
&&&&& static:方法可以是静态的或非静态的。静态方法属于一个类而不属于一个实例。因此, 就像我们只有一个单独的类,我们不能拥有一个静态函数的多份复制。静态函数可以在哪里创建是没有约束的。带有entrypoint指令的函数必须是静态的。静态函数必须具有相关联的实体或者源代码,并且使用类型名称而不是实例名称来引用它们。
&&&&& il managed:由于它的复杂性质,我们将关于这个特性的解释延后。当时机成熟时,它的功能将会被解释清楚。
&&&&& 上面涉及的特性并没有修改函数的输出。 稍后,你将明白为什么我们要提供这些特性的解释。
&&&&& 无论何时我们用C#语言编写一个程序,我们首先在类的名称前指定关键字class,随后,我们将源代码封闭在一对花括号内。示范如下:
class&zzz{}
&&&&& 让我们引进称为class的IL指令:
.assembly&mukhi&{}.class&zzz{&&&&&&.method&public&hidebysig&static&void&vijay()il&managed&&&&&&{&&&&&&&&&&&&.entrypoint&&&&&&&&&&&&ldstr&"hell"&&&&&&&&&&&&call&void&System.Console::WriteLine(class&System.String)&&&&&&&&&&&&ret&&&&&&}}
&&&&& 注意到,汇编器输出中的改变: Class 1 Methods: 1;
&&&&&&hell
&&&&& 伪指令.class之后是类的名称。它在IL中是可选的,让我们通过添加一些类的特性来增强这个类的功能。
.assembly&mukhi&{}.class&private&auto&ansi&zzz{&&&&&&.method&public&hidebysig&static&void&vijay()il&managed&&&&&&{&&&&&&&&&&&&.entrypoint&&&&&&&&&&&&ldstr&"hell"&&&&&&&&&&&&call&void&System.Console::WriteLine(class&System.String)&&&&&&&&&&&&ret&&&&&&}}
&&&&&&hell
&&&&& 我们添加了 3个特性到类的伪指令中。
private:这表示了对类的成员的访问被约束为只能在当前类中。auto:这表示类在内存中的布局将只由运行时来决定,而不是由我们的程序决定。ansi:源代码通常被划分为两个主要的类别:托管代码和非托管代码。
&&&&& 以诸如C语言编写的代码被称为非托管代码或不可信任的代码。我们需要一个特性来处理非托管代码和托管代码之间的互操作。例如,当我们想要在托管和非托管代码之间转移字符串时,这个特性会被使用到。
&&&&& 如果我们跨越托管代码的边界并钻进非托管代码的领域,那么一个字符串——由2字节Unicode字符组成的数组,将会被转换为一个ANSI字符串——由1字节ANSI字符组成的数组;反之亦然。修饰符ansi用于消除托管和非托管代码之间的转换。
.assembly&mukhi&{}.class&private&auto&ansi&zzz&extends&System.Object{&&&&&&.method&public&hidebysig&static&void&vijay()il&managed&&&&&&{&&&&&&&&&&&&.entrypoint&&&&&&&&&&&&ldstr&"hell"&&&&&&&&&&&&call&void&System.Console::WriteLine(class&System.String)&&&&&&&&&&&&ret&&&&&&}}
&&&&&&hell
&&&&& 类zzz从System.Object中派生。在.NET中,为了定义类型的一致性,所有的类型最终都派生于System.Object。因此,所有的对象都有一个共同的基类Object。在IL中,类从其它类中派生,与C++、C#和Java的表现方式相同,
.module&aa.exe.subsystem&<span style="color: #&.corflags&<span style="color: #.assembly&extern&mscorlib{&&&&&&.originator&=&(<span style="color: #&<span style="color: #&<span style="color: #&<span style="color: #&D3&A4&AE&<span style="color: #&)&&&&&&.hash&=&(<span style="color: #&<span style="color: #&F8&C9&<span style="color: #&1F&<span style="color: #&3F&<span style="color: #&D7&AB&AD&E2&DF&1D&E0&&&&&&&&&&&&&F2&9D&4F&BC&)&&&&&&.ver&<span style="color: #:<span style="color: #:<span style="color: #04:<span style="color: #}.assembly&a&as&"a"{&&&&&&.hash&algorithm&<span style="color: #x&&&&&&.ver&<span style="color: #:<span style="color: #:<span style="color: #:<span style="color: #}.class&private&auto&ansi&zzz&extends&System.Object{&&&&&&.method&public&hidebysig&static&void&vijay()&il&managed&&&&&&{&&&&&&&&&&&&.entrypoint&&&&&&&&&&&&ldstr&"hell"&&&&&&&&&&&&call&void&System.Console::WriteLine(class&System.String)&&&&&&&&&&&&ret}&&&&&&.method&public&hidebysig&specialname&rtspecialname&instance&void&.ctor()&il&managed&&&&&&{&&&&&&&&&&&&.maxstack&<span style="color: #&&&&&&&&&&&&ldstr&"hell1"&&&&&&&&&&&&call&void&System.Console::WriteLine(class&System.String)&&&&&&&&&&&&ldarg.0&&&&&&&&&&&&call&instance&void&[mscorlib]System.Object::.ctor()&&&&&&ret&&&&&&}}
&&&&&&hell
&&&&& 你一定想知道为什么我们会编写出这么难看的程序。在迷雾驱散之前你需要保持耐心,所有的一切就要开始有意义了。我们将逐个解释新引进的函数和特性。
&&&&& .ctor: 我们引进了一个新的函数.ctor,它调用了WriteLine函数来显示hell1,但是它没有被调用。.ctor涉及到了构造函数。
&&&&& rtspecialname: 这个特性会告诉运行时&#8212;&#8212;函数的名称是特殊的,它会以一种特殊的方式被对待。
&&&&& specialname: 这个特性会提示编译器和工具&#8212;&#8212;函数是特殊的。运行时可能选择忽略这个特性。
&&&&& instance: 一个常规的函数会被一个实例函数调用。这样一个函数与一个对象关联,不同于静态方法,后者关联到一个类。
&&&&& 在合适的时候,为函数选择特定名称的原因会变得明朗。
&&&&& ldarg.0: 这是一个汇编器指令,它加载this指针或第0个参数的地址到执行栈上。我们随后将详细解释ldarg.0。
&&&&& mscorlib: 在上面的程序中,函数.ctor会被基类System.Object调用。通常,函数的名称以包括代码的库的名称作为前缀。这个库的名称被放置在方括号中。在这个例子中,它是可选的&#8212;&#8212;因为mscorlib.dll是默认的库,并且它包括了.NET所需要的大部分类。
&&&&& .maxstack: 这个伪指令指定了在一个方法被调用时,能够出现在计算栈上的元素的最大数量。
&&&&& .module: 所有的IL文件必须是一个逻辑实体的一部分,或它们的组合体,我们将这些实体称为模块(module)。文件被添加到使用了.module伪指令的模块中。模块的名称可能被规定为aa.exe,但是可执行文件的名称和前面保持一样,即a.exe。
&&&&& .subsystem: 这个指令用于指定可执行体运行在什么操作系统上。这是另一种指定可执行体所代表的种类的方式。一些数字值和它们对应的操作系统如下所示:
&&&&&&&&&&&&2 - A Windows Character 子系统。
&&&&&&&&&&&&3 - A Windows GUI 子系统。
&&&&&&&&&&&&5 &#8211; 像OS/2这样的老系统。
&&&&& .corsflags: 这个伪指令用于指定对于64位计算机唯一的标志。值1表示它是从il中创建的可执行文件,而值64表示一个库。
&&&&& .assembly: 在前面,我们曾经简单涉及过一个名为.assembly的指令。现在让我们进行深入的研究。
&&&&& 无论我们创建了什么,都是一个称为清单(manifest)的实体的一部分。.assembly伪指令标注了一个清单的开始位置。在层次上,模块是清单最小的实体。.assembly伪指令指定了这个模块属于哪个程序集。模块只能包括一个单独的.assembly伪指令。
&&&&& 对于exe文件,这个伪指令的存在是必须的,但是,对于.dll中的模块,则是可选的。这是因为,我们需要使用这个伪指令来创建一个程序集。这是.NET的基本需要。程序集伪指令包括了其它伪指令。
&&&&& .hash: 散列计算是一门在计算机世界中通用的技术,这里有大量使用到的散列方法或算法。这个伪指令用于散列计算。
&&&&& .ver: .ver:伪指令包括了4个由冒号分割的数字。按照下面给定的顺序,它们代表了下面的信息:
主版本编号次版本编号内部版本号修订版本号
&&&&& extern: 如果有涉及到其它程序集的需求,就要使用到extern伪指令。.NET核心类的代码位于mscorlib.dll中。除了这个dll之外,当我们的程序需要涉及到大量其它的dll时,extern伪指令就要排上用场了。
&&&&& originator: 在转移到解释上面程序的本质和意义之前,这是我们要研究的最后一个伪指令。这个伪指令揭示了创建该dll的标识。它包括了dll的所有者公钥的8个字节。它显然是一个散列值。
&&&&& 让我们以一种不同的方式一步一步地温习到目前为止我们所做的事情。
(a)我们开始于一个我们能够编写的最简单的程序。这个程序被称为a.cs,并包括了下面的代码:
class&zzz{&&&&&&public&static&void&Main()&&&&&&&{&&&&&&&&&&&&System.Console.WriteLine("hi");&&&&&&}&}
(b)然后我们使用下面的命令运行C#编译器。
&&&&&&&&&&&&&csc a.cs
&&&&& 因此,会创建名为a.exe的exe文件。
(c)在可执行体中,我们运行一个名为ildasm的程序,它是由Microsoft提供的:
&&&&&&&&&&&&&ildasm /out=a.txt a.exe
&&&&&&这就创建了一个txt文件,具有下面的内容:
//&&Microsoft&(R)&.NET&Framework&IL&Disassembler.&&Version&1.0.2204.21//&&Copyright&(C)&Microsoft&Corp.&&//&VTableFixup&Directory://&No&data..subsystem&<span style="color: #x.corflags&<span style="color: #x.assembly&extern&mscorlib{&&.originator&=&(<span style="color: #&<span style="color: #&<span style="color: #&<span style="color: #&D3&A4&AE&<span style="color: #&)&&&&&&&&//&.h..3&&.hash&=&(<span style="color: #&<span style="color: #&F8&C9&<span style="color: #&1F&<span style="color: #&3F&<span style="color: #&D7&AB&AD&E2&DF&1D&E0&&&&&&&&&&&&&&F2&9D&4F&BC&)&&&&&&&&&&&&&&&&&&&&&&&&//&RD..U.T?O.&&.ver&<span style="color: #:<span style="color: #:<span style="color: #04:<span style="color: #}.assembly&a&as&"a"{&&.hash&algorithm&<span style="color: #x&&.ver&<span style="color: #:<span style="color: #:<span style="color: #:<span style="color: #}.module&aa.exe//&MVID:&{89CFAD60-F5BD-11D4-A55A-96B5C7D61E7B}.class&private&auto&ansi&zzz&&&&&&&extends&System.Object{&&.method&public&hidebysig&static&void&vijay()&il&managed&&{&&&&.entrypoint&&&&//&Code&size&&&&&&&11&(0xb)&&&&.maxstack&&<span style="color: #&&&&IL_0000:&&ldstr&&&&&&"hell"&&&&IL_0005:&&call&&&&&&&void&System.Console::WriteLine(class&System.String)&&&&IL_000a:&&ret&&}&//&end&of&method&zzz::vijay&&&.method&public&hidebysig&specialname&rtspecialname&&&&&&&&&&&instance&void&.ctor()&il&managed&&{&&&&//&Code&size&&&&&&&17&(0x11)&&&&.maxstack&&<span style="color: #&&&&IL_0000:&&ldstr&&&&&&"hell"&&&&IL_0005:&&call&&&&&&&void&System.Console::WriteLine(class&System.String)&&&&IL_000a:&&ldarg.0&&&&IL_000b:&&call&&&&&&&instance&void&[mscorlib]System.Object::.ctor()&&&&IL_0010:&&ret&&}&//&end&of&method&zzz::.ctor&}&//&end&of&class&zzz&//***********&DISASSEMBLY&COMPLETE&***********************
&&&&& 当我们阅读上面的文件时,你将明白它的所有内容都已经在前面解释过了。我们开始于一个简单的C#程序,然后将它编译到一个可执行文件中。在正常的环境下,它将被转换为机器语言或这个程序运行在所在的计算机/微处理器的汇编程序。一旦创建了可执行体,我们就使用ildasm来反汇编它。反汇编输出被保存到一个新的文件a.txt中。这个文件可能被命名为a.il,然后我们可以通过对其运行ilasm反过来再次创建这个可执行体。
&&&&& 让我们看一下最小的VB.NET程序。我们将它命名为one.vb,而它的源代码如下所示:
Public&Module&modmain&&&&Sub&Main()&&&&&&&&System.Console.WriteLine("hell")&&&&End&SubEnd&Module
&&&&& 在编写完上述的代码后,我们运行Visual.Net编译器vbc如下:
&&&&&&&&&&&&&vbc one.vb
&&&&& 这就产生了文件one.exe。
&&&&& 下面,我们执行ildasm如下所示:
&&&&&&&&&&&&&ildasm /out=a.txt one.exe
&&&&& 这就生成了下面的文件a.txt:
//&&Microsoft&(R)&.NET&Framework&IL&Disassembler.&&Version&1.0.2204.21//&&Copyright&(C)&Microsoft&Corp.&&//&VTableFixup&Directory://&No&data..subsystem&<span style="color: #x.corflags&<span style="color: #x.assembly&extern&mscorlib{&&.originator&=&(<span style="color: #&<span style="color: #&<span style="color: #&<span style="color: #&D3&A4&AE&<span style="color: #&)&&&&&&&&&&&&&&//&.h..3&&.hash&=&(<span style="color: #&<span style="color: #&F8&C9&<span style="color: #&1F&<span style="color: #&3F&<span style="color: #&D7&AB&AD&E2&DF&1D&E0&&&F2&9D&4F&BC&)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//&RD..U.T?.O.&&.ver&<span style="color: #:<span style="color: #:<span style="color: #04:<span style="color: #}.assembly&extern&Microsoft.VisualBasic{&&.originator&=&(<span style="color: #&<span style="color: #&<span style="color: #&<span style="color: #&D3&A4&AE&<span style="color: #&)&&&&&&//&.h..3&&.hash&=&(5B&<span style="color: #&1F&D2&5E&1A&<span style="color: #&<span style="color: #&F5&<span style="color: #&B2&<span style="color: #&9F&<span style="color: #&A1&BE&&&&&&&&&&&&&&E5&5E&0D&E4&)&&&&&&&&&&&&&&&&&&//&[B..^.B.).5.&&.ver&<span style="color: #:<span style="color: #:<span style="color: #:<span style="color: #}.assembly&one&as&"one"{&&.hash&algorithm&<span style="color: #x&&.ver&<span style="color: #:<span style="color: #:<span style="color: #:<span style="color: #}.module&one.exe//&MVID:&{1ED1-11D4-A55A-96B5C7D61E7B}.class&public&auto&ansi&modmain&&&&&&&extends&[mscorlib]System.Object{&&.custom&instance&void&[Microsoft.VisualBasic]Microsoft.VisualBasic.Globals/Globals$StandardModuleAttribute::.ctor()&=&(&<span style="color: #&<span style="color: #&<span style="color: #&<span style="color: #&)&&&.method&public&static&void&Main()&il&managed&&{&&&&//&Code&size&&&&&&&11&(0xb)&&&&.maxstack&&<span style="color: #&&&&.locals&init&(class&System.Object[]&V_0)&&&&IL_0000:&&ldstr&&&&&&"hell"&&&&IL_0005:&&call&&&&&&&void&[mscorlib]System.Console::WriteLine(class&System.String)&&&&IL_000a:&&ret&&}&//&end&of&method&modmain::Main&}&//&end&of&class&modmain&.class&private&auto&ansi&_vbProject&&&&&&&extends&[mscorlib]System.Object{&&.custom&instance&void&[Microsoft.VisualBasic]Microsoft.VisualBasic.Globals/Globals$StandardModuleAttribute::.ctor()&=&(&<span style="color: #&<span style="color: #&<span style="color: #&<span style="color: #&).method&public&static&void&&_main(class&System.String[]&_s)&il&managed&&{&&&&.entrypoint&&&&//&Code&size&&&&&&&6&(0x6)&&&&.maxstack&&<span style="color: #&&&&IL_0000:&&call&&&&&&&void&modmain::Main()&&&&IL_0005:&&ret&&}&//&end&of&method&_vbProject::_main}&//&end&of&class&_vbProject//***********&DISASSEMBLY&COMPLETE&***********************
&&&&& 你将惊讶地看到由两个不同的编译器所生成的输出几乎是相同的。我向你展示了这个示例用以证实&#8212;&#8212;语言的无关性,最终,源代码将会被转换为IL代码。无论我们使用VB.NET或C#,都会调用相同的WriteLine函数。
&&&&& 因此,程序语言间的不同现在是表面上的问题。无休止的争论那个语言是最优的是没有意义的。从而,IL使得程序员可以自由使用他们所选择的语言。
&&&&& 让我们揭开上面给出的代码的神秘面纱。
&&&&& 每个VB.NET程序都需要被包括在一个模块中。我们称之为modmain。Visual Basic中的所有模块都是以关键字End结束的,从而我们会看到End Module。这是VB在语法上不区别于C#的地方&#8212;&#8212;C#不理解模块是什么。
&&&&& 在VB.NET中,函数被称为子程序。我们需要子程序来标注程序执行的开始位置。这个子程序被称为Main。
&&&&& VB.NET代码不仅关联到mscorlib.dll,还使用了文件Microsoft.VisualBasic。
&&&&& 在IL中会创建一个名为_vbProject的类,因为在VB中类的名称不是必须的。
&&&&& 称为_main的函数是子函数的开始,因为它具有entrypoint伪指令。它的名称前面有一个下划线。这些名称是由VB编译器选择用来生成IL代码的。
&&&&& 这个函数会传递一个字符串数组作为参数。它具有一个自定义伪指令来处理元数据的概念。
&&&&& 接下来,我们具有这个函数的完整原型,以一系列可选的字节作为终结。这些字节是元数据规范中的一部分。
&&&&& 模块modmain被转换为一个具有相同名称的类。和之前一样,这个类还具有相同的伪指令.custom和一个Main函数。该函数使用了名为.locals的伪指令在栈上创建一个只能在这个方法中使用变量。这个变量只存在于方法执行期间,当方法停止运行时,它就会&#8220;消亡&#8221;。
&&&&& 字段还存储在内存中,但是需要更长的时间来为它们分配内存。关键字init表示在创建期间,这些变量应该被初始化为它们的默认值。默认值依赖于变量的类型。数值总是被初始化为值ZERO。关键字init之后是这些变量的数据类型和它的名称。
阅读(...) 评论()

我要回帖

更多关于 扫码签到功能代码 的文章

 

随机推荐