月度归档:2011年06月

对『源代码』的一点理解(带剧透)

『源代码』(时光网·豆瓣网·IMDb)讲述的是一个空军飞行员上尉在执行任务时牺牲后,其身体被军方机构放在培养皿里进行源代码程序实验,用来执行特殊任务。该实验的基本原理是利用人死后仍有8分钟的残留记忆,再运用老美YY的高科技手段把以某种形态存在的上尉送回到这8分钟里。也就是说,让上尉的意识回到某人(宿主)的死前8分钟,然后取代这个人(宿主)。这个实验的好处就是通过控制上尉,让他不停的穿梭于过去与现实,就能带回一些事故发生时信息,这里上尉的作用有些类似于飞机的黑匣子,只不过比黑匣子能动性和机动性更强些。影片中上尉费尽周折轮回了一次又一次,最后总算找出了那个芝加哥列车爆炸案的制造者,然后告知警方在现实中将其抓获以避免第二波恐怖袭击的发生。

源代码程序无法改变过去发生的事实,比如那天早上整趟列车中的人无一幸免,那么即便上尉回到当时,拆掉炸弹揪出罪犯,但是对于现实爆炸案还是发生的,这就引出了一个平行宇宙[1]的概念。平行宇宙是指一个事件不同的过程或一个不同的决定的后续发展是存在于不同的平行宇宙中的,『蝴蝶效应』中就有这个意思。如果一个事件的不同发展方向会存在于不同的平行宇宙中,那么势必有这样的推论:承认一个平行宇宙的存在就等于承认无数个平行宇宙的存在。但是在『源代码』的理论体系里,只有上尉回去一次,才会衍生出一个平行宇宙,即源代码程序每启动一次才创造一个平行宇宙。所以在最后,上尉停留在了源程序最后一次运行产生的那个平行宇宙里,取代宿主肖恩与女主长相厮守。同时,在这个平行宇宙里,源代码程序试验还没开始,因为在这个世界里那天早上并没有发生火车爆炸案。上尉给他父亲打电话时电话另一端也是那个平行宇宙里的父亲,因为这个世界是在源代码启动时才创造的,因此上尉在那个宇宙里也同样是死于2个月之前。无论哪个平行宇宙,上尉都是一个死人,上尉只能通过源代码程序寄宿在一个叫肖恩的人身上,至于肖恩去哪里了,这个就不知道了,估计是悲剧了吧。如果上尉像前几次一样执行任务失败,没有拆掉炸弹,然后那个平行宇宙中的军方机构启动源代码程序,那么就要理解成平行宇宙中再创造平行宇宙,理论上会创造出无数个平行宇宙,满足指数级增长规律。

可能因为这部电影不是特别难以理解,我才理得清故事的脉络,不像『恐怖油轮』、『记忆碎片』这样的片子,只看1遍根本下不了笔。而且它也不像某些穿越类电影,虽然精彩但毫无逻辑可循。对片子最后列车中时间静止的场面,还有上尉对他父亲告别情节印象很深刻,我比较容易被这类的场景感动。又悬疑又有逻辑又有温馨场面,所以我给它的评分分别是:9.0/10和5/5。

最后还要提一下两位女主角,Vera Farmiga(wiki)和Michelle Monaghan(wiki),这两位都属于第一眼看上去不显漂亮,但是越看越有味道的那种类型。特别前者饰演的穿着空军制服的Goodwin,坐在那儿就能感觉出气质的。(¯﹃¯)(¯﹃¯)(¯﹃¯)。

Reference:
[1] 平行宇宙,  http://zh.wikipedia.org/wiki/%E5%B9%B3%E8%A1%8C%E5%AE%87%E5%AE%99 .

--EOF--

