More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  prowyh's spaceProfileFriendsBlogMore Tools Explore the Spaces community

prowyh's space

任尔东南西北风 太阳底下我独行
September 02

轰炸机驾驶员、柔道高手、钓鱼专家、猎虎英雄

 
据俄罗斯国家电视台网站8月31日报道,普京日前与俄罗斯研究人员一同前往俄罗斯一个国家森林公园跟踪研究西伯利亚猛虎的野外生活。正当普京与野生动物研究专家来到一只被束缚的猛虎面前时,这只老虎突然挣脱枷锁,朝随行的电视台工作人员扑去。就在这时,普京迅速拿起麻醉枪向猛兽射去一支麻醉剂,工作人员这才脱险。在虎中弹入睡后,普京亲手为虎戴上装备全球定位系统的项圈,以便科研人员监控它的动向和健康状况。俄罗斯《消息报》甚至报道称,普京离开国家森林公园时,还专门亲吻了那只老虎并说了声:“再见!”
July 21

对象引用与托管指针(object references and managed pointers)

C/C++中的指针是一种非常灵活而强大的引用机制,但同时也非常脆弱,稍有不慎,就会出错。
 
Java完全摈弃了指针的概念,而代之以对象引用(object reference),基本上消灭了由指针而导致的错误。
 
C#借鉴了Java的作法,引入了对象引用的概念,但同时,不像Java做的那么绝,仍然提供了指针的概念。
 
CLR支持三种类型的指针:
 
1) 托管指针(managed pointers)
2) 非托管指针(unmanaged pointers)
3) 非托管函数指针(unmanaged function pointers)
 
托管指针是一种新类型的指针,指向托管堆中的内存区。
非托管指针是传统的C/C++指针,指向非托管内存区。
非托管函数指针也是传统的C/C++指针,指向函数地址。
 
C#中的指针属于非托管指针,在C#中不能直接使用托管指针,但作为 by-ref 参数传递机制的 ref 和 out 机制就是利用托管指针实现的。
 
C#代码:
 
using System;
public class Test
{
public static void Main()
{
int n = 20;
int m;
 
compute(n, out m);
}
private static void compute(int x, out int y)
{
y = x * 2;
}
}
 
编译以后,生成如下 IL 代码(为了简单起见,这里只取compute()方法的 IL 代码):
 
.method private hidebysig static void  compute(int32 x, [out] int32& y) cil managed
{
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldarg.0
  IL_0003:  ldc.i4.2
  IL_0004:  mul
  IL_0005:  stind.i4
  IL_0006:  ret
} // end of method Test::compute
 
从compute()的 IL 代码即可明显看出,参数 y 的类型即是托管指针 int32&。
 
stind.i4 指令将栈顶的 32 位整数值存储到由次栈顶元素(即参数 y)所指示的内存地址中。
 
上面的C#代码用的是 out 指示符,如果换成 ref 指示符,生成的 IL 代码是一样的,所不同的是,C#编译器会检查 m 的赋值情况,在执行 compute(n, ref m) 之前,m 必须被明确赋值(definite assigned),而对于 out 指示符,m 不需要赋值。
 
托管指针和非托管指针的区别是明显的,托管指针指向的是托管堆中的地址,而非托管指针指向的是非托管内存中的地址。
 
那么,对象引用与托管指针又有什么不同呢?
 
从物理实现的角度看,对象引用也是一种指针,而且是一种托管类型的指针。与托管指针不同,对象引用只能指向对象的起始位置,而不能指向对象内部,也就是说,对象引用指向的是对象的整体,而非局部。
 
而托管指针一般是指向对象内部某个成员的地址。除此之外,托管指针还可以指向求值栈(evaluation stack)中的位置,或静态变量,甚至非托管内存区。
 
July 20

To box or not to box (III)

C#代码:
 
using System;
public class Test
{
public static void Main()
{
object o = 30;
int k = (int)o;
}
}
 
编译以后,生成如下的IL代码:
 
.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       17 (0x11)
  .maxstack  1
  .locals init (object V_0,
           int32 V_1)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   30
  IL_0003:  box        [mscorlib]System.Int32
  IL_0008:  stloc.0
  IL_0009:  ldloc.0
  IL_000a:  unbox.any  [mscorlib]System.Int32
  IL_000f:  stloc.1
  IL_0010:  ret
} // end of method Test::Main
 
其中,局部变量 o 成为 V_0,局部变量 k 成为 V_1。
 
