C語言中奇怪的語法

我們知道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語言中奇怪的語法

這段程式碼用到了C語言中很多的基礎知識,但是讀者不要嘗試這樣寫,大神寫出來的是作品,我們寫出來的就是SHIT。我們來把它一層層展開。先展開第一層,新增省略的部分:

#include #include int main(int _){ _^448&&main(-~_); putchar(——_%64?32|-~7[__TIME__-_/8%8][“>’txiZ^(~z?”-48]>>“;;;====~$::199”[_*2&8|_/64]/(_&2?1:8)%8&1:10); return 0;}

其中_是一個變數,_^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;}

具體實現讀者可以自行深入研究。