Sunday, November 30, 2008

CLRS ex5.1-2 之 我的想法

网上找了两份CLRS的答案,一份是民间的一份是官方的,都没有这题的解答。
跟两位大牛讨论了一下,觉得应该没什么问题了,发一个。

题目:
假设有一个随机数发生器R(0,1)能均匀产生{0,1}中的一个数,即P(0)=1/2,P(1)=1/2
利用它实现一个能产生[a..b]之间的随机数发生器R(a,b)

大致思路:
首先,R(0,1)能产生两个数
考虑由它得到R(0,b-a),能产生[0.. b-a]之间的b-a+1个数。
则a+R(0,b-a) 能产生[a..b]之间的b-a+1个数。

问题转化为怎么求R(0,b-a),举一个例子说明。
假设要得到R(0,4),即[0..4]之间的5个数。
考虑R(0,7)共8个数,调用lg8=3次的R(0,1)显然可以得到
{0,1}^3的3-串,相当于binary的0-7
它们是等概率分布的8个数(P(each) = 1/2*1/2*1/2 = 1/8)。

则问题又转化为如何由1/8等概率的数得到1/5等概率的数,
without loss of generality,设为前5个数。

Proposition:
运行3次R(0,1),如果得到(101,110,111) bin=(5,6,7) dec 则再次运行3次R(0,1),
直到得到前5个数之间的一个才返回。它们的概率是1/5.

Prove:
以运行3次R(0,1)为一个单位,假设运行了k次才得到[0..4]之中的一个,k -> infinite
即前k-1次都得到[5..7]中的一个,第k次猜得到[0..4]之中的一个(否则算法不结束)
令:
事件a="前k-1次为[5..7],最后得到[0..4]"
事件b="前k-1次为[5..7],最后得到一个确定的x \in [0..4]"
显然每次"抛骰子"都是独立的,易得
P(a)=(3/8)^{k-1} * 5/8
P(ab)=(3/8)^{k-1} * 1/8
则P(b|a)=P(ab)/P(a)=(1/8) / (5/8) = 1/5
即统计意义上,for i \in [0..4],b的概率为1/5

类似地,可得算法:
function: R(a,b)
input: int a,b
let: L = floor(lg(b-a))+1, size=2^L, result=0
repeat{
    result = 0;
    for i in [1 .. L]
        temp = temp << 1 + R(0,1);
    endfor
}until (temp < b-a+1);
return temp+a;

算法的期望运行时间:
for loop的运行时间为2^L
E[n] = sum( i*P(i) ) * 2^L
其中P(i)是算法repeat loop运行i次的概率,则P(i) = (3/8)^(i-1)  * (5/8)
sum里面展开后大概是个这样的东西:
1*5/8 + 2*3/8*5/8+3*(3/8)^2*5/8+....+n*(3/8)^{n-1}*5/8

另外bug大牛是这样算期望的:
E(n) = 1 + 上一步算法没结束的概率 * E(n-1)

好像也对,没有具体展开……

[zz]读博和大便的相同点

11:21 PM xxx: 标 题: 读博和大便的相同点
发信站: 两全其美网 (Thu Nov 27 17:12:23 2008), 本站(lqqm.net)


1.都是憋出来的
2.肚子里有货才爽,否则很痛苦
3.即便肚子里面有货,也得有paper才行
4.paper越多心里越踏实
5.paper上的都是屎
6.paper不能是别人用过的
7.paper虽然是别人用过的,但是看不出来就行
8.运气够好的话可以借到paper
9.没有paper的话,如果你很有钱也能解决问题
10.实在没有paper,直接拍拍屁股走人是很需要勇气的

Saturday, November 29, 2008

灰姑娘书(Cinderella Book)

Introduction to Automata Theory, Languages and Computation (Addison-Wesley Series in Computer Science)

很早以前,听过一本书的名字"灰姑娘书"
说是"一个小女孩儿,拼命地从一个古怪装置上拉一条绳子(那个恶魔般的装置上赫然写着"NP完全问题")"

今天突然碰到这个词,回想起封面,实在是感触万千……




Friday, November 28, 2008

给你n个数,其中有且仅有两个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那两个数。

是上次题目的拓展版:
(给你n个数,其中有且仅有一个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那一个数。)


这两个链接给出了很详细的解答。重点在后者,我终于见到有个人跟我的想法一致:
一道不知道如何做的题目,知道了答案,但是往往不知道我们大脑的思考过程。
对于我们这些一般人,往往很难有大牛一样"灵机一动"就想出来的答案(虽然这个灵机一动也是based on大量的做题经验),而是有一个思考过程。
pongba很详细地记录了我们大脑思考时所做的一些细节。


http://www.matrix67.com/blog/archives/511
http://groups.google.com/group/pongba/browse_frm/thread/76b57a772b5ca091#

Thursday, November 27, 2008

磨练UNIX工具技艺:.......盗链

在一个网站上我想下载月华剑士II的音乐集:
http://gh.ffshrine.org/soundtracks/599

发觉结构很简单,歌曲是以CD为目录存放的地址。
比如上面那个url就是编号为599的CD的url,链接进去有这支专辑的各首歌的对应url,比如第一首为:
http://gh.ffshrine.org/song/599/1

歌曲的真实地址也在这个页面里给出了。的确,浏览器里能看到歌曲的地址。
按道理很简单,直接wget就行了。

不过看了页面src后才发觉,事情没想象中那么容易。
歌曲的地址被javascript混淆过,是形如下面的一段东西:
<script>
var data = "var%20addr%20%3D%20%222 (此处删除若干) 0A%09%09";
eval(unescape(data));
</script>

