Programming

八卦John Carmack(上)

今天闲来无事,和一个搞游戏开发的老兄聊起John Carmack来乐。也正赶上博客没啥好写的,那就八卦一把John Carmack吧!

搞图形的不知道John Carmack简直就赶得上学物理的不知道牛顿,学数学的不知道高斯,学编程的不知道Donald Knuth,不可原谅啊!……(怎么又是这个开头?)

JC大哥获得赞誉无数,什么名人堂啦,什么连比尔很多门先生都赏识的程序员啦,什么世界最顶级程序员啦……当然了,这些牛轰轰的荣誉人家都不在乎,他更喜欢被我们看作金属乐队(Metallica)的成员。不愧是牛人,和我们凡人自然不一样……当然当然,这还没完,还有更恐怖的呢,那就是他特别喜欢火箭和法拉利……如果世界上还有觉得法拉利不够快的人,那JC大哥肯定就是其中一个乐!他光买法拉利就买好几辆乐,而且自己总是觉得不够快,还得亲自改装,可见其追求速度达到了何种程度!

好乐,回到正题上来吧。JC大哥做过的游戏几乎个个都是石破天惊的说,别的不说了,就光Doom3 ,Quake3和Return To Castle Wolfenstein中的任何一个拿出来都够吓人了,而况还远不止这三个乎!玩过Quake的都知道里面的3D在当时已经很NB的了,可那还是造出个3D就已经很牛的“远古”时代啊,遥想当年偶一个哥们一出去就必然叫偶去网吧玩Quake……(扯远乐……打住)而Doom3就更不用说乐,里面那超炫的3D和光影效果足以让人怀疑这究竟是不是人类在PC机上实现的,真可谓把计算机3D推向新的极致乐!(当然还有里面那超残酷的情节,如果你还没玩过Doom3,赶快找个带得起来的机器玩一把吧!)

JC大哥不光是ID Software的lead programmer,还是founder,据说在管理方面也很出色。话又说回来了,ID Software还有一个John,John Romero,也很牛的说,不过后来因为和JC大哥就公司发展持不一致意见而愤然离开了。JC大哥有自己的观点,他可不想把ID Software弄成一个出售游戏CD的公司,也不想弄成微软式的大公司,于是ID Software至今为止也只有数十人而已,但里面个个都是腰缠万贯的百万富翁。而就是这么一个小公司,却出了N多火得不行的游戏,连nVIDIA和Microsoft这样的大公司也不得不巴结它。你想啊,如果不好心巴结它,nVIDIA的显卡就买不出去啊,M$的DirectX就大打折扣啊。

而今,JC大哥虽然已经声名在外,但仍然每天坚持研究3D引擎的优化,这一点足以让我们这些小辈学习乐。JC不光自己极力推崇技术,而且还乐于把自己知道的东西和别人分享,从Wolfenstein3D开始,JC就一直适时地将自己的游戏源代码公开,让全世界的程序员分享他的技术结晶,Quake自然也在其中。而即使这样也没有使他的技术和开发出来的游戏逊色于任何行业竞争对手,他的技术、他的游戏仍然是世界最顶尖的。我想这位大牛大概是想通过这激励自己更努力地学习吧!

(题外话1:有些自私的人总担心把自己的知识和别人分享,生怕别人知道会超过他,而实际上呢,你和别人分享得越多自己就收获得越多!题外话2:Quake也好,Doom也罢,在Windows和Linux上都可以玩儿。再对比国内某些做游戏的厂商,就一味盯着Windows,很鄙视的说!说白了还是他们技术不行!)

UNP卷一读书笔记(一)

1. inet_addr()是遗弃的函数,最好不要使用,因为它不能很好地处理错误。ping 255.255.255.255就是由此引发的问题,
因为inet_addr()会把255.255.255.255当成-1!

2. inet_ntoa()使用的是静态缓冲区,因此不是线程安全的函数。而且,inet_ntoa()使用一个结构体类型做参数,而不是指向该结构体的指针。这种情况很少见。

3. 系统调用被中断的一个例子:当父进程在等待accept()时,收到了子进程死亡的信号SIGCHLD,而转去处理这个信号。
此时如果不做处理,系统可能会自动重启这个系统调用,也可能会返回错误EINTR。

