2013年三月月 发布的文章

谋划2013

眼看2013年已经过去1/6了,这个谋划显然有些晚了。之所以晚,根本原因还是之前有些很多事情没有想清楚,即便是现在可能依旧比较朦胧。鉴于去年的目标执行情况不甚理想,尤其是工作目标方面,因此今年在谋划策略方面变得更加务实和收敛,期望能说到做到或做的尽可能的多。

一、个人目标

* 鉴于去年的执行情况,今年将blog定在80篇(大约每5天一篇)应该问题不大,毕竟blog已经成为我生活的一个重要组成部分了。

* 阅读是必不可少的。今年计划将读书目标定在40本(大约每9天一本)。去年的读书效率下降许多,感觉更多是因为自己变得懒散了。所以今年除了“扫库存”之外,还增加了一个改善措施:从省图书馆借书读。俗话说:书非借而不能读也^_^。借书读,一方面降低书架上的书增长的速度,减少了开支;另一方面还可能提高读书效率,借的书毕竟是要还的么^_^。昨天在整理果果的书时发现我的书架已经接近满员了,所以以后非经典书/非紧急书就不打算买了,不知道能否借此戒掉“买书瘾”,想必到时侯还是会纠结一番的^_^。最后在读书方面还是要给自己设置一条主线的,尽量围绕自己的目标达成去选择读哪些而不读哪些,读自己所需要的,按照自己的思路去读,千万不要人读己亦读。

* 学习新编程语言方面。每年一门新语言,但今年目标不甚明确。对函数式语言有些担心,前途似乎没有看起来的那么美,可预见到情况是将长期持续在小众领域徘徊。今年的策略是看缘份了^_^。在日常工作中,Python这门语言的使用是愈来愈多了,今年在Python方面肯定是要继续深入研究一点点的。

* 开源方面。我将继续和同事一起推进buildc的演进,至少会完成已经策划已久的0.3.0版本的设计与实现。在开源方面目前尚未参与过其他人发起的项目,这块更多还是自己找点子,期望今年能有一些新的想法。

* 个人健康方面。前些日子得了肺部感染,连续挂了近两周点滴才控制住病情,钱没少花,罪没少遭,还好目前看起来像是痊愈了。恰因为此,今年才把这个单独拿出来作为个人目标的一部分。事后分析,之所以被细菌感染,更多是因为自己的免疫力太低了。事实上也是这样的:去年一整年都没有什么锻炼身体的活动,免疫力不低就怪了。所以今年打算把晨跑提上日程,记得11年是晨跑坚持时间最长的一年,每天跑上3km,各方面的感觉的确是非常好的。

* 关注代码。和2012相比,今年期望能抽出更多的时间和精力编写自己的代码、阅读和评审其他人的代码。对于程序员出身的我而言,代码的魔力是我无论如何都无法抗拒的。

* 其他能力提升方面。今年重点想提升一下当众Speaking能力。目前当众讲话是没啥问题的,但如何当众把话讲的更好更具吸引力,这方面还需要专门的学习和训练。

二、工作目标

不得不承认去年工作方面的失意让我对工作的热情有所衰退,所以今年在工作目标方面尽量收敛一些,也不想在这里把一些具体目标展开说明了,唯一所求的就是踏踏实实把重点任务做好。另外任何事情做久了,都想期待有一些变化。

三、家庭目标

虽然去年的家庭目标完成的十分不错,不过有了去年的家庭目标做铺垫,今年的目标达成难度加大了许多,压力山大啊。这里先列出一些简单的:

* 更新数码装备:本本、手机、相机、平板、电纸书等。
* 给果果转到规模较大的幼儿园,接受更为规范的教育。
* 常回父母家看看。
* 家庭成员省内、国内、境外游至少各一次。
…. 这里省略很多更难实现的家庭目标。

与工作相比,感觉我在家庭生活上的掌控力更弱一些,让我纠结的事情也更多一些。至于原因么,男人都懂的。

总体来说,至少从这篇blog的篇幅上来看,2013的目标比2012年要收敛许多,尤其在工作方面。有些事情等做完了再说也未尝不好。 

也谈C语言的Struct Hack

今天在浏览网友huangz编写的“Redis源码分析”时,看到如下redis中的代码:

struct sdshdr {
    int len;
    int free;
    char buf[];
};

说实话,这类代码我见过很多,但直到这次我才知道这种coding trick的真实英文称谓是:Struct Hack。

