作者 | 梁唐
大家好,我是梁唐。
不知道大家有没有看过大佬编程呢?我印象最深的就是我第一次上acm赛场,acm赛场当中使用的都是Ubuntu系统的电脑。我就看到了前面几个清华的大佬熟悉地打开了vim,在漆黑的terminal当中上下飞舞,敲起了代码。
当时震撼的不行,脑子里反复的只有一个念头:什么是黑科技?这就是黑科技。
为此,我还特地学了一下如何使用vim编程,也开始了酷(装)炫(bi)生涯,比如经常在萌新面前露一手代码什么的。后来没过多久,我就发现赛场里大部分人都开始使用vim编程了,当然vim编程并不只是逼格高,也有一些其他的用处,比如编译运行比较快,字体也比较好看。
当然想要代码写的好看,有格调,只是换编译器是不行的,还需要在代码本身里面下点功夫。今天就和大家传授两招。
首先最简单的一个就是花括号的位置,虽然大家都说花括号怎么写分成两派,一派喜欢另起一行,一派喜欢跟在代码后面。
但是根据我的观察,至少在国内, 这两派基本上都是按照实力划分的。说真的,我干这行这么多年,就没见过一个实力强劲且花括号换行的大牛。几乎无一例外都是紧跟在后的写法,实际上这样写代码主体会更短,看起来的确会比较舒服。
当年我还是一个萌新的时候,花括号一直是换行写的,直到入了acm的坑,看过了一些顶级选手的代码。发自内心地感叹,括号换行实在是太丑了。所以我个人会比较倾向建议萌新朋友们,可以试试这么写代码,当然这只是我个人的看法。
if (xxx && xxx) {
}
for (int i = 0; i < n; i++) {
}
while (xxx && xxx) {
}
如果说花括号还只是个人习惯的话,那么位运算绝对算是装逼利器。
根据计算机二进制存储数据的性质,我们将一个整数左移一位等价于将它乘以2,右移一位则等价于除以2。所以我们就可以使用左移和右移的位运算操作来代替乘以2、除以2的乘除操作。
比如在二分的时候,我通常会这么写:
while (l+1 < r) {
int m = (l+r) >> 1;
if (xxx) {
l = m;
}else {
r = m;
}
}
如果再结合或运算,那么乘以2+1的操作也可以玩出花,写成 <<1|1,也就是左移一位,再对1计算或运算。比如在一些树形数据结构当中,我们要求一个节点的左右叶子节点的时候,经常会用到乘2+1的操作。
另外一个很常用的是使用 1<<n 来代替2^n ,这也是赛场当中常见的惯用手段。
位运算的操作当然也不只是为了逼格,也是有一定的实际意义的。最大的意义就是运算速度快,实际上左移右移的运算是所有运算当中最快的,比加减运算还要快。而乘除相对来说要慢许多,尤其是除法是最慢的,比加减操作可能慢上几十倍。
有些计算量很大的题目,使用位运算和乘除运算得到的效率能差很多,就会导致明明使用的是同样的算法,但是别人通过了,你却超时了的尴尬情况。
最后一个要说的就是memset,它是C语言当中对数组初始化的功能函数。
我贴一下它的源代码,其实非常简单,就是对一段连续内存进行赋值初始化。
void *(memset)(void *s, int c, size_t n) {
const unsigned char uc = c;
for (su = s; 0 < n; ++su, --n) {
*su = uc;
}
return (s);
}
读一下源码就会发现,它虽然也是一个循环赋值的操作,但是它赋值的对象并不是数组当中的元素,而是字节(char)。这一点要千万注意,比如我们将一个数组初始化成0,我们只需要这样写就可以:
memset(a, 0, sizeof a);
之所以这样可以,是因为0是一个特殊值,以int型举例。C++当中一个32位的int占据4个字节,我们将每个字节赋值成0,那么所有这32位全为0,所以写成十六进制的话会是0x00000000,这个结果自然就是0。
同样我们初始化成-1也是可以的,因为-1的补码全为1。初始化结束之后得到的每一个二进制位都是1,转化成十进制仍然是-1。
如果我们传入的不是0,也不是-1,就会有问题,比如我们假设传入的是3,那么初始化之后这个int就会变成0x03030303,这个数转成十进制显然不是3。所以我们想要通过memset将数组初始化成最大,可以传入0x7f,7f是8位二进制能够表示的最大正整数,使用7f初始化之后得到的int为0x7f7f7f7f,非常接近最大的正整数0x7fffffff。
不过一般比赛的时候,我用得比较多的都是0x3f,这样初始化得到的结果大约是最大整数的一半不到。这样的好处是可以保证当需要两个最大值相加的时候,它们的和也不会超过int范围。
最后做个简单的总结,当你需要对int型的数组初始化的时候,可以这么写:
int a[10000];
memset(a, 0, sizeof a); // 将a全赋值成0
memset(a, -1, sizeof a); // 将a全赋值成-1
memset(a, 0x7f, sizeof a); // 将a全赋值成0x7f7f7f7f7f,接近最大int
好了,这些提升代码逼格的小技巧,你学会了吗?