4. SIGKILL和SIGSTOP是不能被捕捉的,也不能被忽略。

5. UNIX文件中的change time和modification time不是一个概念。 如果我们使用chmod a-w myfile',这是一个change;如果我们用echo foo >> myfile’,那这是一个modification。change修改的是文件的inode;而modification修改的是文件本身的内容。 文件的modification time也被称为时间戳(timestamp)。只读取文件会改变文件的access time,但不会改变上面提到的两个时间。

6. POSIX规定,select的第5个参数应该是const的,但Linux并未这样实现。Linux会修改这个struct timeval,来反映没有睡眠的时间。

7. 收到FIN之后,进程仍然可以向该socket写数据,但会收到一个RST。如果一个进程在收到RST之后,仍然向此socket里写数据,它就会收到一个SIGPIPE信号。该信号的默认处理方式是终止这个进程。所以进程必须能够捕捉到这个信号来避免意外的终止。如果进程捕捉到该信号并返回,或者直接忽略这个信号,写操作会返回一个EPIPE。通过第一次写操作,而不是第二次,是不可能得到SIGPIPE信号的。

《C语言编程艺术》第二、三章发布

经过长期的撰写,《C语言编程艺术》一书的第三章《解读C语言标准(上)》已经初步完善,特公布出来,供大家参考。我不得不承认,写关于标准的东西是件痛苦的事。第二章《关于C的思考》,自从上次公布后又进行了大的修改和完善,也一同公布。

下载地址如下:

第二章:Chapter2-beta.pdf

第三章:Chapter3-beta.pdf

同时,如果各位在阅读过程中如果发现错误,不论其大小(哪怕一个标点符号错误),都请及时反馈给我!我的邮箱是:xiyou!wangcong()yahoo!com!cn。

说实话,从上次公布的效果来看,我对大多数人的表现感到失望!一些人下载之后估计连看都不看(因为他后来还问过我里面讲过的一个问题),而一些人是看了之后什么也不说,好也罢,坏也罢,只字不提!我公布它们还能有什么目的?不就是为了能让更多人帮忙查错吗?不就是为了让书最后出来之后能更好么?如果你看了之后什么也不说,什么也不提,不是辜负了我的好意么?让我以后怎么再继续公布?记住:没有人让我非得公布!我完全可以永远不公布这些书的下载版!实际上,出版社巴不得我永远不公布!他们当然希望能有更多人去买他们的书,而我不在乎这个,我只想把自己写的东西尽量写好!

这也像是做开源软件。做开源的人都是心地善良的人,我们不在乎你是否乐意花钱购买我们的作品,我们需要的是你的反馈和认可,我们想看到的是这个作品会更好更完善。

仅此而已!毕竟,我们,不是,微软。

一些人笑了笑,啪啪点了两下之后,继续默默路过!悲哀!

再谈Quicksort和Binary Search

众所周知,Quicksort是大牛C.A.R. Hoare于1962年发明的(wikipedia上说是1960,但paper上写的是1962),当时他那篇paper现在可以免费下载了。短短12页(其实是11页,而且每页利用率不高)就把quick sort分析得详详细细,头头是道了。其实这个算法只不过是这位牛人职业生涯中最琐碎的贡献,他的主要贡献还是Algol60。那个时候可是牛人辈出的年代,Ken Thompson牛当年可是用汇编写操作系统和编译器的,搁到现在连想都不敢想啊!偶滴神啊!咳咳,不好意思,跑题了……

现在,凡是一本和“数据结构” 或者“算法”靠边的书就必然会提到Quicksort,很多还给出自己的实现。无奈,绝大多数代码都很丑陋(如果不能说非常丑陋的话)。好不容易,我在BWK的牛著《The Practice of Programming》中找到了一个简洁的实现。请欣赏:

接下来我们谈谈Binary Search。1946年,第一个Binary Search算法出现;1962年,第一个“正确”的Binary Search算法出现,最前面十八个都错了。1986年,Jon Bentley在课堂上和《Programming Pearls》这本书中,说明了这么简单的演算法(1962版)可以犯下多少的bug。2006年,书中那个大众引用的程式被抓到integer overflow bug。这个bug是出在这个该死的语句上:

