Chaofan

For the next train

作者: qcf

  • 构建Mao语言运行器-词法分析

    这个世界的本质是无序的。所以编程最广泛也是最常被讨论的用途就是处理在计算机看来什么意义也没有的字符串。而处理编程语言的源代码在其中是很特殊的一种,因为编程语言包含的信息按照一定的规则聚合,解析起来比自然语言容易得多。而处理编程语言的源代码的时候该干什么,取决于软件的目的以及这门语言本身的特性。

    好了,言归正传,说说这次的期末作业Mao语言吧。一开始很多同学估计还没反应过来是怎么一回事。以为做一个解释器只要用宏替换就可以了。另外一些人估计连解释器编译器的区别都没有分清楚。讲道理的话,大一的期末项目不太可能要求学生做编译器的,那么多数据结构都没学,汇编也不懂,没法弄。

    那么什么是解释器呢?

    解释器是能够执行用其他计算机语言编写的程序的系统软件,它是一种翻译程序。 它的执行方式是一边翻译一边执行,因此其执行效率一般偏低,但是解释器的实现较为简单,而且编写源程序的高级语言可以使用更加灵活和富于表现力的语法。

    懂了没?一边翻译一边执行。读入输入然后处理的作业在之前也做过不少,所以有人想到一个一个“单词”地做。关键是,我们处理这些源代码的基本单位是什么?先来看看范例里的一段代码。

    int a;
    double x, y;
    int b, c, d;
    a=5.5;
    x=y=(1+a)*6.44;
    a+4;
    a=a/2;
    print(a);
    y=(c+6)*-(1+1);
    print(y);
    d=a/(2/5);
    print(d);

    这段代码基本上涵盖了Mao语言所有能用到的符号。仔细观察可以发现,关键字只有int和double,如果把print写死进去那就是三个。程序中可以出现任意的空格,因此我们需要把这些空格要么去掉要么屏蔽掉。变量名可以是一个字母也可以是很多个字母和数字的组合,当作一个char来弄显然也不行。我们需要一个专门的过程来将源代码里的“多个字符”映射到一个单位里。我们理想的结果应该是生成一个更有结构的数据流,其中一个符号(int、变量名或者是运算符)是一个单位,没有空格的存在。这样的话,很多东西就方便很多了。

    这个扫描源代码生成结构化数据流的过程就叫做词法分析。大多数编译器或者解释器接收到源代码做的第一件事就是词法分析。词法分析生成的东西叫做token,记号还是符文还是什么,随便翻译。显然,我们要做词法分析,就要把整个源代码字符串遍历一遍,并且扫描完之后就可以把它丢掉了,之后的处理是针对这个数据流而不是纯文本进行的。在整个编译或者解释执行的过程中,对源代码或者它对应的信息我们需要过不止一趟 (pass)。多趟的处理也许对效率有影响,但是实现更加简单。所谓one-pass的编译器,很多时候只是作者的炫技之举。当然,对语法极其简单的Mao语言,这不是不能做,只是写得太杂乱就会失去很多乐趣。

    好了,如何定义我们的数据结构呢?比如:

    struct token {
        int type;
        char * content;
    };

    这是最简单的形式。content表示什么自不必说,可以指变量名或者整数浮点数。而更多的固定形式的符号,我们直接用type表示,然后把content设为NULL即可。

    #define TOKEN_INT       1
    #define TOKEN_DOUBLE    2
    #define TOKEN_PRINT     3
    #define TOKEN_VARIABLE  4
    #define TOKEN_INTNUM    5
    #define TOKEN_DOUBLENUM 6
    #define TOKEN_COMMA     7
    #define TOKEN_SEMICOLON 8
    #define TOKEN_ADD       9
    #define TOKEN_SUB       10
    #define TOKEN_MUL       11
    #define TOKEN_DIV       12
    #define TOKEN_ASSIGN    13
    #define TOKEN_LPAREN    14
    #define TOKEN_RPAREN    15

    整个Mao语言的符号不超过这区区十五种。规定好了token的结构和type的取值,我们就可以开始扫描的过程了。

    开始这个扫描的过程应该不算是什么难事。我们从输入的第一个字符开始判断,就像《C程序设计语言》里常用的那样:

    char ch;
    while ((ch = getchar()) != EOF) {
        /*
         * 对输入字符的类型进行判断
         * 如果是一个字符,我们进入处理字母集合(类型、print或变量名)的函数
         * 如果是加减乘除等符号,我们进入处理运算符的函数
         * 如果是数字或者点号,我们进入处理整数或浮点数的函数
         */
    }

    那么对此,我们可以声明这样的一堆函数:

    void type_var(char ch);
    void number(char ch);
    void symbol(char ch);

    这些函数内部是如何运作的呢?举个例子:

    void type_var(char ch)
    {
        char tmp[20];
        size_t index = 0;
        do {
            tmp[index++] = ch;
        } while ((ch = getchar()) != EOF && isalnum(ch));
        tmp[index++] = '\0';
        if (!strcmp(tmp, "int")) {
            /* 将当前流指向的token设置为TOKEN_INT */
        } else if (!strcmp(tmp, "double") {
            /* 将当前流指向的token设置为TOKEN_DOUBLE */
        } else if (/* 其他情况 */) {
            /* 对应的处理 */
        } else {
            /* 不是关键字,那么定义成变量名,TOKEN_VARIABLE */
        }
        ungetc(ch, stdin); /* 退回一个字符输入 */
        /* 流指针向前进 */
    }

    显然,这个词法分析程序会自动获取能够最大接收的token,并且不需要手动写,自动就排除掉了空格,并且将每个变量名与逗号分隔了开来。

    由于Mao语言的符号数量很少,并且各个类型的token规则特意设置得很简单(浮点数甚至不包含科学计数法的形式)。写一个tokenizer还是非常容易的。不过又一个还没有提到的东西,就是词法分析整数或者浮点数的时候,前面的正负号会被识别成加减号。当然这个问题无关紧要,讲道理的话在后面的语法分析阶段也能解决。不过就当作一个练习吧。

    词法分析就到这,下次来说说语法树的事情。

  • 用C写内存管理真累啊

    今天C语言老师布置了期末的大作业,要求实现一个毛语言。由于我进教室的时候迟到了,所以并不知道老师在此之前说了什么。不过看了一下屏幕上展示的PDF,只是要求实现最基本的变量声明和计算、输入输出的功能。不过我想着,既然做就做好点,把扩展的功能都给加上去。

    思索一会后,我决定把项目中的一部分分离为infrastructure目录,包含一些常用的数据结构,比如链表、字符串、树,以后可能还有散列表。今晚上完毛概以后我就开始动手写,从链表开始(qlist)。虽然之前写过好几次,不过要用C一次写对不出bug还是一件不容易的事情。尤其是我还要在这个链表上模拟泛形。由于C语言没有像C++一样强大的模版,于是我只有用void*来表示数据,然后给链表的结构体里加上一个data_size的成员以表示数据的长度,再要求两个函数指针分别作为深拷贝和内存释放的方法。不过这样也是有问题的,那就是基本数据类型的开销太大了,操作起来也麻烦。比如我要给一个成员类型为int的链表插入,我还得专门定义一个变量,然后把它的地址传给拷贝函数。所以可以考虑一下用宏来解决这个事情。

    话说回来,clion确实是个挺好用的IDE.

  • Hello, world!

    又把服务器的操作系统给换成了Debian,看来也不是一帆风顺,充满了坑。我用源安装的Wordpress有问题,一直提示我确少/etc/wordpress目录下的一个文件。直到我从服务器直接下载了一个解压,好了。然后又是插件无法更新的问题,结果把Wordpress目录的所有者改成apache进程的运行者www-data才行。

    唔,好累,明天C的“大”项目(写不了1000行你告诉我叫大项目?)要来了。然后还要帮别人做点网页什么的,话说我对做网页其实挺无感的……

  • 用C写的一个简易JSON Parser

    初来乍到的一学期实在是无聊,于是在百团大战中加了计算机协会,就认识了几个计算机和软件专业的人。看见一软件同学在空间上转发的C语言期末项目,顿时生了兴趣。代码是春节期间写完的,可惜这个博客被搁置了太久,现在把代码贴上来,也不算过分吧。

    作业的具体内容是:老师给定一个头文件 json.h ,包含了 json 数据结构的定义以及一些相关函数的声明,任务就是实现这些函数,最后提交通过学校的自动化测试。我不是软件的学生,自然没有这个权利,不过手工检验了一下,貌似还没有太大问题。只是最近写一个 web 项目的时候,把其间一个比较“庞大”的 json 文件(也才4KB!)当作测试材料,结果程序直接崩溃了;再截取一小部分,又能运行了。难道是字符串读写复制操作太多?可貌似连 fopen 函数都调用失败了,搞不懂。

    老师们给的头文件想必是由 cJSON 的头文件修改而来的,json结构体的定义如下:

    typedef struct JSON {
        int type;   /* The type of the item, as above. */
    
        char *valuestring;  /* The item's string, if type==JSON_STRING */
        int valueint;   /* The item's number, if type==JSON_TRUE||JSON_FLASE;
                          or quantity of members, if type==JSON_ARRAY||JSON_OBJECT */
        double valuedouble; /* The item's number, if type==JSON_NUMBER */
    
        // the following are created by me
        struct JSON *head; /* Head of array, if type==JSON_ARRAY||JSON_OBJECT */
        struct JSON *last; /* Previous member, if this belongs to an array or object */
        struct JSON *next; /* Next member, if this belongs to an array or object */
        char *name; /* Name of member, if this belongs to an object */
    } JSON;

    函数众多就不一一列举了,有几个实现起来相对复杂的:

    void PrintJSON(JSON *item);
    JSON *Duplicate(JSON *item, int recurse);
    JSON *ParseJSON(const char *value);```

    如果有朋友对 json 不熟悉的话,简单介绍一下:

    {
        "name": "ecnelises",
        "school": {
            "primary": "Chenjiawan",
            "junior": "qinghua",
            "senior": "yucai",
            "university": "tongji"
        },
        "randomvalue": [
            1.5,
            18,
            true,
            null
        ]
    }

    json 的类型有 object、array、string、number、boolean(true、false)、null. array 用方括号 [] 括起来,object是“键值对”(类似C++中的 map),用花括号 {} 括起来。json 是一种简化版的数据结构(相比于庞杂的 xml),在 javascript 和其他地方广泛使用。

    第一个要介绍的函数是 Print,顾名思义就是根据既有的 json 打印出规整的内容。这个函数本身的定义很简单:

    void PrintJSON(JSON *item)
    {
        if (item == NULL){
            return;
        }
    
        switch (item -> type) {
            case JSON_FALSE:
                printf("false");
                break;
            case JSON_TRUE:
                printf("true");
                break;
            case JSON_NULL:
                printf("null");
                break;
            case JSON_NUMBER:
                printf("%lf", item -> valuedouble);
                break;
            case JSON_STRING:
                printf("\"%s\"", item -> valuestring);
                break;
            case JSON_ARRAY:
                PrintArray(1, item, 1);
                break;
            case JSON_OBJECT:
                PrintObject(1, item, 1);
                break;
            default:
                break;
        }
    }

    关键在于调用的另外两个函数:PrintArray 和 PrintObject. 引入这两个函数的原因是方便递归。

    void PrintArray(int layers, JSON *array, int newline)
    {
        if (newline) {
            for (int i = 1; i < layers; ++i) {
                printf("    ");
            }
            printf("[\n");
        }
    
        JSON *find = array -> head;
        for (int c = 1; c <= array -> valueint; ++c) {
            if (find -> type == JSON_OBJECT) {
                PrintObject(layers + 1, find, 1);
            } else if (find -> type == JSON_ARRAY) {
                PrintArray(layers + 1, find, 1);
            } else {
                for (int i = 1; i < layers + 1; ++i) {
                    printf("    ");
                }
                PrintJSON(find);
            }
            if (c < array -> valueint) {
                printf(",");
            }
            printf("\n");
            find = find -> next;
        }
    
        for (int c = 1; c < layers; ++c) {
            printf("    ");
        }
        printf("]");
    
        if (layers == 1) {
            printf("\n");
        }
    }
    
    void PrintObject(int layers, JSON *object, int newline)
    {
        if (newline) {
            for (int i = 1; i < layers; ++i) { // Each calling executes all in the same layer
                printf("    ");
            }
            printf("{\n");
        }
    
        JSON *find = object -> head;
        for (int c = 1; c <= object -> valueint; ++c) {
            for (int i = 1; i < layers + 1; ++i) {
                printf("    ");
            }
            printf("\"%s\": ", find -> name);
    
            if (find -> type == JSON_OBJECT) {
                printf("{\n");
                PrintObject(layers + 1, find, 0); // '{' doesn't make a new line
            } else if (find -> type == JSON_ARRAY) {
                printf("[\n");
                PrintArray(layers + 1, find, 0);
            } else {
                PrintJSON(find);
            }
            if (c < object -> valueint) {
                printf(",");
            }
            printf("\n");
            find = find -> next;
        }
    
        for (int i = 1; i < layers; ++i) {
            printf("    ");
        }
    
        printf("}");
    
        if (layers == 1) {
            printf("\n");
        }
    }

    参数 layers 决定了当前要打印的东西在第几“层”,也就是说前面要有多少层缩进。newline 是为了处理冒号后 { 或 [ 不换行的情况。

    下面的函数 Duplicate 是负责复制 json 的,recurse 参数决定是否“深度”复制,我想大概是递归的意思吧。

    JSON *Duplicate(JSON *item, int recurse)
    {
        JSON *source = item;
        JSON *target = NULL;
        JSON *last = NULL;
        JSON *count;
    
        while (source != NULL) {
            switch (source -> type) {
                case JSON_TRUE:
                    target = CreateTrue();
                    break;
                case JSON_FALSE:
                    target = CreateFalse();
                    break;
                case JSON_NUMBER:
                    target = CreateNumber(source -> valuedouble);
                    break;
                case JSON_NULL:
                    target = CreateNULL();
                    break;
                case JSON_STRING:
                    target -> valuestring = CopyString(source -> valuestring);
                    break;
                case JSON_ARRAY:
                    target = CreateArray();
    
                    if (source -> head != NULL && recurse) {
                        target -> head = Duplicate(source -> head, 1);
                    } else if (source -> head != NULL && !recurse) {
                        target -> head = source -> head;
                    }
    
                    count = target -> head;
                    while (count != NULL) {
                        target -> valueint += 1;
                        count = count -> next;
                    }
                    break;
                case JSON_OBJECT:
                    target = CreateObject();
                    target -> name = CopyString(source -> name);
    
                    if (source -> head != NULL && recurse) {
                        target -> head = Duplicate(source -> head, 1);
                    } else if (source -> head != NULL && !recurse) {
                        target -> head = source -> head;
                    }
    
                    count = target -> head;
                    while (count != NULL) {
                        target -> valueint += 1;
                        count = count -> next;
                    }
                    break;
                default:
                    break;
            }
    
            source = source -> next;
            target -> last = last;
            if (last != NULL) {
                last -> next = target;
            } else {
                item = target; // Give the begining position to item
            }
            last = target;
            target = target -> next;
        }
    
        return item;
    }

    函数思路简单,但是算不上太好写……用“同步跟踪”的方式从当前 json 的子节点开始,最后再把头补上。

    相对而言最复杂的要数 ParseJSON 函数了。基本想法就是先搜索配对的括号,然后递归处理括号里边的内容,直到没括号的时候就根据冒号和逗号分隔开各个字符串然后进行解析。由于源字符串可以不是规整的,所以还得跳过其间的空白。没有学过编译原理,所以程序也不带出错功能,错误的输入结果是未知的。

    JSON *ParseJSON(const char *value)
    {
        /* We read the whole string char by char,
           and create a new function parse_object.
           We make two tables to record emergency of {} and [].
         */
        int blank_state = 1; // 是否空白
        int flayerl = 1;
        int flayers = 1;
        int lallb = 0, sallb = 0; // 记录所有出现的括号
        int lbrackets = 0, sbrackets = 0; // 记录“第一层”的括号
        unsigned long vallen = strlen(value);
        int *lbracket_table = NULL;
        int *sbracket_table = malloc(sizeof(int) * sbrackets);
    
        for (int i = 0; i < vallen; ++i) {
            if (value[i] == '{' && flayerl) {
                flayerl = 0;
                ++lbrackets;
                ++lallb;
            } else if (value[i] == '{' && !flayerl) {
                ++lallb;
            } else if (value[i] == '}' && (lallb != lbrackets)) {
                --lallb;
            } else if (value[i] == '}' && (lallb == lbrackets)) {
                flayerl = 1;
                lbracket_table = (int*)realloc(lbracket_table, sizeof(int) * lbrackets);
                lbracket_table[lbrackets - 1] = i;
            } else if (value[i] == '[' && flayers) {
                flayers = 0;
                ++sbrackets;
                ++sallb;
            } else if (value[i] == '[' && !flayers) {
                ++sallb;
            } else if (value[i] == ']' && (sallb != sbrackets)) {
                --sallb;
            } else if (value[i] == ']' && (sallb == sbrackets)) {
                flayers = 1;
                sbracket_table = (int*)realloc(sbracket_table, sizeof(int) * sbrackets);
                sbracket_table[sbrackets - 1] = i;
            }
        }
        lbrackets = 0;
        sbrackets = 0;
        // 在这里,由于函数是递归调用的关系,两个表只存储“第一层”括号的内容
    
        JSON *res = malloc(sizeof(JSON));
        JSON *pre = res;
        JSON *find = res -> next;
        char *middle;
    
        for (int i = 0; i < vallen; ++i) {
            if (blank_state && isspace(value[i])) {
                continue;
            } else if (blank_state && !isspace(value[i])) {
                switch (value[i]) {
                    case '{':
                        find = parse_object(middle = substr(value + i, '{', '}'));
                        free(middle);
                        i = lbracket_table[lbrackets++] + 1;
                        pre -> next = find;
                        find -> last = pre;
                        pre = pre -> next;
                        find = find -> next;
                        break;
                    case '[':
                        find = parse_array(middle = substr(value + i, '[', ']'));
                        free(middle);
                        i = sbracket_table[sbrackets++] + 1;
                        pre -> next = find;
                        find -> last = pre;
                        pre = pre -> next;
                        find = find -> next;
                        break;
                    case '\"':
                        find = parse_string(value + i);
                        pre -> next = find;
                        find -> last = pre;
                        pre = pre -> next;
                        find = find -> next;
                        i += next_mark(value + i + 1, '\"'); // 返回出现第二个参数对应字符的下一个位置
                        break;
                    case 'f':
                    case 't':
                    case 'n': // 猜测是否为 false、true 或者 null,在函数里验证
                        find = parse_bool_null(value + i);
                        pre -> next = find;
                        find -> last = pre;
                        pre = pre -> next;
                        find = find -> next;
                        blank_state = 0;
                        break;
                    case '+':
                    case '-':
                    default:
                        find = parse_number(value + i);
                        pre -> next = find;
                        find -> last = pre;
                        pre = pre -> next;
                        find = find -> next;
                        blank_state = 0;
                        break;
                }
            } else {
                switch (value[i]) {
                    case ',':
                        blank_state = 1;
                        continue;
                        break;
                    default:
                        continue;
                        break;
                }
            }
        }
        pre = res -> next;
        free(res);
        res = pre;
        res -> last = NULL;
    
        free(lbracket_table);
        free(sbracket_table);
    
        return res;
    }

    附上 parse_object 和 parse_array 的代码:

    JSON *parse_object(const char *value)
    {
        JSON *res = CreateObject();
        JSON *tmp;
        int blank_state = 1;
        char *left;
        char *right;
    
        for (int i = 0; i < strlen(value); ++i) {
            if (blank_state && isspace(value[i])) {
                continue;
            } else if (blank_state && !isspace(value[i])) {
                left = divstr(value + i); // 返回第一个在所有括号之外的 “:” 或者 “,” 的位置
                i += next_mark(value + i, ':') + 1;
                AddItemToObject(res, (tmp = parse_string(left)) -> valuestring, ParseJSON(right = divstr(value + i)));
                free(left);
                free(right);
                free(tmp);
                i += next_mark(value + i, ','); // when this loop end, i will be added by 1
                blank_state = 1; // so here we don't have 1 as an addition
            }
        }
        return res;
    }
    
    JSON *parse_array(const char *value)
    {
        JSON *res = CreateArray();
        char *middle;
        int blank_state = 1;
    
        for (int i = 0; i < strlen(value); ++i) {
            if (blank_state && isspace(value[i])) {
                continue;
            } else if (blank_state && !isspace(value[i])) {
                AddItemToArray(res, ParseJSON(middle = divstr(value + i)));
                free(middle);
                i += next_mark(value + i, ',');
                blank_state = 1;
            }
        }
        return res;
    }

    虽然自己接触C语言有些久了,但是真正写出一个达千行的程序,这还是第一次。寒假在家里反复改反复调试,看来自己代码能力还是很不行。虽然在高手们看来可能这个程序还很拙劣,不过成功运行之后还是很有成就感的。加油吧。

  • 丧,得

    半年以前,我以为我右手执笔,左手握着一本三二,就可以上天入地,斩妖除魔,无所畏惧。我说我是文科里的逗比,理科里的奇葩,留给腾贵一个无奈的背影。看着周黄伟便秘的眼神,心里暗暗发誓,六月八号以后,我一定要将你黑过的那些闲书一本一本地翻完。我也没有想到,作为一个男的,如果变得沉默,除了听了煽情的空间故事,也可能是语文课被抽起来回答问题。想当年在寝室里,左长右粗,讲不完的黑话黄段,夜夜笙歌。我说我横扫了育才门外一条街,最后一次还是匆匆献给了重大精英。我着急地飞过天桥,穿越大街,等着一个背影出现。也会走着相似的路,感受一下民工分量的米线。周一中午,我都会阔气地出门,仿佛贝索斯开着他的大货车来迎接我。我手捧纸箱,飞奔回寝,一刀下去以后,又是一个星期的快活。星期天的早上,我睡到自然醒,惬意地走到教室,翻两道题,电话来了,于是又早退。徐怀雄算个鸟!要是厉害,就与我在大富翁棋盘上,杀个昏天黑地。那段时间,回家少了,在公交车上,看到南京青奥会的新闻,窃想要是我去了南大,青奥会还开吗。后来有人不明就里地问我,为啥比状元活活少了五十分,我想,肯定是因为我多长了五十斤肉。你要是看到每天晚上寝室的盛况,就晓得我夜夜笙歌用得一点都不过分。五六个人围在一个人的床上调戏一个女声的场景你见过吗?不用多想,既然她远渡重洋,历经艰险从佛罗里达来到重庆,不好好玩一玩,怎么对得起大哥的淳朴好客?来把大富翁,分分钟数万,生死一线间。上帝不掷骰子,掷骰子的我们更拽。只是可惜了,我们的乌诺。

    一年以前,刺激不输后来。你看我半夜飞跃,拖着笨重的身躯,说时迟那时快,冲向了别人的……不是床,是楼。经历了几个来回,方才明白,西厢记也不是白写的。话说我说了你别生气,我当时真觉得十一楼的女生傻啊,寝室里这么久少个人都不知道,要是哪天有个男的趁你们睡着了潜入进来咋办?男寝里就有个这样的危险人物啊。睡完自习,回去就两回七大奇迹,休整一下又飞去月下幽会,回来要是忘带钥匙也不紧不忙,等着上铺那位起夜的时候给我开个门。我还真没在八楼的楼梯上睡过一夜,倒也遗憾啊。上了床,还不忘逛逛当当网,下个单。要是发现某本书自己的水平不够,那就记着,等着高三毕业再买。没想到高三毕业还真买了,可惜当当网早已被打入冷宫。寝室里喜欢飞来飞去的还不止我一个,那位仁兄逗比一点不输我,周六早上十点给我来个短信问我在哪儿,答曰寝室,没想到他也在寝室。穿好衣服发现去上个英语课还来得及,于是在校外众人返回的目光中,我们也实现了当个学霸的梦想。后来索性学霸也不装了,佯称去教室自习。整个星期六都慵懒得一比,下午漫步去杨家坪,可以唱个歌,可以逛个街,可以喝个茶,就是不回家。第二天懒觉一醒,外婆做的大鱼大肉下肚,抄抄作业,又是一周。回想起来,逼格倒也是蛮高的。那时候就喜欢买点什么旧制度大革命呀,国史大纲呀,社会契约论呀什么的。拿到教室,放在书立顶层,恨不得人人看见。可惜了你们大概也是这么过来的,一眼拆穿我装逼,搞得我现在都想问学校图书馆接不接受学生捐赠书了。那时候周末回家,我都是不坐座位的。我以为我是总统,看着我的人民幸福,我很高兴。真的,我那时候真这么想过。

    两年前?那就别多说了撒,因为说多了丢脸,真的。好厉害哟,他们都拉我上竞赛诶!王伟看他才讲完,我就写好了程序,以为我天资聪颖,脑力过人,心想这孩子能够培养培养。但是我觉得他傻逼,转头去了文科提高班。后来,后来我才晓得到底谁傻逼……还有什么在厕所里几个男的给女生打电话呀,唱唱单身情歌呀,摩擦摩擦我的背包呀。后来看着腾贵,我就想说,阿祖比你不知道高到哪里去了,我还跟他谈笑风生。原来我以为我是数学天才的时候,在办公室问他,级数是什么。阿祖一笑而过,敲了我三下头。我顿悟,“三年后你就懂”。可惜同济高数讲得太慢。再往前倒,我这天才的名号前还得加上高傲二字。你们都是傻逼。你你你,你用个诺基亚侧滑拽个毛,还说苹果用的是安卓系统,简直是学哈了。

    我们都离理想越来越远。你看,如果再怎么努力,都成不了心中那个很厉害的人,为什么不开心一点?

    怕什么幼稚,我还一度觉得用苹果电脑的都是傻逼呢。

    待我一年之后,成了同济大王,再来跟你把酒促膝谈谈哥的故事。

  • 再见

    时间它过去得快吗?好像并不是。当你紧盯着它,妄图成就自己的快乐时,它不紧不慢,坚守着自己的节奏,让挥霍人生的家伙们分外恼火。然而时间虽然公平,但人心并不。有许多被遗忘的事情,当你重新发现的时候,会禁不住地感叹“哇,居然这么久了!”,并以此覆盖心中的那份遗憾。

    这里的博客便是如此。我看着前一次的更新,回忆那时候自己面对时间表,是怎样的一种心情。我惊叹,一个神秘的日期居然能够把人生分为迥然不同的两个段落。原谅我的无词和无思想,面对后一个段落,我实在没有做好启程的准备。为什么我的话变少了呢?仔细想了想,有关自己的话其实一直都少。

    最后的几个月迷上了知乎。的确,知乎是个好地方,有各路大神用完美的逻辑演绎着他们独特的世界观。但是,知乎上待久了发现,我越来越不像我自己了。我慢慢流失独立的思考和勇敢的态度,不敢表明自己的观点。也许是老了吧,懒于与人争论。但,也有可能是破碎的世界观,根本支撑不起实打实的交流。

    最近几天是录取结果出来的日子。看到许多人去了自己想去的大学和专业,我是真的笑不起来。其实几个月前,几年前,我也期盼着有这样的一个时刻——面带笑脸,把自己打扮得像个大学生的模样,毫不隐瞒地向别人提起自己的目标。可惜现实弄人——不,是自己弄人。我做过无数个梦,想着自己踏进大学、赢取机会的方式,想着未来是如何令人喜悦的一番景象。而现在,我也不知道我为什么会做出这样的决定,我也不知道那位老师给我的承诺是不是一句空话。除此之外对现实愈发清晰的了解也加重着内心的那份无力感。

    没有人在关心你,甚至于你自己。

    即使某件事就放在面前,如果我曾怀有幻想,那我还是会保持着距离继续幻想。但是时候不同了,继续幻想只会招来毁灭。无论有没有机会,也要想着向前。我渐渐失去了动力——这是失败者的典型表现。

    获取信息,寻找兴趣,持续行走。

    我别无选择。

  • 后会有期

    好久没有到这个网站看看了,又是一年。

    当初选文科这条路实在是单纯,就觉得能上好大学。然后呢?没想过。

    如今才开始纠结呢。随便一搜哪个IT公司用人要求第一条就是“计算机及相关专业毕业”。

    呵呵。

    这也怪不了谁。

    没办法咯,这种事情,只有等到两个月后再来慢慢思索了。

    后会有期。

  • 2013-2-19

    很久没有到这里来了,上一篇文章还是两年前的。

    其实中间来过几次,还提到过转学的事情,不过也都是一年多以前了。

    可以说认识了新的人,也忘掉了一些旧的人。回到母校去看看的话,一定会有强烈的物是人非之感。

    其实这篇文,颇带点为赋新词强说愁的感觉。也可能是因为,当我真正打开编辑器的时候,大脑就会变得一片空白吧。

    两年前的我一定不可能想到现在我读的是文科,更不会预见到现在的自己是这副模样。

    怎么说,被一群文科生熏陶了几个月,少说也得有点文艺气质啊。结果回头一看,放屁。

    在文科班,不仅要听历史老师整天灌输阶级斗争的理论,更要忍受数学课上一群女生疑惑的哀叹声。

    开始我甚至会后悔。不过慢慢地,还是带了点感情了吧现在。

    我会在这个方向上越走越远吗,又或者只是生命的一段绕路。

    不知道。

    也许,我也不知道什么是“我所理解的生活”。

  • 开源,让金山更美好

    才在百度C语言吧看到有人说金山卫士开源了,所以特地去Google了一下,发现了这个网址:

    http://code.ijinshan.com/

    第一次看到有国内的这方面的软件开源。不管别人怎么说,做好自己吧!界面貌似是MFC写的,看不懂了。

    希望国内越来越多的软件走向开源。也希望用户们早日扔掉360这个垃圾。

  • 我所习惯的C/C++代码格式

    1.函数开始的大括号专起一行。

    int sample()
    {
        return 0xA0246 * 0454;
    }

    2.类定义和内部内联函数定义。

    class foo {
    public:
        foo() : data(0)
        {}
        foo(int d) : data(d)
        {}
        void print()
         {  printf("%d\n", data);  }
    private:
         int data;
    };

    3.for、while、if等关键字后面1空格,且总是使用大括号。

    while (in != 0) {
        s += in;
    }

    4.头文件使用的保护。

    #ifndef FILE_H
    #deinfe FILE_H
    
    // ...
    
    #endif // FILE_H

    5.缩进4空格。

    6.类定义和实现、变量声明和函数定义勤注释。

    7.太多了,最基本的就这些。