VC中的字节对齐

 
    当在C中定义了一个结构类型时,它的大小是否等于各字段(field)大小之和?编译器将如何在内存中放置这些字段?ANSI C对结构体的内存布局有什么要求?而我们的程序又能否依赖这种布局?这些问题或许对不少朋友来说还有点模糊,那么本文就试着探究它们背后的秘密。
    首先,至少有一点可以肯定,那就是ANSI C保证结构体中各字段在内存中出现的位置是随它们的声明顺序依次递增的,并且第一个字段的首地址等于整个结构体实例的首地址。比如有这样一个结构体:
 
  struct vector{int x,y,z;} s;
  int *p,*q,*r;
  struct vector *ps;
 
  p = &s.x;
  q = &s.y;
  r = &s.z;
  ps = &s;
  assert(p < q);
  assert(p < r);
  assert(q < r);
  assert((int*)ps == p);
  // 上述断言一定不会失败
    这时,有朋友可能会问:"标准是否规定相邻字段在内存中也相邻?"。 唔,对不起,ANSI C没有做出保证,你的程序在任何时候都不应该依赖这个假设。那这是否意味着我们永远无法勾勒出一幅更清晰更精确的结构体内存布局图?哦,当然不是。不过先让我们从这个问题中暂时抽身,关注一下另一个重要问题————内存对齐。
    许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。当一种类型S的对齐模数与另一种类型T的对齐模数的比值是大于1的整数,我们就称类型S的对齐要求比T强(严格),而称T比S弱(宽松)。这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度。比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节内存块上。某些处理器在数据不满足对齐要求的情况下可能会出错,但是Intel的IA32架构的处理器则不管数据是否对齐都能正确工作。不过Intel奉劝大家,如果想提升性能,那么所有的程序数据都应该尽可能地对齐。Win32平台下的微软C编译器(cl.exe for 80×86)在默认情况下采用如下的对齐规则: 任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。Linux下的GCC奉行的是另外一套规则(在资料中查得,并未验证,如错误请指正):任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数。
    现在回到我们关心的struct上来。ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和。嗯?填充区?对,这就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。那么结构体本身有什么对齐要求吗?有的,ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格(但此非强制要求,VC7.1就仅仅是让它们一样严格)。我们来看一个例子(以下所有试验的环境是Intel Celeron 2.4G + WIN2000 PRO + vc7.1,内存对齐编译选项是"默认",即不指定/Zp与/pack选项):
  typedef struct ms1
  {
     char a;
     int b;
  } MS1;
    假设MS1按如下方式内存布局(本文所有示意图中的内存地址从左至右递增):
       _____________________________
       |       |                   |
       |   a   |        b          |
       |       |                   |
       +—————————+
 Bytes:    1             4
    因为MS1中有最强对齐要求的是b字段(int),所以根据编译器的对齐规则以及ANSI C标准,MS1对象的首地址一定是4(int类型的对齐模数)的倍数。那么上述内存布局中的b字段能满足int类型的对齐要求吗?嗯,当然不能。如果你是编译器,你会如何巧妙安排来满足CPU的癖好呢?呵呵,经过1毫秒的艰苦思考,你一定得出了如下的方案:
       _______________________________________
       |       |\\\\\\\\\\\|                 |
       |   a   |\\padding\\|       b         |
       |       |\\\\\\\\\\\|                 |
       +————————————-+
 Bytes:    1         3             4
    这个方案在a与b之间多分配了3个填充(padding)字节,这样当整个struct对象首地址满足4字节的对齐要求时,b字段也一定能满足int型的4字节对齐规定。那么sizeof(MS1)显然就应该是8,而b字段相对于结构体首地址的偏移就是4。非常好理解,对吗?现在我们把MS1中的字段交换一下顺序:
  typedef struct ms2
  {
     int a;
     char b;
  } MS2;
    或许你认为MS2比MS1的情况要简单,它的布局应该就是
       _______________________
       |             |       |
       |     a       |   b   |
       |             |       |
       +———————+
 Bytes:      4           1
    因为MS2对象同样要满足4字节对齐规定,而此时a的地址与结构体的首地址相等,所以它一定也是4字节对齐。嗯,分析得有道理,可是却不全面。让我们来考虑一下定义一个MS2类型的数组会出现什么问题。C标准保证,任何类型(包括自定义结构类型)的数组所占空间的大小一定等于一个单独的该类型数据的大小乘以数组元素的个数。换句话说,数组各元素之间不会有空隙。按照上面的方案,一个MS2数组array的布局就是:
