C语言的一些Hacking写法
很显然,这些写法大多并不规范,也不被提倡。
很显然,咱并没有在windows下试过这些代码,实测大部分在线编程网站用的是Linux,可以接受GNU C扩展支持。如果有人问我为什么折腾,为什么以折腾这些无聊的东西作为目标,那他们完全可以问,为什么要登上最高峰?为什么人类要登月?………我选择去折腾,我,选择去折腾!(逃)
对int值进行位运算:
我们知道,int值以二进制存储。
于是可以用位运算来乘除2的次方数。
1 | int i = 114514; |
>>=2会除以4,以此类推。
Bang-Bang折叠布尔值:
我们知道,判断条件只有两个值,0
(false)和1
(true)。
我们知道,当一个数字前面被加上!
,它将变成一个判断条件。
我们还知道,双重否定表示极度肯定(sodayo~)。
于是,在C语言中:
1 | !!0 == 0 |
没错,双叹号下只有两个值,0和1。
ACM的文章Catch-23: The New C Standard Sets the World on Fire中,通过这个可能并非规范中有定义但确实有效的例子严厉批判了C2x规范将realloc()内存为0设置为未定义行为而非free()内存的行为
这一条,原因是realloc()做free()的写法和bang-bang一样在民间已经广为流传。
printf()中的三元表达式:
没错,至少GNU环境下,printf()中是可以套三元表达式的。
可以试下:
1 | printf(1 ? "true\n" : "false\n"); |
十分,甚至九分的优雅。
for()循环括号内执行代码:
C99放宽了对for()循环的限制。
于是就可以优(编)化(写)代(屎)码(山)。
init是一个表达式,但显然不做判断。
于是:
同时定义两个变量:
1 | for(int i = 0 | int j = 255; ; ) |
当然,有一定UB风险,因为init的值会被丢弃。
condition就是一个判断,不过对于i–的情况,可以用上面的!!判断是否为0。
1 | for (int i = 255; !!i; i--) |
increment是一段代码,显然不会做任何限制
1 | for (int i = 255; !!i; printf("%d\n", i--)) |
非常离谱。
GNU C扩展:
表达式中的语句和声明:
在GNU C中,你可以用({})
来获得一段表达式的值,这种表达式的值等于最后面的那个以;结尾的变量。
比如:
1 | int foo = ({ |
于是foo变量的值等于最后那行语句bar;中bar的值。
__VA_ARGS__宏:
这应该是GNU C最出名的特性了吧。
可以让宏定义接受不定参数。
比如:
1 |
三元表达式的缺省:
bar ?: foo
等同于bar ? bar : foo
有什么用?
比如说:
1 | fd >= 0 ?: exit(1); |
可以让fd在open()失败时退出程序。
还是有点用的。
零长度数组:
char u[0];
这样的写法是可以被接受的,一般放在结构体最后面,使结构体可以变长,普通程序基本用不到。不过在内存寸土寸金的年代,这样显然可以节省不少内存。
属性(attribute()宏):
attribute((constructor)):使函数在main()之前被执行。
attribute((destructor)):使函数在调用之后被执行。
attribute((unused)):函数或变量可能不会使用,可以作为注释用途或抑制编译警告。
attribute((aligned())):设置结构体对齐属性。
后记:
(null)