杂谈

Author Avatar
Magicmanoooo 3月 09, 2019
  • 在其它设备中阅读本文章

自己长期以来,在开发软件的时候总是感觉找不着北,甚有如履薄冰之感。在拜读了
miloyip大大的从零开始的 JSON 库教程之后,有了新的感悟。首先,自己就如同文章中所言,在进行测试或者debug的时候,都只会以printf/cout来打印结果,再用肉眼对比结果是否符合预期。但是随着软件项目变得越来越复杂,这种做法就会显得捉襟见肘。在这种情况下,我们便会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能够保证其他人修改代码之后,原来的功能依旧正确(这称为回归测试(regression testing))。

一般而言,软件开发是以周期为单位进行的。例如,加入一个功能之后,在写关于该功能的单元测试。与此同时,还有另一种软件开发方法论,称为测试驱动开发(test-driven development,TDD),它的主要循环步骤是:

  1. 加入一个测试
  2. 运行所有测试,新的测试应该会失败
  3. 编写实现代码
  4. 运行所有测试,若有测试失败就回到步骤3
  5. 重构代码
  6. 回到步骤1

TDD是先写测试,在实现功能。它的好处是:实现只会刚好满足测试,而不会写一些无关紧要的代码,或是没有被测试的代码。

但无论是采用TDD,还是先实现后测试,都应该尽量加入足够覆盖率的单元测试。

此外,TDD中另一个重要的步骤就是重构(refactoring)。重构的过程是:

在不改变代码外在行为的情况下,对代码作出修改,以改进程序的内部结构。

在TDD的过程中,我们的目标是编写代码以使得程序能够通过测试。但这个目标的诱导性过强,极有可能导致我们忽略正确性以外的其他软件品质。在通过代码之后,代码的正确性得以保证,此时,我们便应该审视现有的代码,看看有没有地方可以改进,而同时能保证测试顺利通过。我们可以安心地做各种修改,因为我们有单元测试,可以判断代码在修改后是否影响原来的行为。

位操作(Bit Twidding Hacks)

1. Compute the sign of an integer

int v;            //试图找出v的sign
int sign;

// CHAR_BIT是每一个byte的位数(一般为8)
sign=-(v<0);
// 或者,为了避免CPU的标志寄存器(flag registers)的branching
sign=-(int)((unsigned int)((int)v)>>(sizeof(int)*CHAR_BIT-1));
// 或者使用更少的instruction(但并不通用)
sign=v>>(sizeof(int)*CHAR_BIT-1);

最后一个表达式执行的运算(32位的integers)为:sign=v>>31。但它比常规的方法sign=-(v<0)要快。这个trick之所以能够正确运行,是因为当signed integers向右移位时(shift right),最左边的bit的值被复制到了其他位。当v为负时,最左边的位为1,否则为0。但是,这种操作只适用于特定架构的CPU。

此外,如果想要使得sign的结果为1或者-1,则需要进行一下操作:

sign=+1|(v>>sizeof(int)*CHAR_BIT-1));    // 如果v<0,则返回-1,否则返回+1

【注】:v为正数时,右移31位之后,高位补0,将得到1,再与1进行或运算,将得到1;当v为负数时,右移31位之后,高位补1,得到0xFFFFFFFF

因为int为带符号类型,带符号类型的最高位时符号位,又因为0xFFFFFFFF的符号位是1,所以这个数是负数。

在内存中,数值均为补码表示,所以0xFFFFFFFF是一个负数的补码。负数从补码求原码:最高符号位不变,其余各位求反,末尾加1。所以,0xFFFFFFFF的原码就是10000000 00000000 00000000 00000001,即-1

此外,如果想要的结果是-101,则需要这样使用:

sign=(v!=0)|-(int)((unsigned int)((int)v)>>(sizeof(int)*CHAR_BIT-1));
// 或者采用以下这种速度更快,但不通用的方式
sign=(v!=0)|(v>>(sizeof(int)*CHAR_BIT-1));
// 或者为了更加简洁、通用
sign=(v>0)-(v<0);

如果试图知道某个数是否为非负数,并返回结果+10,则需要这样写:

sign=1^((unsigned int)v>>(sizeof(int)*CHAR_BIT-1));

Detect if two integers have opposite signs

int x,y;
bool f=((x^y)<0);    //如果x和y符号位不一致,就返回true

Compute the integer absolute value (abs) without branching

int v;
unsigned int ret;
int const mask=v>>sizeof(int)*CHAR_BIT-1;

r=(v+mask)^mask;