K&R: 60 tips of C programming language [3]

  • 41. Any operation that can be achieved by array subscripting can also be done with pointers. The pointer version will in general be faster but, at least to the uninitiated, somewhat harder to understand.
  • 42. All pointer arithmetic, is that pa+1 points to the next object, and pa+i points to the i-th object beyond pa.
  • 43. One difference between an array name and a pointer that must be kept in mind. A pointer is a variable, so pa=a and pa++ are legal. But an array name is not a variable; constructions like a=pa and a++ are illegal.
  • 44. p[-1], p[-2] and so on are syntactically legal, and refer to the elements that immediately precede p[0].
  • 45. The valid pointer operations are assignment of pointers of the same type, adding or subtracting a pointer and an integer, subtracting or comparing two pointers to members of the same array, and assigning or comparing to zero. All other pointer arithmetic is illegal. To assign a pointer of one type to a ponter of another type without a cast is illegal except for void*.
  • 46. C does not provide any operators for processing an entire string of characters as a unit. It uses pointer.
  • 47. If a two-dimensional array is to be passed to a function, the parameter declaration in the function must include the number of columns.
  • 48. The format argument of printf can be an expression such like printf((argc > 1) ? "%s " : "%s", *++argv);.
  • 49. *++argv is a pointer to an argument string, so (*++argv)[0] is its first character.
  • 50. Any pionter can be cast to void* and back again without loss of information.
  • 51. A struct declaration defines a type. The right brace that terminates the list of members may be followed by a list of variables. struct{...} x, y, z; is syntactically analogous to int x, y, z;.
  • 52. A structure declaration that is not followed by a list of variables reserves no storage.
  • 53. The only legal oprations on a structure are copying it or assigning to it as a unit, taking its address with &, and accessing its members.
  • 54. *p->str fetches whatever str points to; *p->str++ increments str after accessing whatever it points to (just like *s++); (*p->str)++ increments whatever str points to; and *p++->str increments p after accessing whatever str points to.
  • 55. It is illegal for a structure to contain an instance of itself but declares a pointer to it is legal.
  • 56. A typedef declaration does not create a new type in any sense; it merely adds a new name for some existing type.
  • 57. For example, typedef int (*PFI)(char *, char *); creates the type PFI, for "pointer to function (of two char * arguments) returning int," which can be used in contexts like PFI strcmp, numcmp;.
  • 58. The same operations are permitted on unioins as on unions as on structures: assignment to or copying as a unit, taking the address ,and accessing a member.
  • 59. A union may only be initialized with a value of the type of its first member; thus the union u described above can be initialized with an integer value.
  • 60. The & operator cannot be applied to bit-field because they are not arrays and they do not have addresses.
  • --EOF--

    K&R: 60 tips of C programming language [2]

  • 20. Variables can be declared inside any block; not only have to declare at the entry of the function.
  • 21. All case expressions must be different in switch statement. Cases and the default clause can occur in any order.
  • 22. The commas that separate function arguments, variables in declarations, etc., are not comma operators, and do not guarantee left to right evaluation.
  • 23. A label has the same form as a variable name, and is followd by a colon. The scope of a label is the entire function.
  • 24. Functions themselves are always external, because C does not allow functions to be defined inside other functions.
  • 25. If a large number of variables must be shared among functions, external variables are more convenient and efficient that long argument lists.
  • 26. The scope of an external variable or a function lasts from the point at which it is declared to the end of the file being complied.
  • 27. There must be only one definition of an external variable among all the files that make up the source program; other files may contain extern declarations to access it. Array sizes must be specified with the definition, but are optional with an extern declaration.
  • 28. If a function is decleared static, howerer, its name is invisible outside of the file in which it is decleared.
  • 29. The register declaration can only be applied to automatic variables and to the formal parameters of a function. It is not possible to take the address of register variable, regardless of whether the variable is actually placed in a register.
  • 30. External and static variables are guaranteed to initialized to zero; automatic and register vaiables have undefined initial values. For external and static vairables, the initializer must be a constant expression.
  • 31. If there are fewer initializers for an array than the number specified, the missing elements will be zero for external, static, and atutomatic variables.
  • 32. One simplest version of quicksort:
    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
    
     /* sort v[left] ... v[right] into increasing order */
    void qsort(int v[], int left, int right)
    {
    	int i, last;
    	void swap(int v[], int i, int j);
     
    	if(left >= right)
    		return;
    	swap(v, left, (left + right)/2);
    	last = left;
    	for(i = left + 1; i <= right; i++)
    	{
    		if (v[i] < v[left])
    			swap(v, ++last, i);
    	}
    	swap(v, left, last);
    	qsort(v, left, last-1);
    	qsort(v, last+1, right);
    }
     
    void swap(int v[], int i, int j)
    {
    	int temp;
     
    	temp = v[i];
    	v[i] = v[j];
    	v[j] = temp;
    }
  • 33. When an included file is changed, all files that depend on it must be recompiled.
  • 34. The name in a #define has the same form as a variable name; the scope of a name defined with #define is from its point of definition to the end of the source file being compiled.
  • 35. Names may be undefined with #undef, usually to ensure that a routine is really a function, not a macro:
  • 1
    2
    
     #undef getchar
    int getchar(void) {}
  • 36. In macro, if a parameter name is preceded by a # in the replacement text, the combination will be expanded into a quoted string with the parameter replaced by teh actual argument.
    1
    2
    3
    4
    
     #define dprint(expr) printf(#expr " = %g\n", expr)
     
    dprint(x/y);//it has the same meaning of following expression.
    printf("x/y" " = %g\n", x/y);
  • 37.The preprocessor operator ## provides a way to concatenate actual arguments during macro expansion.
    #define paste(font, back) front ## back

    so paste(name, 1) creates the token name1.

  • 38. In ANSI C, the type void* replaces char* as the proper type for a generic pointer.
  • 39. The & operator only applies to objects in memory: variables and array elements. It cannot be applied to expressions, constants, or register variables.
  • 40. Unary operators like * and ++ associate right to left. So *ip++ means *(ip++), quite far different from (*ip)++.
  • --EOF--

    K&R: 60 tips of C programming language [1]

    曾经一段时间经常会去Google Groups的comp.lang.c小组里逛逛。在那里,『The C Programming Language』(Kernighan&Richie著,以下简称K&R)是被当成神作供起来的,简直就是C语言领域的“圣经”。于是当时慕名下了电子书看了有一半左右,因为e文不好看着非常累,虽然看后感觉挺有收获,但还是放弃了没有看完。后来不知道哪一次脑热买了K&R实体书,过去几年,它一直沉睡在那里。如今,陆陆续续接触到了各种五花八门的编程语言,C语言除了考试和面试反而不怎么用到了,但是没读完这本书却始终觉得人生不完整。于是,强迫自己将它翻完一遍,感觉真的是非常基础,讲得也透彻,我都不知道用什么词汇去形容它的浅显和通俗了。如果入门时授课老师用这本书当教材应该是个不错的选择。

    读完一遍K&R,顿时觉得程序人生又完整了一些,现在做个总结。

  • 1. Comments may appear anywhere a blank or tab or newline can.
  • 2. printf is not part of C language; there is no input or output defined in C itself. printf is just a useful function from the standard libray of functions that are normally acessible to C programs. printf recognizes %% for % itself. printf uses %f for both float and double.
  • 3. getchar() returns a distinctive value when there is no more input. We can't define a char value to hold the return value of getchar() because char is not big enough to hold EOF in addition to any possible char. Use int instead.
  • 4. A character written between single quotes represents an integer value equal to the numerical value of the character in the machine's character set.
  • 5. With properly designed functions, it is possible to ignore how a job is done; knowing what is done is sufficient.
  • 6. In C, all function arguments are passed "by value." This means that the called function is given the values of its garguments in temporary variables rather than the originals. int is the default return type of function, it could be omitted.
  • 7. An external variable must be defined, exactly once, outside of any funtion.
  • 8. "Definition" refers to the place where the variable is created or assigned storage; "Declaration" refers to places where the nature of the variable is stated but no storage is allocated.
  • 9. Begin variable names with underscore is legal but not recommend, since library routines often use such names.
  • 10. A leading 0 (zero) on an integer constant means octal; a leading 0x or 0X means hexadecimal.
  • 11. String constants can be concatenated at complie time: "hello," "world" is equivalent to "hello, world". char st[] = "hello, " "world!"; is legal.
  • 12. If not all values are specified in enumerations, unspecified values continue the progression from the last specified value. Names in different enumerations must be distinct. Values need not be distinct in the same enumeration. Enumeration variables offer the chance of checking and so are often better than #define.
  • 13. External and static variables are initialized to zero by default.
  • 14. The % operator cannot be applied to float or double.
  • 15. The increment and decrement operators can only be applied to variables; an expression like (i+j)++ is illegal.
  • 16.Bitwise operators like &,|,^,<<,>>,~ only be applied to integral operands, that is char, short, int and long, whether signed or unsigned.
  • 17. x *= y + 1 means x = x * (y + 1) rather than x = x * y + 1.
  • 18. An bitcount function:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
     /* bitcount: count 1 bits in x */
    int bitcount (unsigned x)
    {
    	int b;
     
    	for (b = 0; x != 0; x >>= 1)
    	{
    		if (x & 01)
    			b++;
    	}
    	return b;
    }

    Declaring the argument x to be unsigned ensures that when it is right-shifted, vacated bits will be filled with zeros, not sign bits, regardless of the machine the program is run on.

  • 19. printf("%d %d\n", ++n, power(2, n)); can produce different results with different compilers, depending on whether n is incremented before power is called.
  • --EOF--

    STC单片机串口通信波特率设置问题

            昨天以来一直为一个问题困扰:单片机要发送一个(或一串)即时状态(或数据)到PC端,PC端的串口助手总是取不到正确的值。我在PC端设置好串口为9600Baud,8位数据位,1位起始位,1位停止位,无奇偶校验位。在单片机程序中编程SCON,设置串口工作模式1,8位UART,波特率可变。同时采用定时器1作为波特率发生器,编程TMOD寄存器,设置为方式2,8位自动重装模式。因为STC单片机默认(未设置AUXR寄存器)是工作在12T模式的,因此根据波特率计算公式:Baudrate = Fosc / ( 32 * 12 * (256 - x) ), 其中x为要装入TH1和TL1的初始值, Fosc为外部晶振频率,这里是11.0592MHz。计算可得x = 0xFD。完整的初始化串口程序如下:

    1
    2
    3
    4
    5
    6
    
    SCON = 0x50; /* 串口模式1,8位UART,波特率可变。 */
    TMOD = 0x20; /* 定时器1方式2,8位自动重装模式。 */
    TH1 = 0xFD; /* 波特率为9600@11.0592MHz,定时器初始值0xFD */
    TR1 = 1; /* 启动定时器1 */ 
    ES = 1; /* 允许串口中断 */
    EA = 1; /* 允许总中断开关 */

            但是PC端串口助手接收到的值总是与单片机发送的值不同,两者之间毫无规律可循,而且PC端收到的数据长度总是为单片机发送长度的2倍。有一个例外,那就是发送0x00,这个数值能在PC端正确接收。
            解决方法可以尝试将PC端的窗口波特率设置为4800,即设置成单片发送波特率的一半。我估计原因是假如上面的串口初始化程序没错,PC端接收到的数据长度却是单片机发送出的长度的2倍,就表示接受波特率是发送波特率的2倍。降低接收端波特率,使两端串口仍旧处在同一波特率之下可正常工作。问题是可以这样解决,但是不是这个理由支撑就不清楚了,也有可能是上述的初始化串口程序有问题,本身就是将串口初始成4800波特率的。

    --EOF--