那么首先要decodeurl的encoding(what's this? refer here: http://www.blooberry.com/indexdot/html/topics/urlencoding.htm)
(
一开头我搞错了,以为是html的entity问题,这个可以使用perl的HTML::Entities模块来完成,具体如下:
// data是形如"&nbsp; &lt;"这种东东
use HTML::Entities;
use decode_entities($data);
)
使用python可以很方便地decode,具体如下:
import urllib;
urllib.unquote("%20%3D");

于是得到了预期的东西:
'var addr = "http://dl1.ffshrine.org........ '

接下来,则要使用javascript的eval功能来执行这段javascript代码了……这里我考虑了两种方法:

1. 使用某个实际的html engine,如php…… 来得到最终链接地址
2. 使用某个独立的html engine,我搜索到了javax.script

具体代码如下:(script.java)
import javax.script.*;
import java.net.URL;
import java.io.*;

class script{
        public static void main(String [] args){
                ScriptEngineManager mgr = new ScriptEngineManager();
                ScriptEngine engine = mgr.getEngineByName("JavaScript");

                try{
                        URL url = new URL( "http://gh.ffshrine.org/song/598/1" );
                        InputStreamReader reader = new FileReader( "/home/iveney/workspace/test.js" );

                        Object obj = engine.eval(reader);
                        System.out.println(obj);
               }catch(Exception e){
                        e.printStackTrace();
                }
        }
}

最主要的一步是engine.eval,等同于原javascript中的eval.
编译能通过,但事实上他是有问题的。因为解码后我得到的是这样的东东(我把whitespace escape了):
var addr = "http://dl1.ffshrine.org/soundtracks/dl/598/00720b/Last Blade 2 Arrange/01_-_the_flower_that_blooms_in_the_moonlight.mp3";
function decode(address) {
var Address = "";
Address = address;
Address = unescape(Address);


function unescapesome(thesome) {
        var newString = thesome;
        newString = newString.replace(/%3A/g, ":");
        newString = newString.replace(/%27/g, "'");
        newString = newString.replace(/%28/g, "(");
        newString = newString.replace(/%29/g, ")");
        newString = newString.replace(/%21/g, "!");
        newString = newString.replace(/%7E/g, "~");
        return newString;
}

document.getElementById("linkcode").innerHTML = "<a href='" + unescapesome(escape(Address)) + "'>click here</a>";}

decode(addr);

里面有用到DOM model的document,发觉不能正常运行……
后来我没有深究究竟java这条路要怎么做,因为事实上我看到真实链接已经出来了 -。- 就在第一行!!!

于是马上用curl下载一系列的url,然后准备用相似步骤还原成原来的javascript得到地址。
但是没有得到预期的结果――还原成的是这样的东西:

var addr = new Array()
addr[0] = "http://dl1.f";
addr[1] = "fshrine.org/";
addr[2] = "soundtracks/";
addr[3] = "dl/598/00720";
addr[4] = "b/Last Blade";
addr[5] = " 2 Arrange/m";
addr[6] = "vovld01_-_th";
addr[7] = "e_flower_tha";
addr[8] = "t_blooms_in_";
addr[9] = "the_moonligh";
addr[10] = "t.mp3";
function decode(address) {
var Address = "";
for (var i = 0; i != address.length; i++) {
Address += address[i]
}
Address = unescape(Address);
Address = Address.replace(/\/mvovld/, "/");


function unescapesome(thesome) {
        var newString = thesome;
        newString = newString.replace(/%3A/g, ":");
        newString = newString.replace(/%27/g, "'");
        newString = newString.replace(/%28/g, "(");
        newString = newString.replace(/%29/g, ")");
        newString = newString.replace(/%21/g, "!");
        newString = newString.replace(/%7E/g, "~");
        return newString;
}

document.getElementById("linkcode").innerHTML = "<a href='" + unescapesome(escape(Address)) + "'>click here</a>";}

decode(addr);
我日!原来地址还被一个function wrap着呢!
难道又要我用java来解释吗?
但是为什么我直接查看页面源代码会得到已经被解释过的javascript代码呢?

我猜测原因是因为referer的问题。用curl我没有构造合适的http参数,也许原来的页面有检查我是从哪个referer进去的。
然后我换wget试了试,却惊讶的发觉问题解决了:我能用wget得到正确的解释过的url……
由于最近忙,这里我先不深究,迟点问高手。

最后的shell代码如下:
#!/bin/bash

DIR=/tmp/mp3
#curl "http://gh.ffshrine.org/song/598/[1-12]" --create-dirs /tmp/mp3 -o "/tmp/mp3/#1.html"
mkdir $DIR
for i in `seq 1 48`
do
       wget http://gh.ffshrine.org/song/599/$i -O "$DIR/$i.html"
done

for i in `seq 1 48`
do
        str=`grep "var data" "$DIR/$i.html"`
        str=`echo $str | cut -d '"' -f 2`
        #echo $str
        command="import urllib; js=urllib.unquote('$str'); print js"
        # we get javascript now
        js=`echo $command | python`
        addr=`echo $js | head -n 1 | cut -d \" -f 2`
        echo $addr
        a=`wget "$addr" -P /win/d/Music/LastBladeIIOST/`
done

上面用到了不少工具,待我稍微注释。
首先是一个for loop用wget把所有页面下回来,存到相应位置。注意-O是指定输出文件名,而-P是指定输出的目录。
然后一个for loop处理decode stuff。
1.把该html文件中有用的那一段grep出来。
2.用cut把引号部分提取出来
3.用python decode之
4.再用cut把链接提取出来
5.用wget进行下载

今晚浪费了不少时间,不过当作锻炼一下自己吧。

Wednesday, November 26, 2008

恭喜恭喜:我收到了一封钓鱼邮件

系统 向您发送了一条消息‏
发件人: Windows Live (WindowsLive@live.com)
发送时间: 2008年11月26日 11:56:59


尊敬的MSN用户:
        
     您好!MSN中国网络为了回馈广大新老用户对我们的支持与厚爱。
 
特在国庆期间联合IBM中国有限公司举行国庆庆典抽选活动。
 
在此很荣幸的通知您,您以被MSN中国网络后台系统抽选为{幸运用户}
 
请你牢记您的辛运码 〖5f826〗 您将获得由IBM中国有限公司为您提供
 
丰厚奖金与奖品! 

                                                 领奖专网: http://msn.spasec.cn    
特此通知,顺至祝贺!                      领奖专线: 08-986-986-6268



----------------------------------------------------------------------------------
从网页链接可以看到,它想模仿msn.space.cn
不过它傻得很,竟然不用文字链接,而是直接给出了url。

while(1) iveney.says("bs");

一道题目:2n+1个数中,n个数出现两次,1个数出现1次。O(n)求只出现一次的数

newsmth上的cfans给出了一个很好的解释,
适用于此种题目的generalized版本:
一个元素个数为(n*s+t)的序列里,有n个数出现s次,s是偶数,有1个数出现t次,t是奇数。
求出现奇数次数的数。
转载如下:

N个数,只有一个出现奇数次,其它偶数次,基于
的数学原理是
a xor b xor c
=(a xor b) xor c  = a xor (b xor c)
满足结合律
a xor b = b xor a
满足交换律。

a^(2n)=0
a^(2n-1)=a
a xor 0 = a
a xor 1..1 = bitwise not a

<{0,a}, xor>构成一个群。
0是单位元,a是"0元"。
所以异或下来,就剩下那个数了。
这个题目不行。

因为那个数出现两次,根据结合律和交换律,
必然可以写成 …… (a xor a) ……
=…… xor 0 = ……
0对于异或操作是单位元。

对于这道题目,显然出现两次的数异或后都为0了。而0异或上任何数还是0.
最后异或得到的结果就是那个数。

Tuesday, November 18, 2008

万恶的ILOG

传说中几十万一个license的business,logistic, ....的 constraint solver

在俺手中简直就是废物。

 

建模对我等用惯图灵机工作思维的人来说简直就是折磨。

有一次一个简单的8*8马周游建模不好,竟然能跑我3个小时。

 

最近几天在做最后的一个assignment,快抓狂了,写goal怎么写也写不好。

 

不过无可否认它的实现还是很漂亮的,C++的类、模板等特性被发挥得淋漓尽致。

我还是第一次接触到这样的代码。

 

两套hierachy,一套Ilo开头,用于建模,一套Ilc开头,用于solve model。

非常的extensible与flexible,可以自己定义search时的heuristic,也可以自己定义search的algorithm。另外当然还有很多可能性,不过俺接触到的基本上就这么多了。

 

郁闷,已经在它身上折腾很多时间了。况且估计我以后也用不上。

Saturday, November 15, 2008

永远的KOF

呵呵!本人是个和拳皇一起长大的孩子,很喜欢拳皇哦!下面我就讲些我知道的有关拳皇的分歧问题吧!(如果你们还有点什么问题都可以问我,大家可以一起讨论,鄙人喜欢把草剃京称为小草。)  
首先公布一下SNK的官网http://www.snkplaymore.jp  
1.关于主角京的名字问题。  
KYO KUSANAGI中文名字是草剃京,中间这个字读Ti第四声。草剃这个是日本姓氏实际意思基本没多大,和三神器有关。有种就是字面意思:割去草丛。日文"京"  
的翻译是{きょ}  
(这个问题其实很老土了,但是就是很多朋友都把京说成草稚或草智,个人认为比较汗死~~)  

2.关于漫画问题。  
现在我们大部份看的都是香港版本的漫画,是经过SNK授权,可以编写拳皇漫画的公司。目前发现有三个作者:司徒剑桥,许景琛,永仁・蔡景东。  
(其实这些漫画都是为了满足读者的需要,不然我们都没的看了,就靠官方发表的那点东西我们是会饥渴死的,但是有些东西还是最好参照官网,毕竟那更正版一点)  
个人认为哈:港漫的画风比较舒服,看起来更清晰一点。  

3.关于八神的女朋友问题  
香港漫画里是三角恋,八神喜欢香澄,香澄喜欢小草而不喜欢八神,而小草也喜欢香澄。这样复杂的关系也只有香港人才想的出啦。  
在官方网里其实没这回事,根本没这样的事香澄和他们俩个人都没任何特殊关系。小草的女朋友只有一个,就是YUKI雪。而八神的呢,就是有俩个爱音 乐的女孩子同时爱上八神在KOF99出版前后,SNK出版了一张八神庵个人DRAMA专辑CD,透露关于八神庵与曾经的女朋友――谷间菊理的故事,就是这 张八神庵{夕阳与月},谷间近卫和谷间菊理姐妹同时爱上八神庵,然而谷间菊理最后却病倒了。这个CD的故事并没有结束,半个月后SNK举办的八神庵 『ALONE』 舞台剧,表演的是一年以后,谷间近卫为了就要死去的妹妹菊理去找她爱的庵见最后一面,但庵已经离开她们1年。最后找到了庵,但是庵还是没 有回去看菊理。菊理希望姐姐能为了姐姐自己而活,因为姐姐才最需要庵。最后,庵的飞机起飞。  
(不管你喜欢什么版本的都好,至少我们都是爱拳皇的,希望以后不要再骂人了。)  

4.关于OROCHI的超必杀的问题。  
这个问题是太有意思了,自己想了都想笑,先就用最常用的逻辑想一下,为什么有人会叫他那招全屏攻击的是阳光普照呢!大蛇不是正神,而且日本的太阳 神是天照,太阳是天照就不存在大蛇的阳光普照了吧,中文我们可以翻译成{空间毁灭}因为大蛇的能力是控制空间的神。这个必杀的官网英文名是 Shower right on the whole screen。  

5.关于K",八神和小草的实力问题。  
这个问题我想不应该用香港漫画来讨论吧,如果要讨论至少也不应该对骂。因为港漫有比较大的夸张手法在里面,里面的每个人都几乎可以摧毁一个城市 了。如果有真的想比一下他们能力的话就请你们去官网看一下吧。港漫要比也可以,但是绝对鄙视骂人,BAIDU现在是国际性的网了,让别人看到这会说我们中 国人没素质。  
(我们都喜欢拳皇,这个问题就象你家有俩个哥哥,你都非常喜欢他们,要分出他们谁更厉害是不是要叫他们俩对打一次呢?)  

6.京的真假问题。  
这个问题最有争议,先说香港漫画吧,香港漫画是为了符合游戏内容而写的,也就是说先有游戏才有港漫,港漫里99以后到01的小草也是复制的,这样 基本符合了游戏出场的顺序,基本合理了。如果不是复制的话那么02那个里草剃怎么出场啊,是吧!NESTS。又给人家毁了,总不能说又是他们复制出来的 吧!其实写漫画的作者也很辛苦了,一年要出一本满足大家的胃口也很难了。  
然后再说下官方的吧,在官网里99―2002的京都是真正的京,不是复制的。而02里面那个{里草剃}其实是官方为了纪念学生装的京而设计出来 的。而且2002年是没有任何故事情节的,单纯性的游戏,就象是98的纪念版本一样。98也是没有故事情节单纯性游戏。上面俩个京的配音演员也是不同的, 真小草的是野中政宏。  
(还有个非常明显的证据,只有真的京才有神尘吧,港漫也是这说的,但是游戏里为什么01的京有神尘呢?很假吧)  

7.关于爆走或失控的问题。  
先说漫画里面的吧,八杰集,八神,LEONA,会爆走,因为他们都有OROCHI的血。正常!然后是京,那个完美的复制京也爆走了一次,或者说是 罗刹化,那是因为草剃家族心魔的问题。仇恨越大力量就越容易失控……(香港漫画写的很好就在于他写的很符合现在人性, 比较容易让人接受)  
看下官网吧,上面明确的说了只有拥有OROCHI BLOOD才会爆走或者失控,失控者的名字都要加上OROCHI。例如:OROCHI IORI ,OROCHI LEONA。  
还有就是97八神和LEONA的爆走根本不是因为其他原因,漫画里写的是LEONA打烂了山崎收集的能量杯才爆走的,其实不是。是因为拥有OROCHI BLOOD的生命体在OROCHI出现的时候都会不受控制的本能爆走,目的是迎接他们主人的出现。  

8.关于中国和日本的问题  
对不起,跑下题哈。很多人都骂我们喜欢拳皇的人是卖国贼,现在抵制日货也把我们这些喜欢拳皇的人给抵制了。  
其实我想说的是:现在的拳皇在中国范围来说都已经中国化了,我想大家也有这样的感觉吧!对八神和小草的称呼都是中文吧,讨论的话题也是拳皇而不是 THE KING OF FIGHTS吧!对于我们来说我们喜欢的是一种文化和艺术,我们鄙视的是日本的军阀主义和日本的政治。就象音乐一样,音乐是没国 界的,它只是一种形式而已。爱拳皇更爱我们的祖国,只是有些时候有点恨国人不争气的感觉,为什么中国就写不出或画不出那么好的漫画呢??那么骂我们的人你 们就没喜欢过日本的东西吗?你们就真的还要象清朝一样再一次的闭关锁国吗?  
(终于写完了哟!都是我一个字一个字打上去的,有同感或者也喜欢的拳皇的朋友就回复我一个啦!最好是留点意见哟,有问题也可以问我啦!祝我们和拳皇一起快乐成长) 相册 http://kyoming.photo.163.com   
                                                                    待继……  
                                                                                   流浪的魔 

Thursday, November 13, 2008

Monday, November 10, 2008

屁放得响,能顶市长

猥亵门后 各种"屁"说网上曝红


一直被中共说成"当家作主"的主人,突然成了"屁",中国网民在惊诧之余,仔细想想,确是事实。在中共当权者的眼里,中国大众的地位一直是个"屁",只是这次被林书记撕开了公仆母子的幌子,一不小心点了出来。一时间,各式各样的"屁"说在大陆互联网热起来。

"由"你们算个屁"说开去"
"一个屁的思考"
" 论"屁" "
"论民之屁"
… …

猥亵门事件带给民众的最震惊的莫过于林书记的口出狂言和嚣张:

"你知道我是谁吗?我是北京交通部派下来的,级别和你们市长一样高,敢跟我斗!"  
"我就是干了,怎么样?要多少钱你们开个价吧。我给钱嘛!"
"你们这些人算个屁呀!敢跟我斗,看我怎么收拾你们!"
"我掐了小孩的脖子又怎么样?!"

虽然开始这些语言让很多网民震惊和愤怒,但很快越来越多的人认识到其实林嘉祥说的虽然难听,但却是"大实话": "5000年来最震撼人心的一句大实话:你们算个屁!"

"别人愚我们,我们自己愚自己,愚得不亦乐乎!现在,林嘉祥老爷酒后吐真言,大量愚民才突然惊醒:闹了半个世纪,原来我们算个"屁"!"

"说我是个P,我呆住了,因为以前我是死要面子,不管愿意与否,他还是很给我面子的,但是今天我真的是愕然!"

"我忽然有点感动,历来我对党的干部千篇一律的面孔,虚伪的作风颇有反感,可林书记的出现让我知道,领导干部并不都是虚伪的,也有敢于表露自己的真实想法, 表达自己心声的,那句"我就是干了,你们算个屁啊"在我耳畔久久回响。"

在种类繁多对于"屁"的讨论后,据悉,在大陆一些论坛,"你们算个屁"已经被屏蔽,网民戏称:"连屁都不能放了!"

改名篇

由于人民在中共官员的眼里成了 "屁",一些网民因此建议一些相应的说法要随之改名:"中国屁银行;屁日报;屁大会堂;屁币;中华屁国…"

"如果人民是屁的话,那人民政府就是屁政府,人民公仆就是屁公仆,人民法院就是屁法院,人民教师就是屁教师,全国人民代表大会就是全国屁代表大会!等等类推… …"

"热烈庆祝中华屁民共和国正式成立"

觉悟篇

"你们算个屁啊!"------说出了所有公仆们的心声,明确了人民在他们心目中的地位。

为中国屁民的觉悟欢呼--------------你们终于明白了,你们就是一个屁

许多事情以前事情不理解,只要认识到自己是个屁,也就全明白了。

屁的封号已经高抬了,知足吧。实际连屁都算不上。

林熟鸡一不小心说了真话

原先还自己挺当回事的,现在才明白,我们老百姓在大大小小的书记们的眼里竟然是个屁。

林大淫的一句名言将我从幻想中震醒,原来我压根不是啥中国人,我就是一中国屁,林大淫才是那真正的中国人。

林大人的这句屁话,比我朝有史以来所有官员发表过的重要讲话的重要性的总和还要重要十倍,跟这句屁话比起来,天朝任何官员的所谓重要性讲话统统屁都不是。

林大人的这句屁话,很熏很震撼,用浅显易懂的话语,深刻的诠释了马克思先生那句最为精辟的论断――专制制度的唯一原则就是轻视人类。

反抗篇

用十三亿个屁崩死他们

全中国的屁联合起来,把他们崩到大海里

屁就屁吧,屁多了也许能噎死畜生们。

每个屁各自放确实只是P,假如一起放进一根管子,恐怕不是P,而是ATOM BOM(原子弹)。

十三亿一起放屁把腐官轰出太阳系

越憋可燃气体的浓度越大,甚至遇到火星就燃烧,就爆炸。

屁已经有13亿,只缺少火。

屁者,腹中之气也。偶发,气息微微;群发,则气贯长虹也。

当把10亿个屁点燃,一定非常壮观,那是一片火海,把人间地狱烧毁 。

搞笑篇

我一直为我的人生定位苦恼,不知道自己到底属于哪个群体?
是知识份子?工程师?公民?刁民?还是顺民?
但是,听了林书记的一番话
让我茅塞顿开,原来,我就是一个P呀,
害得我苦想了那么久!

这地方不能呆了,俺打洞去,子子孙孙打下去,不信到不了美利坚。

"想想也滑稽,既然我等是个屁,那你等是管理我等的,自然亦只是个肛门罢了!实在也高不了多少。"

古老的儿歌:屁是一股气,出门上西地,不吃青苗青草,惹得驴子生气。

君之屁,其香如兰,臣在下风,俯仰敞鼻,满朝文武尽作深呼吸状,好香一个屁。
民之屁,大大方方,直来直去,贪官酷吏,防民之屁甚于洪水猛兽,憋曲一团气。

本是官中狼,酗酒更猖狂;鲜花随便采,花朵更爱尝;草民算个屁,京官大如山;闭门苦中乐,和谐作笑谈。

林嘉祥之歌――你们算个屁

你们算个屁,
我不屑搭理你。
我是北京派来滴,
有权有势,你敢咋滴?
摸你小妮又咋滴。

你们算个屁,
我早晚收拾你。
我是北京派来滴,
有钱有人,你敢咋滴?
掐你脖子又咋滴。

你们算个屁,
我后台够硬滴。
我是北京派来滴,
欺男霸女,你敢咋滴?
给两臭钱摆平你。

你们算个屁,
说出来吓死你。
我和市长是兄弟,
黑白两道,畅通无忌。
这世界是我们滴。

论民之屁

林高官腆肚斥骂女童父母曰"尔等无非屁也",坊间以为该林辱及庶民,于是乎口诛笔伐也。余则谓:林高官之以屁喻民,实乃官人待民之道者也。林高官无非酒后真言,而张王李赵等官人,不言而心有戚戚焉者也。

盖官人也者,官前而人后也,先官而后人也,非官者则非人也。是以草民人等既非官,则必非人类也,于是乎无非屁也。

然则林高官果官人乎?又非也。林高官之上,又有高官在焉,高官视林等小吏,又非人类也,无非又一屁也。以是观之,则林高官与民等同,实乃一屁也。

然乎?又非也。林高官之流与屁民实难相较也。盖今日之屁民,泱泱乎,噪噪乎,上口可禁其声,下口难塞其屁,亿万之众,鼓腹回荡其气;不分男女,只需 人皆一屁,则乾坤之间,浑然充斥,其臭如兰,其色如火,阴霾为之冲荡,贪腐为之廓清,其烈烈之势,又岂是林高官之流可逆阻者欤?

以是观之,则林官人等以屁喻民,宜也。试看明日林官人者流,必将于民之大屁中窒息而毙也,信矣哉!民之屁!


来源
http://www.21whpp.com/pbbs/viewtopic.php?f=15&t=36

Sunday, November 9, 2008

中山大学驻香港校友发来贺电

十一月九日(星期日)下午七�正,中山大�香港校���晚�于香港金��一中心4�名都酒�正式�行。
据非正式统计,晚会酒席达近七十席。
李萍副校�、党委书记郑德涛、曾�梓博士等应邀出席并致辞。
中��彭清�副主任、香港政府保安局李少光局�、基本法委��梁��副主任等亦于今晚出席嘉宾,为校庆晚会增色不少。
在晚会正式开始后,全体校友起立并高唱了校歌。
党委书记郑德涛代表黄达人校长发表了庆祝校庆的演说。
众校友欢聚一堂,其乐融融,在表演台上还伴有各种歌舞表演。
晚会气氛一直异常高涨,酒店负责人亦称是近年来气氛最热烈、场面最感人的一次。
晚会结束后,各位校友纷纷上台合照,其中尤以法律系校友为宾友满堂。
众校友纷纷表示,此次晚宴组织妥当、气氛浓烈,给广大来港工作、进修的校友提供了一次聚首一堂,增�友�的机会。
同时,这也是一次不可多得的再会校友、祝福母校的盛晚宴。
在此谨祝各位中大学子学业事业进步,母校中山大学今后再创辉煌。

最后送上谢震龙校友撰写的校庆祝辞:
中藏华夏五千年文化栽培桃李日壮大
山聚乾坤亿万载正气作育英才扬国学


以上为严肃版本,私人版本及相片稍后再上。

LaTeX的画图宏包/工具太多了……

PSTricks, pgf/tikz, metapost, qtree, asymptote, graphviz .......
可视化的有 dia, kivio,inkscape........

虽说选择多种多样是好的,但是每一种都差别很远,也没有哪种是绝对最好的。
用起来有点痛苦,毕竟只是偶尔用一下,如果要精通就太浪费时间了,但是不精通又很快忘记。

这里大概记录一下吧。
PSTricks: 没用过,不表。据说不是很通用
pgf/tikz: 功能很强大,可以直接嵌入在LaTeX里使用,可以画几乎任何的图,另外有专门画树的功能,还能画自动机。2.0的manual竟然有560页。
metapost: 最nb的东西,可惜觉得语法有点脑残,不想去学…… 也是可以画很多东西,得到的是mps文件可以直接includegraphics
graphviz:用一种叫DOT的语言,画图论的图、自动机、目录树、关系图、ER图最方便了,但是因为是自动计算坐标位置,所以有时可能不能完全得到自己想要的位置。
支持输出为多种格式,甚至能直接输出为tex源文件(pgf/tikz语法)
asymptote:据说很nb的工具,可以支持3d效果,但是还没用过。

dia:类似visio,当然没visio那么强大,不过已经能凑合着使用了。
kivio:同上,功能似乎还弱点。
inkscape:画矢量图的利器,以前用过一次,但是觉得java运行起来有点慢就没用了。

Wednesday, November 5, 2008

[zz]林书记事件和谐版

作为受几十年教育的平凡人,作为一个有理想,有道德的大龄青年,我认为大家冤枉了我们的林书记,通过仔细研究视频,我认为事情的经过是这样的:林书记工作非常忙,日理万机,下班后根本没时间回家,还要利用吃饭的时间和一女人讨论建设祖国的事情,讨论到关键时,甚至于上厕所的时间也没有,林书记一边憋尿,一边对加强祖国的建设发表真知灼见。说时迟,那时快。正好有一11岁小学女生经过,林书记出于对祖国花朵的关心,详细地询问了小女孩的生产,生活及学习情况,并对小女孩的成长提出了宝贵意见。林书记终于憋不住尿了,为了不随地小便,污染环境,德高望重的林书记起身前往厕所。小女孩深深地为林书记的伟大人格魅力所吸引,于是也跟着林书记往厕所走,边走边汇报自己在过去的一年所取得的伟大成绩。到了厕所门口,学过中医的林书记不顾自己已经被尿憋得很难受,伸出双手放在小女孩脖子动脉处进行了一些把脉,通过诊断,林书记发现小女孩身体发育比较慢,于是林书记邀请小女孩进厕所,以便他注射一些蛋白质到小女孩体内,以促进小女孩的生长发育。小女孩被感动的哭了,坚决不接受上了年纪的林书记的恩惠,于是就跑开了.....  
这样的好书记,真是干部的榜样!

zz[电脑教程]令人目瞪口呆的四位世界级电脑大师

zz verycd.com 前面几段可以跳过去
g950g950 (楼主)  2007/8/9 顶楼 举报
「任何先进的科技一定是一种难以察觉的神奇力量。」

巫师一生之中一定都会有着一把拥有力量并潜藏危险的 "魔术法杖" 。巫婆会有属於他自己
啄养的 "神奇妖精" ,可以利用它们来制造大灾难。当然神秘术士会有他自己利用树木和铁
罐制作出来的 "泥人" 来听从他的命令。而我们也有我们的 "个人电脑"。

一台拥有自己生命且拥有强大创造力的 "神秘仪器" 。通常电脑会对滑鼠的移动有反应,或
是会利用任务执行结果来回应你念过的 "咒语" ,而且可以不利用 "超自然的力量" 来创造
你无法想像的惊人结果。虽然电脑可以执行我们给的命令,但是却难以平伏我们对 "电脑神
奇力量" 的好奇。

但时常发生的是电脑耍脾气、反抗你,甚至把你原本整齐的数字表搞得一团乱,你小心的输
入你的词句以及你漂亮的艺术创作,却被搞砸了。当这些事情发生时,我们确信我们是被电
脑耍了,电脑并没有在我们的控制之下,电脑正在尝试把我们原本有条不紊的东西搞得一团
乱。

无论个人电脑是我们忠实的 "仆人" 还是 "小恶魔" ,我们都可以知道一件事,就是这个沈
默的 "潘朵拉铁盒子" 里有着无数不为人知的秘密。打开它封印的 "潘朵拉铁盒" ,你会发
现到一堆毫无表情的元件,而我们只有少数的线索可以知道哪些是什麽。电脑内部大部分都
是长的像 "狮身女怪" 的微晶片,而且只提供一些奇怪的 "咒语编码" 在它那无法看透的表
面。其中还有 "电子回路迷宫" 、像是漂亮却毫无意义的 "象形文字" 。还有一些是非常重
要的元件,例如长得像 "法老王墓碑" 上印有危险、禁止拆解的硬盘和电源供应器。

一种值得深思熟虑并怡然自得的目标,我们可以深切地去思考你每天面对好几个小时的这个
 "潘朵拉的铁盒" ,知道他内部是如何运作来达成你的需求。如果这样可以解决你的问题或
让你发现新问题,那它就达到他的功用了。
=======================================================================
1.公元1998年在我�高二那年,那还是我用Windows 98的时候有次我系统崩溃了,

因为我那时还是个电脑白痴,我同学帮我介绍了一个电脑高手来帮我修电脑。

他看了一下电脑,问我有没有Windows 98的光盘?,我说没有。

他想了一下,叫我把家里电话拿给他,我心里想说修电脑要电话干什么?

但人家是电脑高手,我也不好意思说什么,就把电话拔下来给他了。

他把电话线空着的一头接在主机版的一个插孔内,然后进入了DOS,

然后就开始在电话上不停的按着键,他按键的速度非常快,但是他都只按电话的0,1两个键


我搞不懂这有什么用?但也不敢问,看了半个多小时,他还是不停的按这两个键,

我渐渐的有些困,我问他这东西要搞多久?他说还要几个小时,我给他倒了杯咖啡,

就一个人去隔壁书房睡觉了。

醒来的时候,一看已经过了5个多小时,我起身到隔壁,看见他正在Windows 98里面调试,

过了一会儿,他说,你试试,我坐上椅子用了一下,真的好了,我当时也不懂电脑,

谢过人家就走了。

后来我慢慢对电脑有了一些概念才了解到,

原来当时那位电脑高手是使用机器语言编了一个Windows 98系统

从我给他的电话直接输入到硬盘,

我后来问我同学那位高手的下落,我同学说前几年去了美国之后,杳无音讯....
========================================================================
2.公元2000年在我�大一那年,那还是我用Windows Me的时候有次我系统崩溃了,

因为我那时还是个电脑入门,我朋友帮我介绍了一个电脑高手来帮我修电脑。
  
他看了一下电脑,问我有没有Windows Me的安装盘?我说没有。
  
他想了一下,叫我把一张空的DVD刻录盘和一根奈米细针拿给他,

我心里想说修电脑要刻录盘和奈米细针干什么?但人家是电脑高手,我也不好意思说什么,

於是我就把DVD刻录盘拿一张来给他了。
  
他把奈米细针头对着刻录盘戳,他戳的速度非常快,但是只戳深或浅,

我搞不懂这有什么用?但也不敢问,看了半个多小时,他还是不停的戳着DVD刻录盘,

我渐渐的有些困,我问他这东西要搞多久?他说要几个小时,我给他倒了杯葡萄酒,

就一个人去隔壁客厅沙发上睡觉了。
  
醒来的时候,一看已经过了6个多小时,我起身到隔壁,看见他正在Windows Me里面调试,

还装上了Office、Photoshop、迅雷、魔兽世界等软件……过了一会儿他说,

你试试,我坐上椅子用了一下,真的好了,我当时也不懂电脑,谢过人家就走了。
  
后来我慢慢对电脑有了一些基础,终于明白了,

原来当时那位电脑高手是用奈米细针头刻了一个单面双层的DVD,在里面刻上了Windows Me
、Office、

Photoshop、魔兽世界的安装程序,我后来问我朋友那位高手的下落,

我朋友说前几年去了法国之后,杳无音讯....
========================================================================
3.公元2001年在我�大二那年,那还是我用Windows XP的时候有次我不小心把D盘格式化了


因为我那时还是个电脑新手,我亲戚帮我介绍了一个电脑高手来帮我的电脑D盘恢复数据。
  
他看了一下电脑,问我有没有Windows XP备份过的Ghost?我说没有。
  
他想了一下,叫我把一块奈米强力磁铁棒拿给他,还问我D盘里有什么东西?

我心里想说修电脑要奈米强力磁铁棒干什么?

但人家是电脑高手,我也不好意思说什么,就把奈米强力磁铁棒给他了,

还告诉他我的D盘里面全是火影忍者的动画。
  
他把整颗硬盘拆了,用奈米强力磁铁棒在盘子上面画圈圈,他画圈圈的速度非常快,

但是他的奈米强力磁铁棒好像有碰到又好像没有碰到,我搞不懂这有什么用?但也不敢问,

看了半个多小时,他还是不停的在硬盘上画着圈,我渐渐的有些困,

我问他这东西要搞多久?他说要几个小时,我给他倒了杯�格�威士忌,

就一个人去隔壁房间睡觉了。
  
醒来的时候,一看已经过了7个多小时,我起身到隔壁,看见他正在D盘里面调试,

里面全是火影忍者的动画片,过了一会儿,他说,

你试试,我坐上椅子用了一下,真的好了,我当时也不懂电脑,谢过人家就走了。
  
后来我慢慢对电脑有了一些体会,终于体悟到,

原来当时那位电脑高手是用奈米强力磁铁棒直接在硬盘上写数据,

他凭着惊人的记忆力将他曾经看过的火影忍者动画片数据都写入了硬盘,我后来问我亲戚那
位电脑高手的下落,

我亲戚说他前几年去了英国之后,杳无音讯....
========================================================================
4.就在最近,我刚装上的Vista突然自己崩溃了

就在最近,我刚装上的Vista突然自己崩溃了,虽然我会写很多程序,但是Vista崩溃了我也


有办法,我朋友给我介绍了一个高手来帮我修电脑。

他看了一下电脑,问我有没有Vista的安装盘,我说没有。

他想了一下,叫我拿一根没用的网线和一把剪刀,我想修电脑要网线和剪刀干什么,但人家

是高手,我也不好说什么,就拿了一根没用的网线和一把剪刀给他。

他把网线一头戳到网卡上,剪断另外一头,然后就在哪里不停的拨弄那八根线。他拨弄的速

度非常快,但是拨弄得线总是不一样,我搞不懂这有什么用,但也不敢问,看了半个多小

时,他还是不停的拨弄着网线。我渐渐的有些困,我问他这东西要搞多久,他说要几个小

时,我给他倒了杯茶,就一个人去隔壁睡觉了。

醒来的时候,一看已经过了4个多小时,我起身到隔壁,看见他正在Vista里面调试,还装给

装好了Visual Studio 2005 Team Suit、Ms SqlServer 2005 Enterprise Edition、BT,还


下载边看American片……过了一会儿,他惊觉我站在后面,不好意思地对我说,你试试。我

坐上椅子用了一下,真的好了,我当时太震惊了,整个人傻在哪里,谢过人家就走了。

后来我读到了前面那两篇文章,终于醒悟,原来当时那位高手是用网线模拟网络启动,下载

了整个Vista,还有Visual Studio 2005 Team Suit、Ms SqlServer 2005 Enterprise
Edition、

BT,以及他正在看的American片,我后来问我朋友那位高手的下落,我朋友说前几年去了

美国之后,杳无音讯了十几年,期间也回过中国两次,最近这一次回来说,干这个太没意思

了,回来收拾收拾准备去阿尔法半人马座,他说他也听不懂那高手说什么,只是最近传闻微

软的Vista全世界都无法激活,估计跟他的离去有关,当然了,这几天也是杳无音讯....

Monday, November 3, 2008

[zz]C/C++中的序列点

From newsmth:

0. 什么是副作用(side effects)

C99定义如下
Accessing a volatile object, modifying an object, modifying a file, or
calling a function that does any of those operations are all side effects,
which are changes in the state of the execution environment.

C++2003定义如下
Accessing an object designated by a volatile lvalue, modifying an object,
calling a library I/O function, or calling a function that does any of
those operations are all side effects, which are changes in the state of
the execution environment.

可以看出C99和C++2003对副作用的定义基本类似,一个程序可以看作一个状态机,在
任意一个时刻程序的状态包含了它的所有对象内容以及它的所有文件内容(标准输入
输出也是文件),副作用会导致状态的跳转

一个变量一旦被声明为volatile-qualified类型,则表示该变量的值可能会被程序之
外的事件改变,每次读取出来的值只在读取那一刻有效,之后如果再用到该变量的值
必须重新读取,不能沿用上一次的值,因此读取volatile-qualified类型的变量也被
认为是有副作用,而不仅仅是改写

注,一般不认为程序的状态包含了CPU寄存器的内容,除非该寄存器代表了一个变量,
例如
void foo() {
  register int i = 0;  // 变量i被直接放入寄存器中,本文中被称为寄存器变量
                       // 注,register只是一个建议,不一定确实放入寄存器中
                       // 而且没有register关键字的auto变量也可能放入寄存器
                       // 这里只是用来示例,假设i确实放入了寄存器中
  i = 1;  // 寄存器内容改变,对应了程序状态的改变,该语句有副作用
  i + 1;  // 编译时该语句一般有警告:"warning: expression has no effect"
          // CPU如果执行这个语句,也肯定会改变某个寄存器的值,但是程序状态
          // 并未改变,除了代表i的寄存器,程序状态不包含其他寄存器的内容,
          // 因此该语句没有任何副作用
}
特别的,C99和C++2003都指出,no effect的expression允许不被执行
An actual implementation need not evaluate part of an expression if it
can deduce that its value is not used and that no needed side effects
are produced (including any caused by calling a function or accessing
a volatile object).


1. 什么是序列点(sequence points)

C99和C++2003对序列点的定义相同
At certain specified points in the execution sequence called sequence
points, all side effects of previous evaluations shall be complete and
no side effects of subsequent evaluations shall have taken place.

中文表述为,序列点是一些被特别规定的位置,要求在该位置前的evaluations所
包含的一切副作用在此处均已完成,而在该位置之后的evaluations所包含的任何
副作用都还没有开始

例如C/C++都规定完整表达式(full-expression)后有一个序列点
extern int i, j;
i = 0;
j = i;
上面的代码中i = 0以及j = i都是一个完整表达式,;说明了表达式的结束,因此
在;处有一个序列点,按照序列点的定义,要求在i = 0之后j = i之前的那个序列
点上对i = 0的求值以及副作用全部结束(0被写入i中),而j = i的任何副作用都
还没有开始。由于j = i的副作用是把i的值赋给j,而i = 0的副作用是把i赋值为
0,如果i = 0的副作用发生在j = i之后,就会导致赋值后j的值是i的旧值,这显
然是不对的

由序列点以及副作用的定义很容易看出,在一个序列点上,所有可能影响程序状态
的动作均已完成,那这样能否推断出在一个序列点上一个程序的状态应该是确定的
呢?!答案是不一定,这取决于我们代码的写法。但是,如果在一个序列点上程序
的状态不能被确定,那么标准规定这样的程序是undefined behavior,稍后会解释
这个问题


2. 表达式求值(evaluation of expressions)与副作用发生的相互顺序

C99和C++2003都规定
Except where noted, the order of evaluation of operands of individual
operators and subexpressions of individual expressions, and the order
in which side effects take place, is unspecified.

也就是说,C/C++都指出一般情况下在表达式求值过程中的操作数求值顺序以及副
作用发生顺序是未说明的(unspecified)。为什么C/C++不详细定义这些顺序呢?
原因是因为C/C++都是极端追求效率的语言,不规定这些顺序,是为了允许编译器
有更大的优化余地,例如
extern int *p;
extern int i;
*p = i++;  // (1)
根据前述规定,在表达式(1)中到底是*p先被求值还是i++先被求值是由编译器决定
的;两次副作用(对*p赋值以及i++)发生的顺序是由编译器决定的;甚至连子表
达式i++的求值(就是初始时i的值)以及副作用(将i增加1)都不需要同步发生,
编译器可以先用初始时i的值(即子表达式i++的值)对*p赋值,然后再将i增加1,
这样就把子表达式i++的整个计算过程分成了两个不相邻的步骤。而且通常编译器
都是这么实现的,原因在于i++的求值过程同*p = i++是有区别的,对于单独的表
达式i++,执行顺序一般是(假设不考虑inc指令):先将i加载到某个寄存器A(如
果i是寄存器变量则此步骤可以跳过)、将寄存器A的值加1、将寄存器A的新值写回
i的地址;对于*p = i++,如果要先完整的计算子表达式i++,由于i++表达式的值
是i的旧值,因此还需要一个额外的寄存器B以及一条额外的指令来辅助*p = i++的
执行,但是如果我们先将加载到A的值写回到*p,然后再执行对i增加1的指令,则
只需要一个寄存器即可,这种做法在很多平台都有重要意义,因为寄存器的数目往
往是有限的,特别是假如有人写出如下的语句
extern int i, j, k, x;
x = (i++) + (j++) + (k++);
编译器可以先计算(i++) + (j++) + (k++)的值,然后再对i、j、k各自加1,最后
将i、j、k、x写回内存,这比每次完整的执行完++语义效率要高


3. 序列点对副作用的限制

C99和C++2003都有类似的如下规定
Between the previous and next sequence point a scalar object shall
have its stored value modified at most once by the evaluation of an
expression. Furthermore, the prior value shall be accessed only to
determine the value to be stored. The requirements of this paragraph
shall be met for each allowable ordering of the subexpressions of a
full expression; otherwise the behavior is undefined.

也就是说,在相邻的两个序列点之间,一个对象只允许被修改一次,而且如果一个
对象被修改则在这两个序列点之间对该变量的读取的唯一目的只能是为了确定该对
象的新值(例如i++,需要先读取i的值以确定i的新值是旧值+1)。特别的,标准
要求任意可能的执行顺序都必须满足该条件,否则代码将是undefined behavior

之所以序列点会对副作用有如此的限制,就是因为C/C++标准没有规定子表达式求
值以及副作用发生之间的顺序,例如
extern int i, a[];
extern int foo(int, int);
i = ++i + 1;  // 该表达式对i所做的两次修改都需要写回对象,i的最终值取决
              // 于到底哪次写回最后发生,如果赋值动作最后写回,则i的值
              // 是i的旧值加2,如果++i动作最后写回,则i的值是旧值加1,
              // 因此该表达式的行为是undefined
a[i++] = i;  // 如果=左边的表达式先求值并且i++的副作用被完成,则右边的
             // 值是i的旧值加1,如果i++的副作用最后完成,则右边的值是i
             // 的旧值,这也导致了不确定的结果,因此该表达式的行为将是
             // undefined
foo(foo(0, i++), i++);  // 对于函数调用而言,标准没有规定函数参数的求值
                        // 顺序,但是标准规定所有参数求值完毕进入函数体
                        // 执行之前有一个序列点,因此这个表达式有两种执
                        // 行方式,一种是先求值外层foo调用的i++然后求值
                        // foo(0, i++),然后进入到foo(0, i++)执行,这之
                        // 前有个序列点,这种执行方式还是在两个相邻序列
                        // 点之间修改了i两次,undefined
                        // 另一种执行方式是先求值foo(0, i++),由于这里
                        // 有一个序列点,随后的第二个i++求值是在新序列
                        // 点之后,因此不算是两个相邻的序列点之间修改i
                        // 两次
                        // 但是,前面已经指出标准规定任意可能的执行路径
                        // 都必须满足条件才是定义好的行为,这种代码仍然
                        // 是undefined

前面我提到在一个序列点上程序的状态不一定是确定的,原因就在于相邻的两个序
列点之间可能会发生多个副作用,这些副作用的发生顺序是未指定的,如果多于一
个的副作用用于修改同一个对象,例如示例代码i = ++i + 1;,则程序的结果是依
赖于副作用发生顺序的;另外,如果某个表达式既修改了某个对象又需要读取该对
象的值,且读取对象的值并不用于确定对象新值,则读取和修改两个动作的先后顺
序也会导致程序的状态不能唯一确定
所幸的是,"在相邻的两个序列点之间,一个对象只允许被修改一次,而且如果一
个对象被修改则在这两个序列点之间只能为了确定该对象的新值而读一次"这一强
制规定保证了符合要求的程序在任何一个序列点位置上其状态都可以确定下来

注,由于对于UDT类型存在operator重载,函数语义会提供新的序列点,因此某些
对于built-in类型是undefined behavior的表达式对于UDT确可能是良好定义的,
例如
i = i++;  // 如果i是built-in类型对象,则该表达式在两个相邻的序列点之间对
          // i修改了两次,undefined
          // 如果i是UDT类型该表达式也许是i.operator=(i.operator++(int)),
          // 函数参数求值完毕后会有一个序列点,因此该表达式并没有在两个
          // 相邻的序列点之间修改i两次,OK

由此可见,常见的问题如printf("%d, %d", i++, i++)这种写法是错误的,这类问
题作为笔试题或者面试题是没有任何意义的
类似的问题同样发生在cout << i++ << i++这种写法上,如果overload resolution
选择成员函数operator<<,则等价于(cout.operator<<(i++)).operator<<(i++),
否则等价于operator<<(operator<<(cout, i++), i++),如果i是built-in类型对
象,这种写法跟foo(foo(0, i++), i++)的问题一致,都是未定义行为,因为存在
某条执行路径使得i会在两个相邻的序列点之间被修改两次;如果i是UDT则该写法
是良好定义的,跟i = i++一样,但是这种写法也是不推荐的,因为标准对于函数
参数的求值顺序是unspecified,因此哪个i++先计算是不能预计的,这仍旧会带来
移植性的问题,这种写法应该避免


4. 编译器的跨序列点优化

根据前述讨论可知,在同一个表达式内对于同一个变量i,允许的行为是
A. 不读取,改写一次,例如
     i = 0;
B. 读取一次或者多次,改写一次,但所有读取仅仅用于决定改写后的新值,例如
     i = i + 1;  // 读取一次,改写一次
     i = i & (i - 1);  // 读取两次,改写一次,感谢puke给出的例子
C. 不改写,读取一次或者多次,例如
     j = i & (i - 1);

对于情况B和C,编译器是有一定的优化权利的,它可以只读取一次变量的值然后
直接使用该值多次

但是,当该变量是volatile-qualified类型时编译器允许的行为究竟如何目前还
没有找到明确的答案,ctrlz认为如果在两个相邻序列点之间读取同一个volatile-
qualified类型对象多次仍旧是undefined behavior,原因在于该读取动作有副作
用且该副作用等价于修改该对象,RoachCock的意见是两个相邻的序列点之间读取
同一个volatile-qualified类型应该是合法的,但是不能被优化成只读一次。一
段在嵌入式开发中很常见的代码示例如下
extern volatile int i;
if (i != i) {  // 探测很短的时间内i是否发生了变化
  // ...
}
如果i != i被优化为只读一次,则结果恒为false,故RoachCock认为编译器不能
够对volatile-qualified类型的变量做出只读一次的优化。ctrlz则认为这段代码
本身是不正确的,应该改写成
int j = i;
if (j != i) {  // 将对volatile-qualified类型变量的多次读取用序列点隔开
  // ...
}

虽然尚不能确定volatile-qualified类型的变量在相邻两个序列点之间读取多次
行为是否合法以及将如何优化(不管怎么样,对于volatile-qualified类型这种
代码应该尽量避免),但是可以肯定的是,对于volatile-qualified类型的变量
在跨序列点之后必须要重新读取,volatile就是用来阻止编译器做出跨序列点的
过激优化的,而对于non-volatile-qualified类型的跨序列点多次读取则可能被
优化成只读一次(直到某个语句或者函数对该变量发生了修改,在此之前编译器
可以假定non-volatile-qualified类型的变量是不会变化的,因为目前的C/C++
抽象机器模型是单线程的),例如
bool flag = true;
void foo() {
  while (flag) {  // (2)
    // ...
  }
}
如果编译器探测到foo()没有任何语句(包括foo()调用过的函数)对flag有过修
改,则也许会把(2)优化成只在进入foo()的时候读一次flag的值而不是每次循环
都读一次,这种跨序列点的优化很有可能导致死循环。但是这种代码在多线程编
程中很常见,虽然foo()没有修改过flag,也许在另一个线程的某个函数调用中
会修改flag以终止循环,为了避免这种跨序列点优化带来到错误,应该把flag声
明为volatile bool,C++2003对volatile的说明如下
[Note: volatile is a hint to the implementation to avoid aggressive
optimization involving the object because the value of the object
might be changed by means undetectable by an implementation. See 1.9
for detailed semantics. In general, the semantics of volatile are
intended to be the same in C++ as they are in C. ]


5. C99定义的序列点列表

― The call to a function, after the arguments have been evaluated.
― The end of the first operand of the following operators:
     logical AND && ;
     logical OR || ;
     conditional ? ;
     comma , .
― The end of a full declarator:
     declarators;
― The end of a full expression:
     an initializer;
     the expression in an expression statement;
     the controlling expression of a selection statement (if or switch);
     the controlling expression of a while or do statement;
     each of the expressions of a for statement;
     the expression in a return statement.
― Immediately before a library function returns.
― After the actions associated with each formatted input/output function
   conversion specifier.
― Immediately before and immediately after each call to a comparison
   function, and also between any call to a comparison function and any
   movement of the objects passed as arguments to that call.


6. C++2003定义的序列点列表

所有C99定义的序列点同样是C++2003所定义的序列点
此外,C99只是规定库函数返回之后有一个序列点,并没有规定普通函数返回之后
有一个序列点,而C++2003则特别指出,进入函数(function-entry)和退出函数
(function-exit)各有一个序列点,即拷贝一个函数的返回值之后同样存在一个
序列点

需要特别说明的是,由于operator||、operator&&以及operator,可以重载,当它
们使用函数语义的时候并不提供built-in operators所规定的那几个序列点,而
仅仅只是在函数的所有参数求值后有一个序列点,此外函数语义也不支持||、&&
的短路语义,这些变化很有可能会导致难以发觉的错误,因此一般不建议重载这
几个运算符


7. C++2003中两处关于lvalue的修改对序列点的影响

在C语言中,assignment operators的结果是non-lvalue,C++2003则将assignment
operators的结果改成了lvalue,目前尚不清楚这一改动对于built-in类型有何意
义,但是它却导致了很多在合法的C代码在目前的C++中是undefined behavior,例

extern int i;
extern int j;
i = j = 1;
由于(j = 1)的结果是lvalue,该结果作为给i赋值的右操作数,需要一个lvalue-
to-rvalue conversion,这个conversion代表了一个读取语义,因此i = j = 1就
是先将1赋值给j,然后读取j的值赋值给i,这个行为是undefined,因为标准规定
两个相邻序列点之间的读取只能用于决定修改对象的新值,而不能发生在修改之后
再读取
由于C++2003规定assignment operators的结果是lvalue,因此下列在C99中非法的
代码在C++2003中却是可以通过编译的
extern int i;
(i += 1) += 2;
显然按照C++2003的规定这个代码的行为是undefined,它在两个相邻的序列点之间
修改了i两次

类似的问题同样发生在built-in类型的前缀++/--operators上,C++2003将前缀++/--
的结果从rvalue修改为lvalue,这甚至导致了下列代码也是undefined behavior
extern int i;
extern int j;
i = ++j;
同样是因为lvalue作为assignment operator的右操作数需要一个左值转换,该转
换导致了一个读取动作且这个读取动作发生在修改对象之后

C++的这一改动显然是考虑不周的,导致了很多C语言的习惯写法都成了undefined
behavior,因此Andrew Koenig在1999年的时候就向C++标准委员会提交了一个建
议要求为assignment operators增加新的序列点,但是到目前为止C++标准委员会
都还没有就该问题达成一致意见,我将Andrew Koenig的提议附后,如果哪位有时
间有兴趣,可以看看,不过不看也不会有任何损失 :-)