|<-    array[1]     ->|<-    array[2]     ->|<- array[3] …..
__________________________________________________________
|             |       |              |      |
|     a       |   b   |      a       |   b  |………….
|             |       |              |      |
+———————————————————-
Bytes:  4         1          4           1
    当数组首地址是4字节对齐时,array[1].a也是4字节对齐,可是array[2].a呢?array[3].a ….呢?可见这种方案在定义结构体数组时无法让数组中所有元素的字段都满足对齐规定,必须修改成如下形式:
       ___________________________________
       |             |       |\\\\\\\\\\\|
       |     a       |   b   |\\padding\\|
       |             |       |\\\\\\\\\\\|
       +———————————+
 Bytes:      4           1         3
    现在无论是定义一个单独的MS2变量还是MS2数组,均能保证所有元素的所有字段都满足对齐规定。那么sizeof(MS2)仍然是8,而a的偏移为0,b的偏移是4。
    好的,现在你已经掌握了结构体内存布局的基本准则,尝试分析一个稍微复杂点的类型吧。
  typedef struct ms3
  {
     char a;
     short b;
     double c;
  } MS3;
    我想你一定能得出如下正确的布局图:
        
        padding 
           |
      _____v_________________________________
      |   |\|     |\\\\\\\\\|               |
      | a |\|  b  |\padding\|       c       |
      |   |\|     |\\\\\\\\\|               |
      +————————————-+
Bytes:  1  1   2       4            8
          
    sizeof(short)等于2,b字段应从偶数地址开始,所以a的后面填充一个字节,而sizeof(double)等于8,c字段要从8倍数地址开始,前面的a、b字段加上填充字节已经有4 bytes,所以b后面再填充4个字节就可以保证c字段的对齐要求了。sizeof(MS3)等于16,b的偏移是2,c的偏移是8。接着看看结构体中字段还是结构类型的情况:
  typedef struct ms4
  {
     char a;
     MS3 b;
  } MS4;
    MS3中内存要求最严格的字段是c,那么MS3类型数据的对齐模数就与double的一致(为8),a字段后面应填充7个字节,因此MS4的布局应该是:
       _______________________________________
       |       |\\\\\\\\\\\|                 |
       |   a   |\\padding\\|       b         |
       |       |\\\\\\\\\\\|                 |
       +————————————-+
 Bytes:    1         7             16
    显然,sizeof(MS4)等于24,b的偏移等于8。
    在实际开发中,我们可以通过指定/Zp编译选项来更改编译器的对齐规则。比如指定/Zpn(VC7.1中n可以是1、2、4、8、16)就是告诉编译器最大对齐模数是n。在这种情况下,所有小于等于n字节的基本数据类型的对齐规则与默认的一样,但是大于n个字节的数据类型的对齐模数被限制为n。事实上,VC7.1的默认对齐选项就相当于/Zp8。仔细看看MSDN对这个选项的描述,会发现它郑重告诫了程序员不要在MIPS和Alpha平台上用/Zp1和/Zp2选项,也不要在16位平台上指定/Zp4和/Zp8(想想为什么?)。改变编译器的对齐选项,对照程序运行结果重新分析上面4种结构体的内存布局将是一个很好的复习。
    到了这里,我们可以回答本文提出的最后一个问题了。结构体的内存布局依赖于CPU、操作系统、编译器及编译时的对齐选项,而你的程序可能需要运行在多种平台上,你的源代码可能要被不同的人用不同的编译器编译(试想你为别人提供一个开放源码的库),那么除非绝对必需,否则你的程序永远也不要依赖这些诡异的内存布局。顺便说一下,如果一个程序中的两个模块是用不同的对齐选项分别编译的,那么它很可能会产生一些非常微妙的错误。如果你的程序确实有很难理解的行为,不防仔细检查一下各个模块的编译选项。
    思考题:请分析下面几种结构体在你的平台上的内存布局,并试着寻找一种合理安排字段声明顺序的方法以尽量节省内存空间。
    A. struct P1 { int a; char b; int c; char d; };
    B. struct P2 { int a; char b; char c; int d; };
    C. struct P3 { short a[3]; char b[3]; };
    D. struct P4 { short a[3]; char *b[3]; };
    E. struct P5 { struct P2 *a; char b; struct P1 a[2];  };
 
 
 1、 sizeof应用在结构上的情况