到底什么是Struct Hack?其实倒也没有什么明确定义。首先它是一种coding trick;其次一定是与struct相关的;关键是struct中要仅有一个变长的字段,且该字段是struct中最后的一个字段,就像上面 sdshdr中的buf那样。这样的coding trick到底有何作用呢?

我们来看看redis中是如何利用这种coding trick的。sds是redis string的一种实现,全称是Simple Dynamic Strings,从字面意义来看,这是一种动态字符串,是可以在运行时确定其大小并创建的。我们来看看其创建代码:

typedef char *sds;

sds sdsnewlen(const void *init, size_t initlen) {
    struct sdshdr *sh;

    if (init) {
        sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
    } else {
        sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
    }

    if (sh == NULL) return NULL;

    sh->len = initlen;
    sh->free = 0;

    if (initlen && init)
        memcpy(sh->buf, init, initlen);
    sh->buf[initlen] = '\0';

    return (char*)sh->buf;
}

sdsnewlen在分配内存时,一次分配的内存大小不仅仅是sizeof(struct sdshdr),而是加上了真正存储字符串的buf的大小,并将buf作为返回值返回,sds就是buf,buf就是sds。这样通过sdshdr实例, 我们可以直接获得其对应的sds,也就是buf。更为关键的一点是,如果我已知sds,我们还可以获得其对应的sdshdr(huangz在文中称 sdshdr是sds handler的缩写,我倒是觉得hdr更像是header的缩写),见下面代码:

static inline size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
    return sh->len;
}

这种trick给代码带来的极大的效率。想象一下如果redis的sdshdr定义是这样的:

struct sdshdr {
    int len;
    int free;
    char *buf;
};

/*  sdsnewlen */
struct sdshdr *sh;
sh = zmalloc(sizeof(struct sdshdr));
memset(sh, 0, sizeof(*sh));
sh->buf = zmalloc(initlen+1);

看起来似乎也能在运行时实现buf的动态size指定,但sdshdr与sds之间的纽带就被彻底割裂了(当然你也可以在 malloc sh时将buf内存也一并分配出来,然后手工将buf指向struct外的内存首地址,不过一旦这么做,就显得不那么tricky了)。

另外这里要探讨的是最后那个字段buf,是声明为buf[]好,还是buf[0]好,又或是buf[1]呢?redis使用的是buf[],在C99中这 是绝对合法的,这种定义被称为variable-length arrays(变长数组)。由于下标为空,这里的buf就好像是一个占位符,只有符号意义,但却并不实际占用空间。32bit平台下 sizeof(struct sdshdr) = 8,显然没有buf的份儿。不过在C99以前的标准中,是不允许变长数组出现的,你的Gcc很可能出现如下警告:“ISO C90 不允许可变数组成员”。不过C99以前很多编译器的扩展默认都是支持变长数组的,这也是这种trick之前就大行其道的原因之一,只不过是在C99之后变 得名正言顺了罢了。

如果将buf[]改为buf[0]呢?在C99以及支持变长数组扩展的编译器下也都是等同于buf[]的,不过C99以前的标准编译器还是会警告:ISO C 不允许大小为 0 的数组‘buf’ [-pedantic]。

用buf[1]替代buf[]则是一个兼容性最好的方案。在一些其他开源代码中,你也会常见buf[1]这种情形,如果以redis hds代码为例,我们用buf[1]替代buf[0]:

struct sdshdr {
    int len;
    int free;
    char buf[1];
};

相应的,sdsnewlen的代码以及sdslen中通过sds获取sdshdr的代码就应该做相应的修改了,简要修改如下:

/* sdsnewlen */

sds sdsnewlen(const void *init, size_t initlen) {
    struct sdshdr *sh;

    if (init) {
        sh = zmalloc(sizeof(struct sdshdr) – 1 + initlen + 1);
    } else {
        sh = zcalloc(sizeof(struct sdshdr) – 1 + initlen + 1);
    }

    if (sh == NULL) return NULL;

    sh->len = initlen;
    sh->free = 0;

    if (initlen && init)
        memcpy(sh->buf, init, initlen);
    sh->buf[initlen] = '\0';

    return (char*)sh->buf;
}


static inline size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*)(s-(offsetof(struct sdshdr, buf)));
    return sh->len;
}

注意:使用这种coding trick为的就是获得一种运行时的动态行为,struct的大小也是动态的(这种struct的声明是一种incomplete type),所以这种struct都是在堆上分配内存的,在栈上分配显然是没有标准可移植的方法的;同样,由于是size不确定的incomplete type,这种struct一般不用于声明struct数组。

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats