我們知道C語言的自由度非常高,高到我們可以用其裝X。
陣列如何解釋
我們先來一段程式碼:
int main() { int i=0; int a[]={1,2,3,4,5}; 1[a] = 99; while(i<5) printf(“%d ”,*(i+++a)); return 0;}
請問:上面的程式碼有沒有錯誤?
答:上面的程式碼沒有錯誤,它的輸出是
1 99 3 4 5
有讀者就要問了,1[a]這麼表達沒問題嗎?這要涉及到在C語言中陣列的解釋,我們知道陣列頭就是一個指標,指向陣列的起始位置,而a[n]的含義就是*(a+n*sizeof(type(a))),注意後面的表示式n*sizeof(type(a)),這是一個地址偏移量。比如32位整型陣列a,a[3]第地址就是a+3*4。所以我們要得到a[3]的值也可以寫成*(a+3),而*(a+n)等於*(n+a),所以*(a+3)等於*(3+a),
而C語言對陣列a[n]的解釋始終是把陣列起始位置a和偏移量n加到一起得到真實地址,再從真實地址中取出值
,偏移量在前在後都沒影響。所以a[n]和n[a]是一樣的,指向同一個地址。
而*(i+++a)按照運算子優先順序和陣列規則等於a[i++],這個不難理解。
main函式也是可以被呼叫的
估計很多讀者都會思考一個問題,main函式能不能被呼叫,答案就是可以,我們來看下面的程式碼
int c=0;int main() { c++; if(c<5) main(); printf(“%d ”,c——); return 0;}
上面的程式碼也可以正常執行,輸出結果是
5 4 3 2 1
作品賞析
第19屆國際C語言混亂大賽優勝作品A clock in one line
main(_){_^448&&main(-~_);putchar(——_%64?32|-~7[__TIME__-_/8%8][“>‘txiZ^(~z?”-48]>>“;;;====~$::199”[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
這段程式碼可以正常執行,輸出為當前時間,需要新增2個頭檔案time。h和studio。h,輸出如下,我在15點03分14秒編譯輸出的結果:
這段程式碼用到了C語言中很多的基礎知識,但是讀者不要嘗試這樣寫,大神寫出來的是作品,我們寫出來的就是SHIT。我們來把它一層層展開。先展開第一層,新增省略的部分:
#include
其中_是一個變數,_^448表示_和448做異或操作,如果_等於448就返回0,在C語言中有表示式a&&b,如果a為0,就不會判斷b是否為0,一定會返回0,所以這裡可以用if語句代替,而-~_表示按位取反再取負,實際上-~_就是_+1,把_換成i,所以再展開:
int main(int i){ if(i != 448) main(i+1); putchar(——i%64?32|-~7[__TIME__-i/8%8][“>‘txiZ^(~z?”-48]>>“;;;====~$::199”[i*2&8|i/64]/(i&2?1:8)%8&1:10); return 0;}
前面就是一個遞迴操作,讓i加到448。後面再來看,putchar為輸出一個字元,裡面是我們首先遇到一個?,這個是C語言中唯一的三目運算子,所以前面一定是一個整體,我們把下面展開:
if(——i%64) putchar(32|-~7[__TIME__-i/8%8][“>’txiZ^(~z?”-48]>>“;;;====~$::199”[i*2&8|i/64]/(i&2?1:8)%8&1);else putchar(10);
__TIME__是一個預定義符號,是當前時間,格式是HH:MM:SS。
我們還發現了一個符號>>,這是個右移運算子,所以前後一定是分開的。
10在ASCII中可以看出是LF符號,表示換行,也就是輸出64個字元後就換行,而i會到448,最終會進行7次換行。再化簡:
int main(int i){ char a,b,c; if(i != 448) main(i+1); i——; if(i%64 != 0) { a = -~7[__TIME__-i/8%8][“>‘txiZ^(~z?”-48]; b = “;;;====~$::199”[i*2&8|i/64]/(i&2?1:8)%8; c = a>>b&1; putchar(32|c); } else putchar(’\n‘); return 0;}
a中注意有兩個[],按照運算子優先順序展開,7先和第一個[]裡結合
a = -~[*(__TIME__-i/8%8+7)][“>’txiZ^(~z?”-48]
-~再展開 [*(__TIME__-i/8%8+7)][“>‘txiZ^(~z?”-48]+1
*(__TIME__-i/8%8+7)是取出了值,只能作為陣列的偏移量,再次展開
a = (“>’txiZ^(~z?”-48)[*(__TIME__-i/8%8+7)]+1,再展開
a = “>‘txiZ^(~z?”[*(__TIME__-i/8%8+7) - 48]+1
將*(__TIME__-i/8%8+7)寫成陣列的形式
a = “>’txiZ^(~z?”[__TIME__[7 - i/8%8] - 48]+1
int main(int i){ char a,b,c; if(i != 448) main(i+1); i——; if(i%64 != 0) { a = “>‘txiZ^(~z?”[__TIME__[7 - i/8%8] - 48]+1; b = “;;;====~$::199”[i*2&8|i/64] / (i&2?1:8)%8; c = a>>b&1; putchar(32|c); } else putchar(’\n‘); return 0;}
__TIME__-i/8%8就是“HH:MM:SS”-i/8%8,會得到時分秒的一個偏移值。48就是字元1的ASCII碼,7 - i/8%8輸出範圍是0~7,__TIME__[7 - i/8%8]- 48輸出範圍就是數字0~10(冒號:的ASCII碼比9大1),對應陣列“>’txiZ^(~z?”的每一個值。
__TIME__[7 - i/8%8] - 48 | a 0 | 0x3F 1 | 0x2D 2 | 0x75 3 | 0x79 4 | 0x6A 5 | 0x5B 6 | 0x5F 7 | 0x29 8 | 0x7F 9 | 0x7B 10 | 0x40
b的解釋就很容易了,“;;;====~$::199”[i*2&8|i/64] / (i&2?1:8)%8就是a[n]/m%8的形式,i*2&8的值只可能是0或8,i/64的值為0~6,表示行。範圍為0~7。
而main預設傳遞的引數為1,所以i第一次執行的時候為1,後面依次遞迴,直到i為447。
int main(){ int i; char a,b,c; for(i=447;i>=0;i——) { if(i%64 != 0) { a = “>‘txiZ^(~z?”[__TIME__[7 - i/8%8] - 48]+1; b = “;;;====~$::199”[i*2&8|i/64] / (i&2?1:8)%8; c = a>>b&1; putchar(32|c); } else putchar(’\n‘); } return 0;}
具體實現讀者可以自行深入研究。