请看下面的结构:
struct MyStruct
{
double dda1;
char dda;
int type
};
对结构MyStruct采用sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求:
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。你知道为什么在VC中会得出这样一个结果吗?
其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。
类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char:偏移量必须为sizeof(char)即1的倍数;
int:偏移量必须为sizeof(int)即4的倍数;
float:偏移量必须为sizeof(float)即4的倍数;
double:偏移量必须为sizeof(double)即8的倍数;
Short:偏移量必须为sizeof(short)即2的倍数。
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
下面用前面的例子来说明VC到底怎么样来存放结构的。
struct MyStruct
{
double dda1;
char dda;
int type
};
为上面的结构分配空间的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;接下来为第二个成员dda分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地方满足对齐方式,该成员变量占用 sizeof(char)=1个字节;接下来为第三个成员type分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof (int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+ 3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。
下面再举个例子,交换一下上面的MyStruct的成员变量的位置,使它变成下面的情况:
struct MyStruct
{
char dda;
double dda1;
int type
};
这个结构占用的空间为多大呢?在VC6.0环境下,可以得到sizeof(MyStruc)为24。结合上面提到的分配空间的一些原则,分析下VC怎么样为上面的结构分配空间的。
struct MyStruct
{
char dda;//偏移量为0,满足对齐方式,dda占用1个字节;
double dda1;//下一个可用的地址的偏移量为1,不是sizeof(double)=8的倍数,需要补足7个字节才能使偏移量变为8(满足对齐方式),因此VC自动填充7个字节,dda1存放在偏移量为8的地址上,它占用8个字节。
int type;//下一个可用的地址的偏移量为16,是sizeof(int)=4的倍数,满足int的对齐方式,所以不需要VC自动填充,type存放在偏移量为16的地址上,它占用4个字节。
};
所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof (double)=8)的倍数,所以需要填充4个字节,以满足结构的sizeof(double)=8的倍数。所以该结构总的大小为:sizeof (MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有放任何有意义的东西。
VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。
VC 中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
下面举例说明其用法。
#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢复对齐状态
以上结构的大小为16,下面分析其存储情况,首先为m1分配空间,其偏移量为0,满足我们自己设定的对齐方式(4字节对齐),m1占用1个字节。接着开始为 m4分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。如果把上面的#pragma pack(4)改为#pragma pack(16),那么我们可以得到结构的大小为24。
2、 sizeof用法总结
在VC中,sizeof有着许多的用法,而且很容易引起一些错误。下面根据sizeof后面的参数对sizeof的用法做个总结。
A. 参数为数据类型或者为一般变量。例如sizeof(int),sizeof(long)等等。这种情况要注意的是不同系统系统或者不同编译器得到的结果可能是不同的。例如int类型在16位系统中占2个字节,在32位系统中占4个字节。
B. 参数为数组或指针。下面举例说明.
int a[50]; //sizeof(a)=4*50=200; 求数组所占的空间大小
int *a=new int[50];// sizeof(a)=4; a为一个指针,sizeof(a)是求指针的大小,在32位系统中,当然是占4个字节。
C.参数为结构或类。Sizeof应用在类和结构的处理情况是相同的。但有两点需要注意:第一、结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态变量的存储位置与结构或者类的实例地址无关。第二、没有成员变量的结构或类的大小为1,因为必须保证结构或类的每一个实例在内存中都有唯一的地址。
下面举例说明,
Class Test{int a;static double c};//sizeof(Test)=4,静态成员不对结构或者类的大小产生影响
Test *s;//sizeof(s)=4,s为一个指针。
Class test1{ };//sizeof(test1)=1,没有成员变量的结构或类的大小为1
D. 参数为其他。下面举例说明。
int func(char s[5]);
{
cout<
//数的参数在传递的时候系统处理为一个指针,所以sizeof(s)实际上为求指针的大小。
return 1;
}
//sizeof(func(“1234”))=4//因为func的返回类型为int,所以相当于求sizeof(int).
以上为sizeof的基本用法,在实际的使用中要注意分析VC的分配变量的分配策略,这样的话可以避免一些错误。
 
相关文章:
 
 

让系统时间更准确

使用NTP协议可以让你的计算机自动与服务器上的时间同步。从而保持最准确的时间。
中国国家授时中心的IP地址是:210.72.145.44。
 
在 Windows XP/2000 可下使用以下命令设置计算机的时间同步:
 
在控制面板,管理工具,服务中找到 Windows Time,设置启动类型为自动,再按下启动按钮。
在 DOS 命令行下让 Windows 使用国家授时中心的时间服务器:net time /setsntp:210.72.145.44
要想立刻与时间服务器同步需要重启时间服务,相应的命令是:
net stop w32time
net start w32time
以后你的机器就会有一个准确的时间。

量子力学历史笑话

一.
  故事发生在二十世纪初的法国。
  巴黎延续着千百年的灯红酒绿,香榭丽舍大道上散发着繁华和暧昧,红磨坊里弥漫着躁动与彷徨。
  而在此时的巴黎,有一个年轻人,名字叫做德布罗意,从他的名字当中可以看出这是一个贵族,事实上德布罗意的父亲正是法国的一个伯爵,并且是正是一位当权的内阁部长。这样一个不愁吃不愁穿只是成天愁着如何打发时光的花花公子自然要找一个能消耗精力的东西来磨蹭掉那些无聊的日子,德布罗意则找到了一个很酷的“事业”──研究中世纪史。据说是因为中世纪史中有着很多神秘的东西吸引着这位年轻人。  
  时间一转就到了1919,这是一个科学界急剧动荡动着的年代。就在这一年,德布罗意突然移情别恋对物理产生了兴趣,尤其是感兴趣于当时正流行的量子论。
  具体来说就是感兴趣于一个在当时很酷的观点:光具有粒子性。这一观点早在十几年前由普朗克提出,而后被爱因斯坦用来解释了光电效应,但即便如此,也非常不见容于物理学界各大门派。
  德布罗意倒并不见得对这一观点的物理思想有多了解,也许他的理解也仅仅就是理解到这个观点是在说“波就是粒子”。
  或许是一时冲动,或许是因为年轻而摆酷,德布罗意来到了一派宗师朗之万门下读研究生。  
  从此,德布罗意走出了一道足以让让任何传奇都黯然失色的人生轨迹。
 
二.
  历史上德布罗意到底花了多少精力去读他的研究生也许已经很难说清,事实上德布罗意在他的五年研究生生涯中几乎是一事无成。事实上也可以想象,一个此前对物理一窍不通的中世纪史爱好者很难真正的在物理上去做些什么。
  白驹过隙般的五年转眼就过去了,德布罗意开始要为他的博士论文发愁了。其实德布罗意大约只是明白普朗克爱因斯坦那帮家伙一直在说什么波就是粒子,(事实上对于普朗克大约不能用“一直”二字,此时的普朗克已经完全抛弃自己当初的量子假设,又回到了经典的就框架。)而真正其中包含的物理,他能理解多少大约只有上帝清楚。
  五年的尽头,也就是在1924,德布罗意终于提交了自己的博士论文。他的博士论文只有一页纸多一点,不过可以猜想这一页多一点的一份论文大约已经让德布罗意很头疼了,只可惜当时没有枪手可以雇来帮忙写博士论文。
  他的博士论文只是说了一个猜想,既然波可以是粒子,那么反过来粒子也可以是波。
  而进一步德布罗意提出波的波矢和角频率与粒子动量和能量的关系是:
      动量=普朗克常数/波矢
      能量=普朗克常数*角频率
      这就是他的论文里提出的两个公式
  而这两个公式的提出也完全是因为在爱因斯坦解释光电效应的时候提出光子的动量和能量与光的参数满足这一关系。
  可以想象这样一个博士论文会得到怎样的回应。在对论文是否通过的投票之前,德布罗意的老板朗之万就事先得知论文评审委员会的六位教授中有三位已明确表态会投反对票。
  本来在欧洲,一个学生苦读数年都拿不到学位是件很正常的事情,时至今日的欧洲也依然如此。何况德布罗意本来就是这么一个来混日子的的花花公子。
  然而这次偏偏又有些不一样──德布罗意的父亲又是一位权高望众的内阁部长,而德布罗意在此厮混五年最后连一个Ph.D都没拿到,双方面子上自然也有些挂不住。
  情急之中,导师朗之万往他的一个好朋友那里寄了一封信。
  当初的朗之万是不是碍于情面想帮德布罗意混得一个PhD已不得而知,然而事实上,这一封信却改变了科学发展的轨迹。
 
三.
  这封信的收信人是爱因斯坦。信的内容大致如下:

  尊敬的爱因斯坦阁下:
        在我这里有一位研究生,已经攻读了五年的博士学位,如今即将毕业,在他提交的毕业论文中有一些新的想法………………
  请对他的论文作出您的评价。
  另外顺便向您提及,该研究生的父亲是弊国的一位伯爵,内阁 的**部长,若您……,将来您来法国定会受到隆重的接待

                           朗之万

  在信中,大约朗之万的潜台词似乎就是如果您不肯给个面子,呵呵,以后就甭来法国了。不知是出于知趣呢,还是出于当年自己的离经叛道而产生的惺惺相惜,爱因斯坦很客气回了一封信,大意是该论文里有一些很新很有趣的思想云云。
  此时的爱因斯坦虽不属于任何名门望派,却已独步于江湖,颇有威望。有了爱因斯坦的这一封信,评审委员会的几位教授也不好再多说些什么了。
  于是,皆大欢喜。
  浪荡子弟德布罗意就这样“攻读”下了他的PhD(博士)。
  而按照当时欧洲的学术传统,朗之万则将德布罗意的博士论文印成若干份分寄到了欧洲各大学的物理系。
  大约所有人都以为事情会就此了结,多少年以后德布罗意那篇“很新很有趣” 博士论文也就被埋藏到了档案堆里了。
  德布罗意大约也就从此以一个PhD的身份继续自己的浪荡生活。。
  但历史总是喜欢用偶然来开一些玩笑,而这种玩笑中往往也就顺带着改变了许多人的命运。
  在朗之万寄出的博士论文中,有一份来到了维也纳大学。

 
四.
  1926年初。维也纳。
  当时在维也纳大学主持物理学术活动的教授是德拜,他收到这份博士论文后,将它交给了他的组里面一位已经年届中年的讲师。
  这位讲师接到的任务是在两周后的seminar(学术例会)上将该博士论讲一下。
  这位“老”讲师大约早已适应了他现在这种不知算是平庸还是算是平静的生活,可以想象,一个已到不惑之年而仍然只在讲师的位置上晃荡的人,其学术前途自然是朦胧而晦暗。
  而大约也正因为这位讲师的这种地位才使得它可以获得这个任务,因为德拜将任务交给这位讲师时的理由正是“你现在研究的问题不很重要,不如给我们讲讲德布罗意的论文吧”。
  这位讲师的名字叫做──薛定谔(Schrodinger)
  在接下来的两周里,薛定谔仔细的读了一下德布罗意的“博士论文”,其实从内容上来讲也许根本就用不上“仔细”二字,德布罗意的这篇论文只不过一页纸多一点,通篇提出的式子也不过就两个而已,并且其原型是已经在爱因斯坦发表的论文中出现过的。
  然而论文里说的话却让薛定谔一头雾水,薛定谔只知道德布罗意大讲了一通“波即粒子,粒子即波”,除此之外则是“两个黄鹂鸣翠柳”──不知所云。
  两周之后,薛定谔硬着头皮把这篇论文的内容在seminar上讲了一下,讲者不懂,听者自然也是云里雾里,而老板德拜则做了一个客气的评价:“这个年轻人的观点还是有些新颖的东西的,虽然显得很孩子气,当然也许他需要更深入一步,比如既然提到波的概念,那么总该有一个波动方程吧”
  多年以后有人问德拜是否后悔自己当初作出的这一个评论,德拜自我解嘲的说“你不觉得这是一个很好的评论吗?”
  并且,德拜建议薛定谔做一做这个工作,在两周以后的seminar上再讲一下。
  两周以后。薛定谔再次在seminar上讲解德布罗意的论文,并且为德布罗意的“波”找了一个波动方程。
  这个方程就是“薛定谔方程”!
  当然,一开始德布罗意的那篇论文就已经认为是垃圾,而从垃圾产生出来的自然也不会离垃圾太远,于是没人真正把这个硬生生给德布罗意的“波”套上的方程当一回事,甚至还有人顺口编了一首打油诗讽刺薛定谔的方程:欧文用他的泼赛,计算起来真灵通:泼赛代表什么呢?没人能够说得清!(欧文就是薛定谔,psi (泼赛)是薛定谔波动方程中的一个变量)
  故事的情节好像又一次的要归于平庸了,然而平庸偏偏有时候就成了奇迹的理由。
  大约正是薛定谔的“平庸”使得它对自己的这个波动方程的平庸有些心有不甘,他决定再在这个方程中撞一撞运气。

五.

  上面讲到的情节放到当时的大环境中来看就好像是湖水下的一场大地震──从湖面上看来却是风平浪静。
  下面请允许我暂时停止对“老”讲师薛定谔的追踪,而回过头来看一看这两年发生物理学界这个大湖表面的风浪。
  此前,玻尔由普朗克和爱因斯坦的理论的启发提出了著名的“三部曲”,解释了氢光谱,在这十几年的发展当中,由玻尔掌门的哥本哈根学派已然是量子理论界的“少林武当”。
  1925,玻尔的得意弟子海森堡提出了著名的矩阵力学,进一步抛弃经典概念,揭示量子图像,精确的解释了许多现象,已经成为哥本哈根学派的镇门之宝──量子届的“屠龙宝刀”。不过在当时懂矩阵的物理学家没有几个,所以矩阵力学的影响力仍然有限。事实上就是海森堡本人也并不懂“矩阵”,而只是在他的理论出炉之后哥本哈根学派的另一位弟子玻恩告诉海森堡他用的东西在数学中就是矩阵。
  再回过头来再关注一下我们那个生活风平浪静的老讲师薛定谔在干些什么──我指的是在薛定谔讲解他的波动方程之后的两个星期里。
  事实上此时的他正浸在温柔乡中──带着他的情妇在维也纳的某个滑雪场滑雪。
  不知道是宜人的风景还是身边的温香软玉,总之是冥冥之中有某种东西,给了薛定谔一灵感,而就是这一个灵感,改变了物理学发展的轨迹。
  薛定谔从他的方程中得出了玻尔的氢原子理论!

六.

  倚天一出,天下大惊。
  从此谁也不敢再把薛定谔的波动方程当成nonsense(扯淡)了。
  哥本哈根学派的掌门人玻尔更是大为惊诧,于是将薛定谔请到哥本哈根,详细切磋量子之精妙。
  然而让玻尔遗憾的是,在十天的漫长“切磋”中,两个人根本都不懂对方在说些什么。
  在一场让两个人都疲惫不堪却又毫无结果的“哥本哈根论剑”之后,薛定谔回到了维也纳,薛定谔回到了维也纳之后仍然继续做了一工作,他证明了海森堡的矩阵力学和他的波动方程表述的量子论其实只是不同的描述方式。
  从此“倚天”“屠龙”合而为一。
  此后,薛定谔虽也试图从更基本的假设出发导出更基本的方程,但终究没有成功,而不久,他也对这个失去了兴趣,转而去研究“生命是什么”。
  历史则继续着演义他的历史喜剧。
  德布罗意,薛定谔都在这场喜剧中成为诺奖得主而名垂青史。
 
尾声
  其实在这一段让人啼笑皆非的历史当中,上帝还是保留了某种公正的。薛定谔得出它的波动方程仅在海森堡的矩阵力学的的诞生一年之后,倘若上帝把这个玩笑开得更大一点,让薛定谔在1925年之前就导出薛定谔方程,那恐怕矩阵力学就根本不可能诞生了(波动方程也就是偏微分方程的理论是为大多数物理学家所熟悉的,而矩阵在当时则没有多少人懂)。如此则此前在量子领域已辛苦奋斗了十几年的哥本哈根学派就真要吐血了!
  薛定谔方程虽然搞出了这么一个波动方程,却并不能真正理解这个方程精髓之处,而对它的方程给出了一个错误的解释──也许命中注定不该属于他的东西终究就不会让他得到。
  对薛定谔方程的正确解释是有哥本哈根学派的玻恩作出的。(当然玻恩的解释也让物理界另一位大师──爱因斯坦极为震怒,至死也念念不忘“上帝不会用掷色子来决定这个世界的”,此为后话)。
  更基本的量子力学方程,也就是薛定谔试图获得但终究无力企及的的基本理论,则是由根本哈根学派的另一位少壮派弟子──狄拉克导出的,而狄拉克则最终领袖群伦,建起了了量子力学的神殿。

新“百家姓”排名出炉 看看你排第几?

   根据国家自然科学基金委支持的一项最新研究表明,我国新的 “百家姓”顺序近日新鲜出炉。
新顺序是:
>1.李,
>2.王,
>3.张,
>4.刘,
>5.陈,
>6.杨,
>7.黄,
>8.赵,
>9.周,
>10.吴,
>11.徐,
>12.孙,
>13.朱,
>14.马,
>15.胡,
>16.郭,
>17.林,
>18.何,
>19.高,
>20.梁,
>21.郑,
>22.罗,
>23.宋,
>24.谢,
>25.唐,
>26.韩,
>27.曹,
>28.许,
>29.邓,
>30.萧,
>31.冯,
>32.曾,
>33.程,
>34.蔡,
>35.彭,
>36.潘,
>37.袁,
>38.于,
>39.董,
>40.余,
>41.苏,
>42.叶,
>43.吕,
>44.魏,
>45.蒋,
>46.田,
>47.杜,
>48.丁,
>49.沈,
>50.姜,
>51.范,
>52.江,
>53.傅,
>54.钟,
>55.卢,
>56.汪,
>57.戴,
>58.崔,
>59.任,
>60.陆,
>61.廖,
>62.姚,
>63.方,
>64.金,
>65.邱,
>66.夏,
>67.谭,
>68.韦,
>69.贾,
>70.邹,
>71.石,
>72.熊,
>73.孟,
>74.秦,
>75.阎,
>76.薛,
>77.侯,
>78.雷,
>79.白,
>80.龙,
>81.段,
>82.郝,
>83.孔,
>84.邵,
>85.史,
>86.毛,
>87.常,
>88.万,
>89.顾,
>90.赖,
>91.武,
>92.康,
>93.贺,
>94.严,
>95.尹,
>96.钱,
>97.施,
>98.牛,
>99.洪,
>100.龚。
  这项调查和研究是由中国科学院遗传与发育生物学研究所袁义达研究员主持完成的,历时两年。调查涉及全国1110个县和市,得到了2.96亿人口的数据, 共获得姓氏4100个。通过县、地区、省三级人口比例的统计,从而得到了当今中国新百家姓的排序。

今天把我的 Hotmail 变成了 Live Mail

  

图片:

图片:

今天看到在Neowin有人找到一个不用邀请也能把Hotmail 变成Live Mail Beta的方法,但成功率只有50%罢了!
也有人说你的hotmail用了3年的成功转换机会比较高,N多人都已经成功了!!!

1.首先要把你的个人资料改去
Language –> English
Country –> United States
State –> Florida.

2. 把这个网址LINK 放在浏览器(只是IE)
http://by101fd.bay101.hotmail.msn.com/cgi-bin/BetaOptIn?page=option&curmbox=00000000%2d0000%2d0000%2d0000%2d000000000001&a=b9a426ebd4880ad9d14db4b4c55a69f8bb8dea2282102422220030b2b6bb98c8

3.Sign in

4.你应该登入不到,再把网址放在浏览器。

5.当你看到"Participate",那么你就成功了。

 
 
Live Mail 功能介绍:
 
Live Mail 最主要的升级一是支持拖放,这样在网页上操作信件就和在终端上一样方便了,二是容量扩大到2G了。

zuma 和 giza 要是支持双人对战就好了

周围好几个人都和我讨论过,zuma 和 giza 要是支持双人对战就好玩多了。
就比如说我们组里的人吧,平时就喜欢吹牛,玩什么都说自己水平比别人高。
zuma 和 giza 不支持现场比赛,大家吹起来就更肆无忌惮了。
 
zuma 和 giza 也许可以这样支持对战:
第一种方式就和泡泡龙一样,两个人各自在自己的场地里玩,但是有一方销的球多了,就会随即的给对方添一些球。
 
另一种,zuma 和 giza 可以两个人在同一个场地里玩,两个人每人守主自己的洞口,不要让球滚进来,有空的时候还可以发几个球到对方的队列上给对手捣捣乱。

想通了一个魔术

    有一天坐在公交车上无聊的瞎琢磨事情,突然被我想通了一个魔术。这是我前两个月在电视上看到的一个传统中国魔术,是当时为一位魔术师做采访时他表演的。大致表演方法就是:在桌面上倒扣一只碗,扣下时,魔术师给你看那只碗是空的。然后魔术师手中拿一只鸡蛋,在倒扣的碗上这么一晃,鸡蛋就到碗里去了。那个魔术师吹嘘自己的外号叫鬼手,自己的手比观众的眼睛还看。不过我才不信他的鬼话,要想把鸡蛋赛今碗里,就一点会把碗掀起来一点,而人眼对于物体的运动非常敏感,加上图像在视网膜上的滞留现象,如果碗动了,是一定会被人眼捕捉到的。当然如果碗被掀起时间只有几个毫秒,那这个动作也许摄像机或电视漏过,电视每秒钟只能播放大约30帧,如果饭碗的动作少于30毫秒,或许真的会被摄像机漏过。但是我不相信人的动作有这么快,何况还有现场观众呢。
    这个魔术师肯定是拿了两个鸡蛋。在他把碗扣在桌子上的同时就放了一个鸡蛋在里面,这个动作使比较容易躲过观众眼睛的。然后再拿出另一个鸡蛋放在手里装模作样,手在碗上晃来晃去的时候把手里的鸡蛋藏在袖子里。这样给人的敢决就是把鸡蛋塞到碗里去了。
    这个魔术肯定不能用透明的玻璃碗来变:)