ldc.i4.s 30 指令我们上文已经见过,将 30 推入栈中。
 
box [mscorlib]System.Int32 指令将栈顶元素(30)进行装箱操作,并将对象引用推入栈中。
 
stloc.0 指令将栈顶元素(30装箱后的对象引用)存入 V_0。
 
ldloc.0 将 V_0 推入栈中。
 
unbox 指令有两种形式:unbox valuetype,unbox.any typeTok
 
unbox valuetype 的意思是将装箱的值类型转换为其原初形式(raw form)[1],实际上是返回指向对象中的值的指针(valueTypePtr),并推入栈中。
 
unbox.any typeTok 指令抽取对象中的值(也就是被装箱的值),返回,并推入栈中。
 
由此可见,指令 unbox 和 unbox.any 是不同的:unbox 只是返回指向值的指针,而 unbox.any 则返回实际的值。
 
unbox.any [mscorlib]System.Int32 指令从栈顶元素(对象引用,即 o)所引用的对象中抽取装箱的值(即30),并将其推入栈顶。
 
stloc.1 指令将栈顶元素存入 V_1(即 k)。
 
由此可见,相比装箱操作(boxing)而言,拆箱操作(unboxing)只是取对象中的地址或抽取对象中的值,并不算太费时。
 
[1] 值类型有两种表示:
 
1) 原初形式(raw form) ,当某个值类型嵌入其它对象中时,即以原初形式表示;
2) 装箱形式(boxed form),当某个值类型的数值(data)被装箱到一个对象中时,即以装箱形式表示,此时可以作为一个独立的实体(an independent entity)而存在。
 
July 19

To box or not to box (II)

C#代码:
 
using System;
public class Test
{
public static void Main()
{
int n = 30;
Console.Write("n: {0}", n);
}
}
 
编译以后,将生成如下的IL代码:
 
.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       22 (0x16)
  .maxstack  2
  .locals init (int32 V_0)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   30
  IL_0003:  stloc.0
  IL_0004:  ldstr      "n: {0}"
  IL_0009:  ldloc.0
  IL_000a:  box        [mscorlib]System.Int32
  IL_000f:  call       void [mscorlib]System.Console::WriteLine(string, object)
  IL_0014:  nop
  IL_0015:  ret
} // end of method Test::Main
 
ldc.i4.s num 的意思是 push num onto the stack as int32, short form. 所以 ldc.i4.s 30 的意思是将 30 作为 32 位整数推入栈顶。
stloc.0 的意思是 pop a value from stack into local variable 0.
 
所以 ldc.i4.s 30 和 stloc.0 两条指令执行的结果是完成C#中的赋值(变量定义)语句:
 
int n = 30;
 
这里,局部变量 n 变成了 V_0。
 
ldstr "n: {0}" 指令的意思是将字符串 "n: {0}" 推入栈顶。
 
ldloc.0 的意思是 load local variable 0 onto stack,即将 V_0 的值推入栈顶。(此时由ldstr推入的字符串成为次栈顶元素。)
 
box typeTok 的意思是 convert a boxable value to its boxed form,即将值转换为装箱形式,即引用类型。
box [mscorlib]System.Int32 即是将栈顶的 32 位整数值转换为装箱后的形式,然后返回对象引用,并将此对象引用推入栈顶。
 
call void [mscorlib]System.Console::WriteLine(string, object) 即是以栈顶元素(装箱后的对象引用)和次栈顶元素(由ldstr推入的字符串)为参数,调用WriteLine方法。
 
这段代码唯一不好理解的是由 box 完成的装箱操作。
 
简单说来,box 操作分为三步:
 
1) 创建一个无名的新对象,类似于 new Object();
2) 将要装箱的值(存在于栈顶)从栈顶弹出,拷贝到新创建的对象中;
3) 返回该对象引用,并推入栈顶。
 
如果转换为C的语义,可以这样理解:
 
1) 调用malloc()分配适当大小的内存区;
2) 将值从栈中弹出,拷贝到该内存区;
3) 返回该内存区的地址,并推入栈顶。
 
当然,这只是一个有助于理解的类比,实际的实现并不一定与此相同。
 
由 box 指令的操作可以看出,存在两个比较费时的操作,即1) 创建新对象,以及2) 值的拷贝。
 
由此可见,虽然C#通过 boxing 机制模糊了引用类型和值类型的边界,使得程序员可以写出一致的代码,但 box 操作不可避免地存在着性能损失。所以,应该尽可能地避免 box 操作。
 
