|

10/24程序员日快乐,祝所有键盘舞者不再孤单!快来看看计协特稿吧~
一天一个离职小技巧
前言
今天是10.24,是程序员的节日(但大多数程序员都在加班就是了)。
在这个特殊的日子里,作为一个未来有可能陷入中年退休危机的人,我不禁陷入了沉思——如何才能恰好程序员这一碗饭?
所谓恰饭恰饭,意思就是恰掉别人碗里的饭,只要让后来的人碗里没有饭,那程序员就永远不会退休。
为什么现在的企业喜欢招年轻人?因为肯加班。拿钱少。计算机领域也是一个急速发展的领域,有很多知识是老程序员们在学校中无没能接触的。这样看来,程序员的中年危机其实也是符合市场规律的。
古人云:“教会徒弟饿死师傅。”。只要中年程序员让自己处于无可替代的位置,便不会招致祸患。于是,为了让新人看不懂公司里的代码(bushi),混沌代码应运而生。
(这个看上去很迷幻的代码是可以编译的)
当你看完这个教程后,相信你也能在反复折磨中,一点点地写出人类不能理解的代码!(大雾)
01
特殊的main
曾经有人问过我这么一个问题:"程序运行时只能从main函数开始吗?"
简单来说是这样的:
[list=1,
[*,操作系统在创建进程后,把控制权交给了程序的入口,这个入口就是运行库中的某个入口函数。
[*,入口函数对运行库和程序运行环境作初始化,包括堆,线程全局变量的构造。
[*,入口函数在完成初始化之后,调用main开始执行
[*,main函数执行完成后返回到入口函数,然后入口函数进行清理工作,然后进行系统调用结束进程。
[/list,在这个过程中,我们发现main函数跟其他的函数没有任何区别,唯一的不同就是会被操作系统调用而已,有返回值,也可以有参数。
```c++
#include<iostream>
using namespace std;
int main(int x)
{
if(x!=10)
cout << main(x + 1) << " ";
if(x==1)
return 0; // 给系统的返回值最好为0,表示正常退出
else
return x;
}
```
```C++
10 9 8 7 6 5 4 3 2
```
(可以想想为什么是这个输出)
02
特殊的变量名
我们先来复习一下不能当变量的名称吧!
[list=1,
[*,变量名必须由字母、数字、下划线组成
[*,首字符不能是数字
[*,不能太长,具体长度限制由编译器设定。太长会被编译器截取前面的,如果定义的两个变量,在编译器可识别的范围相同,而后面的不同,编译器将视为同一个变量。
[*,不能是C的保留字,如if,for,main,break等不能作为变量名
[/list,依照这个命名规则,我们会发现一些有意思的变量
```c++
int _,__,___,____;
```
这种变量名是合法的……
使用ctrl+f替换你的变量名,可以给任课老师和助教留下不可磨灭的印象!(大雾)
```c++
using namespace std;
const int __ = 10;//养成良好的代码习惯,函数内不要出现常数
int main(int _)
{
if(_!=__)
cout << main(_ + 1) << " ";
if(_==1)
return 0;
else
return _;
03
特殊的逻辑运算符
二元逻辑运算符 && 和 || 有着极其重要的性质。
以a&&b为例
若a = 0,则 a&&b == 0
编译器十分智能,判断a&&b的值时,若发现 a==0 则根本不会去判断b的值。而 b 在表示一些函数时,根本不会被执行!这也更能混淆视听!
```c++
int func_a
{
puts("This is func a!");
return 0;
}
int func_b
{
puts("This is func b!");
return 0;
}
int main
{
if( (func_a&&func_b) == 0 )
puts("This is main!");
}
```
(看上去出现的func_b压根不会执行)
```
This is func a!
This is main!
```
继续修改我们最初的代码
```C++
const int __ = 10;
int main(int _)
{
(_!=__)&&printf("%d "]main(_ + 1));
if(_==1)
return 0;
else
return _;
}
```
04
特殊的位运算
按位或|,按位与&,按位异或^, 取反~ 等都是常用的一些位运算符,我们常常用它们替代我们一眼就能看出来什么意思的==,!= , +等常见运算符。
```c++
if(a^b) == if(a!=b)
a+1 = -~a
```
(更多的以后再学吧)
```c++
0x64 = 十六进制下的64 = 100
0xA = 10
```
```c++
const int __ = 0xA;
int main(int _)
{
(_^__)&&printf("%d "]main(-~_));
if(!(_^1))
}
```
(现在你还能一眼看出来这个简单输出2~10的代码吗?)
05
特殊的三目运算符
我们常常用简单的三目运算符去替代复杂的if-else结构,在这样做的同时也会降低代码的可读性,是混沌代码的不二之选!
```c++
(c?a:b)
```
这个表达是的意思是,c为真时这个表达式值为a,否则为b.(注:a,b应是同一类型的变量,至少可以强制转换…
```c++
int main(int _)
{
return (_^1)?_:_^_;
}
```
06
特殊的数组下标
我们都很熟悉数组的下标,例如a[0]表示a数组第0位。
但很多人不知道的是——运算符对原生数组满足可交换性
```c++
a[0]==0[a,
```
这样的写法是可以编译的!
我们也可以通过取址符&发现更混沌的代码
```c++
int x;
x == (&x)[0]== 0[&x,
```
```c++
const int __ = 0xA;
int main(int _)
{
(_^0[&__,)&&printf("%d "]main(-~_));
return (0[&_,^1)?0[&_,:0[&_,^_;
}
```
另外,也可以用类似手法调取字符串常量的某一位,如:
```c++
"114514"[3,==3["114514"]='5'
"114514"[2,==2["114514"]='4'
```
啊这……
小结
我们可以去掉不必要的换行符,得到最后的混沌代码
```c++
int main(int _){(_^0[&__,)&&printf("%d "]main(-~_));return (0[&_,^1)?0[&_,:0[&_,^_;}
```
(谁能想到这仅仅是从2输出到10的简单代码?)
至此,我们的代码终于不会被别人看懂了!
看吧,没人下岗的世界完成了。
课后作业
(节选自知乎)
试分析如下代码逻辑和作用
```c++
#include <cstdio>
int main(int _){_^0x64&&printf("%d "]_)&&main(-~_);printf(_^1?"%d ":"%d\n"]_);}
```
第二题:
```c++
int __ = {1, 10] 100] 1000};
int main(int _)
{
_ ^ 400 && (_ < 4 ? 1 : putchar(((!((_ / 4) / ((_ % 4)["3210"][__ - 48,) % 10) && (_ % 4) ^ 3) ? 32 : ((_ / 4) / ((_ % 4)["3210"][__ - 48,) % 10 + 48)))) && main(-~_);
putchar(((!(((_ + 3) / 4) / (((3 + _) % 4)["0123"][__ - 48,) % 10) && ((3 + _) % 4) ^ 0) ? _ ^ 398 ? 32 : 48 : (((3 + _) / 4) / (((3 + _) % 4)["0123"][__ - 48,) % 10 + 48))) && _ ^ 1 ? 1 : putchar(10);
}
```
|
|