KEEP K.I.S.S.

tk's blog

推荐一篇文章

Notes on Programming in C

文章讲述C编程里的一些注意点,涉及命名、风格、数据结构。作者是 Rob Pike

其中关于注释、复杂度、数据驱动以及函数指针这几小节特别值得一读。

简要笔记:

注释

作者倾向于少写注释,只写必要的

  1. 代码清晰,命名良好的代码应该是自解释的
  2. 编译器不会检查注释,不能保证注释是正确的,尤其是代码已经修改而注释没有更正
  3. 注释会打乱代码的排版
  4. 注释用于解释全局变量或类型的用法,介绍不寻常或者重要的子程序,将大量计算划分小节
  5. 避免花哨的注释
  6. 给数据写注释要比算法注释有用的多
  7. 如果代码需要注释来帮助理解,那么应该考虑重写以便于理解

复杂度

作者认为大部分程序写的过于复杂源于不好的设计,但是没有讲设计而是给了6条规则

  1. 避免过早优化
  2. 在没有测定程序运行效率前,不要进行优化
  3. 高级算法在数据规模小的时候会变慢,除非确信数据规模很大,不要使用高级算法。就算 数据规模很大,首先考虑规则2
  4. 高级算法比简单算法更难实现,更容易滋生bug,使用简单算法和简单的数据结构
  5. 数据主导。使用了正确的数据结构,那么算法就很清晰明了
  6. 没有规则 6

总结起来2点:杜绝过早优化,数据主导

包含文件

包含文件不应包含已经被包含的文件

避免编译依赖和物理耦合

PS:这里介绍两个技巧

  1. 前向声明,用于避免引用整个头文件
  2. 不透明指针,在C++中常称作 Pimpl idiom,可以完全接偶接口(头文件)和 实现(源文件),修改实现可不必重新编译其他引用了模块的部分,可以用来保证二进制 兼容性(在制作动态库时很有用)。

lua 之爱

《programming in lua》第二版的中文版这本书快看完了,中间夹杂着英文版的看,因为翻译并非完美,而且英文版的也很容易看懂。

这里是我的读书笔记:http://book.douban.com/people/tisyang/annotation/3076942/ 但并非每个要点都会有笔记。

lua 是一个值得学习的语言而且也不会需要过多时间,这本书是学 lua 一定要看的,云风 大神也这样推荐

愈学愈爱

C 语言中的位取反操作符

话不多说,先来看一段代码:

 

 1 #include <stdio.h>
 2 
 3 int main(int argc, char *argv[])
 4 {
 5         unsigned char uc = 0x1f;
 6         unsigned int ui = ~uc;
 7         unsigned long long ull = ~uc;
 8 
 9         unsigned char cc = ~uc;
10         printf("uc = %#x\n", uc);
11         printf("cc: ~uc = %#x\n", cc);
12         printf("ui: ~uc = %#x\n", ui);
13         printf("ull: ~uc = %#llx\n", ull);
14         return 0;
15 }

因为用到了 C99 标准的 long long 类型,所以编译时候需要添加 "-std=c99" 选项:

gcc -Wall -std=c99 -o file file.c

输出结果(x86 mingw32):

 

uc = 0x1f
cc: ~uc = 0xe0
ui: ~uc = 0xffffffe0
ull: ~uc = 0xffffffffffffffe0
 
这里显现出了 位取反 操作的结果特殊性,对于通常而言,我们以为一个 unsigned char 位取结果的类型也应该是 unsigned char ,也就是说当我们把这个结果赋给一个 unsigned int (或者 unsigned long long)类型的值时候,这个值也应该是 不会超过 unsigned char 的取值范围的。但是上述代码显示出并非这样的情况。
 
上述代码显示出把 unsigned char 位取反值赋给 unsigned int 变量时,除了低位字节是补码外,高位字节都是 0 的补码 1了。对于赋值给 unsigned long long 变量也是如此。
 
我个人理解的解释是,在 x86-32 的机器上,机器字长为 32,寄存器长度也是32,对于像 unsigned char 这样的 1 byte 的值,运算时会存储在寄存器中的低位字节,高位字节都为0,然后位取反操作指令会对整个寄存器的值位取反,然后赋值给 unsigned int (4 bytes)这样的变量时,是将整个寄存器值取出。这样高位字节都会是 0 的补码 1。但是这样怎么解释赋值给 unsigned long long 类型变量发生的情况呢?或者说对于位取反操作的结果是个特殊状态的值?
 
PS: 最近在看 CSAPP(《深入理解计算机系统》)第二版,其中在做练习题 2.12 发现了上述情形,之前不知道,所以就研究了一下下,不过还是半解。。。。

Simple, not simpler

好久没有写小结型的文章了,主要是这段时间以来感觉自己稍微有些混乱,思绪繁飞不得掌控。

这段时间主要是看《代码大全》(第二版),内容很细致,包括从思路设计到实际编码各个环节,有很多常用的技巧以及注意事项。

开始看 Lua 的相关书籍了,这主要是受到最近一直泛起的“simple”的思绪,渴望回归“简单”。《Programming in Lua》这书很不错,目前看到 chapter 7,重大的收获是 对于函数式编程的的一些较深的理解,比如 closure 和 tail call。

之前学习的 Ruby 在目前工作中最主要的用途还是 正则表达式,用来处理文本数据。主要是自己弱爆了,不想去学 grep,sed 这些更为适合的工具,不过好在 Ruby 用的也是 Perl 风格的正则表达式。Ruby 的主张是 Coding for fun, Ruby 很容易学,也很容易用,主要是因为 面向对象设计完全和多种多样的方法风格。我现在就主要靠 irb (Interactive Ruby Shell) 来做计算器,功能强大,方便实用。 [Θ▽Θ]

交叉看的其他一些书籍有《代码整洁之道》(看书名就知道是干嘛的了,书中的语气措辞我比较喜欢,偏激进)、《深入理解计算机系统》(真的是“深入”,好书不需要解释,看完前言估计你就很有胃口了)、《OpenGL 超级宝典》(偏向工具库方面。。。)

当前最大的问题是心态噪杂,难以平复,想沉浸如那种简简单单的状态却感到困难。看来自己当前悟性还是不够,也许是自己太过心急了。

贴一句爱因斯坦的话共勉:Make things as simple as possible, but not simpler.

命令行、脚本化、自动化以及其他

昨晚一个同学问了我一个问题,他跟着导师做项目,问为什么以及怎么去解析特定样式的命令行参数,比如

./DT -r rrr.txt -t ttt.txt -p 0 -s 5 

这里明显是为了脚本化和自动化用的,具体解析就不讲了,其实命令行参数设置可以参考 GNU 风格,比如上面的这个就可以写作

./DT -r,rrr.txt -t,ttt.txt -p,0 -s,5 

GNU 风格的好处在于明辨,参数项和参数值在同一个字符串中,用逗号分隔,进行解析时候就简便一些。命令行的函数好处在于便于使用管道(PIPE)和脚本来实现自动化,避免低效的手工和重复。

这段时间自己在看《程序员修炼之道》,非常好的一本书,感觉到自己有好多的不足,至少从怎么做一个卓有成效的程序员,来讲,我并没有明显的使用脚本去自动化去做一些事情,总是依赖GUI和手工。。。。

其实讲的很多内容并不是局限在编程这个领域,书中也是这么说的,学以致用,卓有成效的手段在各个领域都会有所用处。看来自己还是得不断努力了,不要再愚昧愚蠢的手工重复。。。。。

此书强烈推荐。