int mid =(low + high) / 2;

您看出哪里错来了么?其实是low+high超出int表示的正数范围时就会变成负数,这时mid也跟着错了,而后面用mid取数组元素自然也不对了。修补就很简单了:

int mid = low + ((high - low) / 2);

下面是《Practice of Programming》中的Binary Search,不那么通用,而且也存在上面说的bug!把它修改成通用(模仿一下标准C库函数bsearch(3))而且bug-free的代码就留给您作为课后作业了。

BTW:这里有篇小文章,分析了为什么90%的程序员写不出bug-free的binary search。里面确实道出了很多程序员的无奈,用某牛的话说就是我们只能证明bug的存在,而不能证明其不存在,代码越多越是如此!

Brackets, too

If a is legal, (a), {a}, [a] are legal;
If a and b are both legal, ab is legal.

You are given a string S with length of n. Assume that Si(1 <= i <= n) is the i-th character of S. Your task is to find out whether there is a k(1 <= k <= n) that SkSk+1Sk+2…SnS1S2…Sk-1 is legal.

Input
There are several test cases. The first line contains an integer N. Then N test cases follows. In each test case, there is an only line that contains S. The length of S will not be larger than 1000. There are only (, ), {, }, [, ] in S.

Output
For each test case, output a single line “YES” (without quotation marks) if there is such a k, or “NO” (without quotation marks) otherwise.

Sample Input
3
}()[]{
}([)]{
()][

Sample Output
YES
NO
YES

! /usr/bin/env python

import string, sys

def exchange_str(str, i):
str1 = str[0:i]
str2 = str[i:]
return str2+str1

def is_legal(str):
if(len(str)==0):
return 1
if(len(str)==1):
return 0

if(str[0]=='('):
    i = string.find(str, ")")
    if(i==-1):
        return 0
    elif(i==len(str)-1):
        return is_legal(str[1:len(str)-1])
    else:
        return is_legal(str[0:i+1]) and is_legal(str[i+1:len(str)])
if(str[0]=='['):
    i = string.find(str, "]")
    if(i==-1):
        return 0
    elif(i==len(str)-1):
        return is_legal(str[1:len(str)-1])
    else:
        return is_legal(str[0:i+1]) and is_legal(str[i+1:len(str)])
if(str[0]=='{'):
    i = string.find(str, "}")
    if(i==-1):
        return 0
    elif(i==len(str)-1):
        return is_legal(str[1:len(str)-1])
    else:
        return is_legal(str[0:i+1]) and is_legal(str[i+1:len(str)])

n = string.atoi(sys.stdin.readline())
while(n!=0):
str = sys.stdin.readline()
str = str[0:len(str)-1]
tmp = “”
legal = 0;
for i in range(0, len(str)):
tmp = exchange_str(str, i)

    #print "==&gt;"+tmp+"n"
    if(is_legal(tmp)):
        legal = 1
        break
if(legal==1):
    print "YES"
else:
    print "NO"
n-=1

Brackets

Given a string consisting of brackets of two types find its longest substring that is a regular brackets sequence.

Input

There are mutiple cases in the input file.
Each case contains a string containing only characters ‘(’ , ‘)’ , ‘[’ and ‘]’ . The length of the string does not exceed 100,000.
There is an empty line after each case.

Output

Output the longest substring of the given string that is a regular brackets sequence.
There should be am empty line after each case.

Sample Input

([(]()]

([)]

Sample Output

[()]

include

include

include

include

using namespace std;

class Substr{
private:
int from;
int length;
string s;
bool ispair(char a, char b)
{
if(a ==’(‘ && b == ‘)’)
return true;
else if(a == ‘[‘ && b == ‘]’)
return true;
else
return false;
}
void calculate()
{
int max = 0;
int len = 0;
const char* p = this->s.c_str();
stack vc;

    do
    {
        if(!vc.empty() &amp;&amp; ispair(vc.top(),*p)){
            vc.pop();
            len++;
        } else {
            if(len &gt; max){
                max = len;
                from = p-this-&gt;s.c_str();
                length = len*2;
                from -= length;
            }
            len = 0;
            vc.push(*p);
        }
    }while(*p++);
}

public:
Substr(string str)
{
s = str;
from = 0;
length = 0;
}
void print()
{
calculate();
cout<<s.substr(from, length)<<endl;
cout<>s){
Substr ss(s);
ss.print();
}
return 0;
}

翠花儿,上代码~!

无意(确实是无意)间写了这么一个程序,它既没调用里面的打印”hello!”的函数,也没使用循环,却是无限循环地打印”hello!”!这个程序是不可移植的,在Win上应该不行,在Linux上可能也有差异。我是在FC5上编译运行的,效果不错。

include

include

void foo(char arg)
{
volatile int foo = 3;
volatile int bar = 9;
char str[8];
strcpy(str, arg);
printf(“ret=%pn”, (int
)(&foo - 1));
}

void hello()
{
printf(“hello!n”);
}
int main(void)
{
char t[32] = { 0 };
memset(t, ‘x90’, 31);
unsigned long ptr = (unsigned long )&t[20];
ptr = (unsigned long)&hello;
ptr = (unsigned long
)&t[24];
*ptr = (unsigned long)&main;
printf(“hello@%pn”, hello);
foo(t);
return 0;
}

[翻译]每个程序员都要学习C的十个理由

做人要厚道,转载请指明出处!

原文见:http://www.jubuu.com/ten-reasons-why-every-programmer-should-learn-c.html

每个程序员都应该在他的职业生涯中去学习C。好处很多,不能忽视。它不仅能带来更多的工作机会,而且还能教你更多关于计算机的东西。

1. 相对于其它编程语言(C++,Java),C是较为底层的。在更底层进行编程工作能让你进一步理解计算机,作为一个整体。

2. 设备驱动程序和操作系统都是专门用C写的。可能你从来没写过一个设备驱动程序,但是如果你需要去修改一个,该怎么办呢?

3. 如果你想找一个对单片机进行编程的工作,又怎么办呢?它们也是用C编程的。你还打算因为不想学一门新的编程语言而限制你的工作范围吗?

4. C语言程序比用其它编程语言写的程序更小更快。有时,你的程序需要速度的提升,而这通常只有C才做得到。

5. 如果你学过C,你可以学习任何现代的编程语言。背后的原因是大多数现代的编程语言都是基于C的(Java,C++,C#)。

6. 因为C已经存在很多年了,它拥有庞大的社区和集体的代码库。这就使得你可以快速有效地去实现新的算法或者函数——它们以前都被编写过。

7. C是开放源代码社区的语言。开源的宠儿Linux就是用C编写的。如果你懂得C,你就可以参与和奉献很多开源社区,比如Source Forge

8. C是唯一一门教授你指针到底是什么东西的语言。C#和Java完全跳过了这一部分。而正是指针,才给予了C威力。

9. C仍然是编程工作所需的最通用的语言。值得你花时间去掌握它。

10. 任何带有微芯片的东西都支持C。从你的微波炉到手机,C驱动着这些技术。

[翻译]C的影响力

(译者注:这是非常好的一篇短文,是我见过的论述C语言影响力最好的文章,特此翻译过来,和大家一起分享。同时也作为我写的书中第一章的一部分。 原文见:http://ripplingbrainwaves.blogspot.com/2007/07/c-reflections.html

[做人要厚道,转载请指明出处!]

C在我的记忆中占据着重要的地位,因为它是我学过的真正通用而且具有专业扩展性的语言之一。我被一些人的想法吓到了,他们说希望学习计算机编程的诫条和技巧,但除了C的名气外却对它一无所知。

让那种想法变得吓人的原因是C持续增长的至关重要性。就像人类历史是当前文明和文化成型的看不见但又普遍的因素一样,C(尤其是用C/C++写的软件)是当前软件开发生态系统中看不见但又普遍的内在因素。给我一个操作系统,编译器,集成开发环境,Java虚拟机,设备驱动程序,我会给你指出C的影响力。

C的集中性和有效性直接来自于它的这种能力——不仅能那么紧密地映射到执行它的机器,而且能让程序员从极痛苦的实际硬件(寄存器,内存分段,字节序,操作码)的细节中抽象出来。实际上,C/C++编译器在弥补这个缺口上已经变得非常成熟了,一个不专业的程序员尝试自己去做这些甚至会降低优化的水平。我知道C的替代品已经出来了(通常和C差别并不大),但C自身的成功看起来已经把它固定为其生境中默认的选择了。

然而,用C/C++做实际的工作经常要当心机器结构和问题领域结构之间的平衡。考虑到“我需要密切地贴近机器工作”,因此选择C/C++;或者,考虑到“啊,把全部这些步骤都写出来太气人了”,因此逃离标准库或者众多其它的库中的一个,这都不需要花多长时间。我很惊讶,完善的内置字符串支持究竟缺乏到何种程度才导致某种用途的C的替代语言崛起。还有,缺乏成熟的内置数据结构,比如链表和映射,它们应该太广泛了,以至于不该提起。我陷入丢失堆栈跟踪,因为一些未捕捉的异常,甚至一些异常抛出。一些库或工具包为程序员提供了如此全面的函数和宏的封装,以至于感觉结果好像是一种方言(智能指针?向量?),这并不让人惊讶。

自然而然,折衷就是任何给定的程序中都不要使用这些外部的库或者语言特性,开销和复杂度也不是强制性的。而且,使用把程序员和机器联系起来的一种语言在某些方面是令人满意的。这种语言有大量的根据证明程序是为计算机,不是“理论的图灵机”,而写的:指针——只不过是有组织的一组变量单元的结构体,引用内存地址而不是对象的变量,以多种方式解释内存内容的能力,作弄内存的void*函数。它当然可能诡异,而且有时甚至很容易导致错误,但是“无用信息输入,无用信息输出”的原则始终适用。很多人在这方面比我更有经验,更熟练。

对我来说,C是象征性的编程语言,因为它巧妙地连接了人类思想和机器计算。那些能编写高效C代码的人也是能跨越两个领域的人。过分尝试让C代码更像人类思维,程序就可能会运行很糟糕。过分尝试为机器而裁减C代码,程序就可能变成难以扩展的,魔鬼般地神秘的一团。去学C/C++吧!人们喜欢说Lisp是如何激发灵感的。思想和机器在C代码中的混合可能会产生相似的灵感。至少,你可能会更加感激编译器/解释器实际上在为你做的工作。如果没有别的,只有读通C/C++的能力也是务实的,因为它仍然存活在企业和遗留的代码中。C/C++在开源世界中碰巧也是非常重要的,多亏了gcc这个东西,你可能听说过它(在我使用Gentoo的时候,gcc可能是CPU周期的最大消耗者)。

为什么void main是愚蠢的?

a) 最直接的回答:因为标准就是这么规定的。C99第5.1.2.2.1节“Program startup”
中明确规定好了的,不服不行。当然了,您要写不可移植的C就得另说了。

b) main函数的返回值用于说明程序的退出状态。如果返回 0,则代表程序正常退出;
返回其它数字的含义则由系统决定。通常,返回非零代表程序异常退出。在Win下
你可能觉得这并不重要,但请想想Unix/Linux下面那些依靠程序“返回值”工作的
脚本和Makefile!

c) main函数被认为要返回int(main is supposed to return an int.)。调用main的
程序假设它会返回一个int,并且把它压栈。如果它没有返回,就有可能导致
程序崩溃!

d) 在Win上可行并不代表都可行,在所有i386上可行也不代表都可行。世界是丰富多彩
的,除了Window$和i386,还有很多系统和构架。这里有一个void main导致程序崩溃
的例子,是在ARM上的RISC OS中,见下:
http://users.aber.ac.uk/auj/voidmain.shtml

e) C++之父在其主页上这样评价void main:“这种定义从来没有在C++中出现过,也没在C中出现。” “一致的实现可能提供多种main的版本,但他们必须都是int返回类型。” “即使你的编译器接受void main,你也要避免它。”

所以,你不应该,更不能教别人,使用void main。如果你的老师上课时教给你写void main,你应该毫不犹豫地站起来告诉他/她:这样是不对的! 同理,任何讲C语言的书中带有void main的例子都是不严谨的!