July 15

To box or not to box

给定:
 
int n = 30;
 
下面的两条C#语句有什么区别吗?
 
1. System.Console.WriteLine("n: {0}", n);
2. System.Console.WriteLine("n: {0}", n.ToString());
 
当然,这两条语句产生的结果是没有任何区别的:
n: 30
 
既然如此,还有什么可研究的吗?先别急着下结论,研究研究再说。
 
首先,我们来看看WriteLine()的语法格式:
 
public static void WriteLine(string format, Object arg0)
 
从此语法格式可知,WriteLine()的第二个参数要求是Object类型,上述的语句2是满足的,因为n.ToString()的结果是string类型,而string类型就是Object类型;而语句1是不满足的,因为n是值类型(value type),而WriteLine()又没有形如WriteLine(string format, int arg0)的语法,既然如此,那 n 又是如何转换为 Object 类型的呢?(否则编译就会出错:类型不匹配!)
 
C#区分了引用类型(reference type)和值类型(value type)。
 
引用类型涉及到两个实体(当然,对程序员是透明的):对象和对象引用,对象存在于堆(heap)中,而对象引用存在于栈(stack)中。对于一个对象,可以存在若干个对象引用指向该对象;当然也存在不指向任何对象的对象引用,该对象引用的值即为null;对于不存在任何对象引用的对象,即成为GC要回收的对象。
 
而对于值类型,只存在一个实体,即值本身,该实体存在于栈中。
 
由此可见,引用类型和值类型有着很大的区别。这种区别有时候是必要的,如:
 
for (int i = 0; i < 10; i++)
{
// loop statements
}
 
这里的循环控制变量 i 作为值类型,就非常自然而高效。如果 i 也作为引用类型,将大大降低 for 语句的效率。但有的时候却是不必要的,如上述的WriteLine()语句,如果引用类型和值类型不能互相转换,则必须要有类似如下的WriteLine语法:
 
public static void WriteLine(string format, int arg0)
 
而且WriteLine对于所有的值类型都要有类似的语法,这将使得 FCL 非常庞大而笨拙!
 
C#语言通过 box/unbox 设施(机制)比较好地解决了这个问题。使得下述声明
 
public static void WriteLine(string format, Object arg0)
 
适用于所有的数据类型。所以,我们可以写出像
 
WriteLine("n: {0}", n);
 
这样的语句。这里的 n 通过 box 机制自动转换为 Object 类型。通过检查编译器生成的中间代码可以更清楚地看出这一点:
 
  .locals init (int32 V_0)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   30
  IL_0003:  stloc.0
  IL_0004:  ldstr      "n: {0}"
  IL_0009:  ldloc.0
  IL_000a:  box      [mscorlib]System.Int32
  IL_000f:   call      void [mscorlib]System.Console::WriteLine(string, object)
 
从这段 IL 代码中可以看出,n 通过 box 指令转换为了引用类型,然后再调用WriteLine(string, object)方法。
 
从这个例子可以看出,C#中的 box/unbox 机制,就像 using 语句(using statement,非using directive)一样,是一种便用设施(handy facility)。这种便用设施使得程序更加优雅,正因为此,Java语言从5.0开始也引入了 box/unbox 机制。
 
June 01

我的工作空间 (My Workspace)

看来网络广告是真有效果,以前也知道有Microsoft Office Online版本的事情,没怎么在意。今天在spaces.live.com上看到office.live.com的广告,点了一下,就成了Office Live Workspace的用户了。Open-mouthed
 
试用了一下,感觉不错。
 
 
界面做的非常专业,不愧是Microsoft,一上来就是大手笔,比我做的界面强多了…… Hot
 
试比较:
 
 
虽然我认为已经很专业了,但和Microsoft相比,还是差那么一点点。Smile
 
1) 从Microsoft的几个在线系统(spaces.live.com, hotmail.com, msdn.microsoft.com, workspace.office.live.com)来看,Microsoft已经舍弃了HTML中的Button。
 
2) workspace.office.live.com只是一个Workspace,文档的编辑还是利用Office桌面软件,这一方面是Online版的编辑器现在还无法与桌面软件相比(spaces.live.com的编辑器已经足够好了,但从UE的角度来说,还是无法与Word相比),另一方面也是一种策略,Microsoft不可能丢掉Desktop Office这只Cash Bull的!
 