222. Sequence points and lvalue-returning operators
Section: 5  expr     Status: drafting     Submitter: Andrew Koenig     Date: 20 Dec 1999

I believe that the committee has neglected to take into account one of the differences between C and C++ when defining sequence points. As an example, consider

    (a += b) += c;

where a, b, and c all have type int. I believe that this expression has undefined behavior, even though it is well-formed. It is not well-formed in C, because += returns an rvalue there. The reason for the undefined behavior is that it modifies the value of `a' twice between sequence points.

Expressions such as this one are sometimes genuinely useful. Of course, we could write this particular example as

    a += b; a += c;

but what about

    void scale(double* p, int n, double x, double y) {
        for (int i = 0; i < n; ++i) {
            (p[i] *= x) += y;
        }
    }

All of the potential rewrites involve multiply-evaluating p[i] or unobvious circumlocations like creating references to the array element.

One way to deal with this issue would be to include built-in operators in the rule that puts a sequence point between evaluating a function's arguments and evaluating the function itself. However, that might be overkill: I see no reason to require that in

    x[i++] = y;

the contents of `i' must be incremented before the assignment.

A less stringent alternative might be to say that when a built-in operator yields an lvalue, the implementation shall not subsequently change the value of that object as a consequence of that operator.

I find it hard to imagine an implementation that does not do this already. Am I wrong? Is there any implementation out there that does not `do the right thing' already for (a += b) += c?

5.17  expr.ass paragraph 1 says,

The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue.
What is the normative effect of the words "after the assignment has taken place"? I think that phrase ought to mean that in addition to whatever constraints the rules about sequence points might impose on the implementation, assignment operators on built-in types have the additional constraint that they must store the left-hand side's new value before returning a reference to that object as their result.

One could argue that as the C++ standard currently stands, the effect of x = y = 0; is undefined. The reason is that it both fetches and stores the value of y, and does not fetch the value of y in order to compute its new value.

I'm suggesting that the phrase "after the assignment has taken place" should be read as constraining the implementation to set y to 0 before yielding the value of y as the result of the subexpression y = 0.

Note that this suggestion is different from asking that there be a sequence point after evaluation of an assignment. In particular, I am not suggesting that an order constraint be imposed on any side effects other than the assignment itself.
Francis Glassborow:

My understanding is that for a single variable:

Multiple read accesses without a write are OK
A single read access followed by a single write (of a value dependant on the read, so that the read MUST happen first) is OK
A write followed by an actual read is undefined behaviour
Multiple writes have undefined behaviour
It is the 3) that is often ignored because in practice the compiler hardly ever codes for the read because it already has that value but in complicated evaluations with a shortage of registers, that is not always the case. Without getting too close to the hardware, I think we both know that a read too close to a write can be problematical on some hardware.

So, in x = y = 0;, the implementation must NOT fetch a value from y, instead it has to "know" what that value will be (easy because it has just computed that in order to know what it must, at some time, store in y). From this I deduce that computing the lvalue (to know where to store) and the rvalue to know what is stored are two entirely independent actions that can occur in any order commensurate with the overall requirements that both operands for an operator be evaluated before the operator is.

Erwin Unruh:

C distinguishes between the resulting value of an assignment and putting the value in store. So in C a compiler might implement the statement x=y=0; either as x=0;y=0; or as y=0;x=0; In C the statement (x += 5) += 7; is not allowed because the first += yields an rvalue which is not allowed as left operand to +=. So in C an assignment is not a sequence of write/read because the result is not really "read".

In C++ we decided to make the result of assignment an lvalue. In this case we do not have the option to specify the "value" of the result. That is just the variable itself (or its address in a different view). So in C++, strictly speaking, the statement x=y=0; must be implemented as y=0;x=y; which makes a big difference if y is declared volatile.

Furthermore, I think undefined behaviour should not be the result of a single mentioning of a variable within an expression. So the statement (x +=5) += 7; should NOT have undefined behaviour.

In my view the semantics could be:

if the result of an assignment is used as an rvalue, its value is that of the variable after assignment. The actual store takes place before the next sequence point, but may be before the value is used. This is consistent with C usage.
if the result of an assignment is used as an lvalue to store another value, then the new value will be stored in the variable before the next sequence point. It is unspecified whether the first assigned value is stored intermediately.
if the result of an assignment is used as an lvalue to take an address, that address is given (it doesn't change). The actual store of the new value takes place before the next sequence point.
Jerry Schwarz:

My recollection is different from Erwin's. I am confident that the intention when we decided to make assignments lvalues was not to change the semantics of evaluation of assignments. The semantics was supposed to remain the same as C's.

Ervin seems to assume that because assignments are lvalues, an assignment's value must be determined by a read of the location. But that was definitely not our intention. As he notes this has a significant impact on the semantics of assignment to a volatile variable. If Erwin's interpretation were correct we would have no way to write a volatile variable without also reading it.

Lawrence Crowl:

For x=y=0, lvalue semantics implies an lvalue to rvalue conversion on the result of y=0, which in turn implies a read. If y is volatile, lvalue semantics implies both a read and a write on y.

The standard apparently doesn't state whether there is a value dependence of the lvalue result on the completion of the assignment. Such a statement in the standard would solve the non-volatile C compatibility issue, and would be consistent with a user-implemented operator=.

Another possible approach is to state that primitive assignment operators have two results, an lvalue and a corresponding "after-store" rvalue. The rvalue result would be used when an rvalue is required, while the lvalue result would be used when an lvalue is required. However, this semantics is unsupportable for user-defined assignment operators, or at least inconsistent with all implementations that I know of. I would not enjoy trying to write such two-faced semantics.

Erwin Unruh:

The intent was for assignments to behave the same as in C. Unfortunately the change of the result to lvalue did not keep that. An "lvalue of type int" has no "int" value! So there is a difference between intent and the standard's wording.

So we have one of several choices:

live with the incompatibility (and the problems it has for volatile variables)
make the result of assignment an rvalue (only builtin-assignment, maybe only for builtin types), which makes some presently valid programs invalid
introduce "two-face semantics" for builtin assignments, and clarify the sequence problematics
make a special rule for assignment to a volatile lvalue of builtin type
I think the last one has the least impact on existing programs, but it is an ugly solution.

Andrew Koenig:

Whatever we may have intended, I do not think that there is any clean way of making

    volatile int v;
    int i;

    i = v = 42;

have the same semantics in C++ as it does in C. Like it or not, the subexpression v = 42 has the type ``reference to volatile int,'' so if this statement has any meaning at all, the meaning must be to store 42 in v and then fetch the value of v to assign it to i.

Indeed, if v is volatile, I cannot imagine a conscientious programmer writing a statement such as this one. Instead, I would expect to see

    v = 42;
    i = v;

if the intent is to store 42 in v and then fetch the (possibly changed) value of v, or
    v = 42;
    i = 42;

if the intent is to store 42 in both v and i.

What I do want is to ensure that expressions such as ``i = v = 42'' have well-defined semantics, as well as expressions such as (i = v) = 42 or, more realistically, (i += v) += 42 .

I wonder if the following resolution is sufficient:

Append to 5.17  expr.ass paragraph 1:

There is a sequence point between assigning the new value to the left operand and yielding the result of the assignment expression.
I believe that this proposal achieves my desired effect of not constraining when j is incremented in x[j++] = y, because I don't think there is a constraint on the relative order of incrementing j and executing the assignment. However, I do think it allows expressions such as (i += v) += 42, although with different semantics from C if v is volatile.

Notes on 10/01 meeting:

There was agreement that adding a sequence point is probably the right solution.

Notes from the 4/02 meeting:

The working group reaffirmed the sequence-point solution, but we will look for any counter-examples where efficiency would be harmed.

For drafting, we note that ++x is defined in 5.3.2  expr.pre.incr as equivalent to x+=1 and is therefore affected by this change. x++ is not affected. Also, we should update any list of all sequence points.

Notes from October 2004 meeting:

Discussion centered around whether a sequence point "between assigning the new value to the left operand and yielding the result of the expression" would require completion of all side effects of the operand expressions before the value of the assignment expression was used in another expression. The consensus opinion was that it would, that this is the definition of a sequence point. Jason Merrill pointed out that adding a sequence point after the assignment is essentially the same as rewriting

    b += a

as

    b += a, b

Clark Nelson expressed a desire for something like a "weak" sequence point that would force the assignment to occur but that would leave the side effects of the operands unconstrained. In support of this position, he cited the following expression:

    j = (i = j++)

With the proposed addition of a full sequence point after the assignment to i, the net effect is no change to j. However, both g++ and MSVC++ behave differently: if the previous value of j is 5, the value of the expression is 5 but j gets the value 6.

Clark Nelson will investigate alternative approaches and report back to the working group.

一道很tricky的题目

int a,b,c;
int *p;
a=100;
c=10;
p=&c;
b=a/*p;
printf("%d",b);

输出是啥?











































没有输出,编译不通过,因为/*是注释的开始……