3) 使用workspace.office.live.com需要安装(或打开,enable)几个ActiveX控件,利用这些控件来连接Web与Desktop,这样使得workspace.office.live.com与Desktop Office的交互更加顺畅,而不会太有两张皮的感觉。这恐怕只有Microsoft才能够做到,因为你如果只有Online System,无法与桌面进行顺畅的交互。而如果Microsoft没有IE,要想顺利实现这些也是很难的(甚至是不可能的)。
 
Web的桌面化(Web as Your Desktop)是目前的大趋势,SaaS(Software as a Service)在起着助推器的作用,但完全实现这点目前还有技术上的难度,HTML、DOM还有待于继续完善,JavaScript还很弱(不论是编程模型,还是调试手段,以及执行效率),浏览器对标准的实现程度还是一个问题。
 
May 31

高人与狂人 (What are about the great men)

高人
 
有人对康德诉苦,说读他的这本书(《纯粹理性批判》)苦于手指头太少了,康德惊异地问为什么?回答说,我用每个手指头按住一个子句,十个手指头用完了,你写的这一句还没完。
 
—— 李泽厚著《批判哲学的批判:康德述评》(修订第六版)
 
狂人
 
一日,日机空袭,警报响起,联大的教授和学生四下散开躲避。刘文典跑到中途,忽然想起他“十二万分”佩服的陈寅恪身体嬴弱且目力衰竭,于是便率几个学生折回来搀扶着陈往城外跑去。他强撑着不让学生扶他。大声叫嚷着:“保存国粹要紧!保存国粹要紧!”让学生们搀着陈先走。这时,只见他平素藐视的新文学作家沈从文也在人流中,便顾不得自己气喘如牛,转身呵斥到:“你跑什么跑?我刘某人是在替庄子跑,我要死了,就没人讲《庄子》了!你替谁跑?”
 
刘文典多年潜心研究庄子,出版了十卷本《庄子补正》,陈寅恪为之作序,推崇备至。曾有人向刘氏问起古今治庄子者的得失,他大发感慨,口出狂言道:“在中国真正懂得《庄子》的,只有两个人,一个是庄周,还有一个就是刘某人。”
 
—— 徐百柯著《民国那些人》
 
真正的学者总是会流传一些奇闻轶事,即使被海涅“揶揄”为“没什么生平可说的”康德,也会有使人“会心”的故事,更不用说刘文典那使人“喷饭”的狂狷了。
 
May 29

据说是比尔的千金 (Bill's daughter?)

 
据说是比尔•盖茨的女儿
 

灾区学生烈日下备战高考[组图]

 
5月28日,陕西宁强县一中高三年级学生顶着烈日听英语课
 
 
5月28日,陕西宁强县一中高三年级学生顶着烈日听英语课
 
陕西南部的汉中市宁强县,紧邻四川广元,在此次地震中受灾严重,再加上余震影响,目前中小学尚未复课。随着高考临近,宁强县一中为高三年级1100多名学生建立了帐篷校舍,供他们学习、住宿。由于帐篷闷热,很多课程只能放到帐篷外上,学生们顶着烈日依然认真学习,抓紧时间备战高考。
 
新华社记者 陈钢 摄
 
黄土地的希望!
 
May 27

吉本《罗马帝国衰亡史》

刚收到新一期的《季风书讯》。
 
本期《季风书讯》重点推介英国史学家爱德华·吉本的《罗马帝国衰亡史》:“从本周近五百种新书中选出《罗马帝国衰亡史》,是因为此书实
在重要。”
 
 
吉本花费20年光阴写成煌煌六卷的巨著,并以此书而“成为继希罗多德、修昔底德、塔西佗、马基雅维里等欧洲史家后的又一位史学大家。”
 
此书译者席代岳先生,“在美国,以一人之力,译了十几年,终于完成”!
 
写书难,译书更难。在语义差距甚大的两种语言之间进行转换,实为不易!以一人之力,穷十几年之功,其“愚公移山”之精神实在让人感佩!
 
西方文化,言必称希腊、罗马。如果说文学上通过荷马、埃斯库罗斯,哲学上通过柏拉图、亚里士多德,数学上通过必达哥拉斯、欧几里得,使得我们对希腊多少还知道点的话,对于罗马,恐怕除了其讲拉丁语之外就一无所知了,或许,延续至今的罗马教皇是罗马最伟大的历史成就?!Smile
 
呜呼!
 
View more entries