首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

六種變態 Hello World(C 語言版):極致混淆與最小化的藝術

     編輯:關於C語言

前言

以下六個程式片段皆能在 GCC 編譯通過(最後一個需 g++),完成相同任務:輸出 Hello, World,同時將原始碼混淆至極致。展示 C 語言前處理器、運算子、控制流的驚人表達力。

hello1.c:巨集遞迴展開

#define _________ }
#define ________ putchar
#define _______ main
#define _(a) ________(a);
#define ______ _______(){
#define __ ______ _(0x48)_(0x65)_(0x6C)_(0x6C)
#define ___ _(0x6F)_(0x2C)_(0x20)_(0x77)_(0x6F)
#define ____ _(0x72)_(0x6C)_(0x64)_(0x21)
#define _____ __ ___ ____ _________
#include<stdio.h>
_____

原理:層層巨集定義,最終 _____ 展開為完整 main() { putchar(0x48); putchar(0x65); ... }。0x48='H', 0x65='e'...

hello2.c:陣列指標算術與 ASCII 運算

#include<stdio.h>
main(){
    int x=0, y[14], *z=&y;
    *(z++)=0x48; *(z++)=y[x++]+0x1D;
    *(z++)=y[x++]+0x07; *(z++)=y[x++]+0x00; *(z++)=y[x++]+0x03;
    *(z++)=y[x++]-0x43; *(z++)=y[x++]-0x0C; *(z++)=y[x++]+0x57;
    *(z++)=y[x++]-0x08; *(z++)=y[x++]+0x03; *(z++)=y[x++]-0x06;
    *(z++)=y[x++]-0x08; *(z++)=y[x++]-0x43; *(z++)=y[x]-0x21;
    x=*(--z); while(y[x]!=NULL) putchar(y[x++]);
}

原理:利用 y 陣列初始值為 0,透過相加/相減 ASCII 差值構建字元碼存入 y,最後反向遍歷輸出。

hello3.c:goto 狀態機

#include<stdio.h>
#define __(a) goto a;
#define ___(a) putchar(a);
#define _(a,b) ___(a) __(b);
main() {
 _:__(t) a:_('r',g) b:_('$',p)
 c:_('l',f) d:_(' ',s) e:_('a',s)
 f:_('o',q) g:_('l',h) h:_('d',n)
 i:_('e',w) j:_('e',x) k:_('\n',z)
 l:_('H',l) m:_('X',i) n:_('!',k)
 o:_('z',q) p:_('q',b) q:_(',',d)
 r:_('i',l) s:_('w',v) t:_('H',j)
 u:_('a',a) v:_('o',a) w:_(')',k)
 x:_('l',c) y:_('\t',g) z:___(0x0)
}

原理:每個標籤輸出一字元並 goto 下一標籤,構成有限狀態機。z 輸出 \0 結束。

hello4.c:遞迴 main + 陣列指標

int n[]={0x48,0x65,0x6C,0x6C,0x6F,0x2C,0x20,0x77,0x6F,0x72,0x6C,0x64,0x21,0x0A,0x00},*m=n;
main(n){putchar(*m)!='\0'?main(m++):exit(n++);}

原理:main 遞迴呼叫自身,參數 n 實為指標 m 遞增,putchar 輸出當前字元直到 \0。

hello5.c:純位運算構建字元碼

main(){
    int i, n[]={(((1<<1)<<(1<<1)<<(1<<1)<<(1<<(1>>1)))+((1<<1)<<(1<<1))),
                 (((1<<1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)))+(1<<(1>>1))),
                 ...  // 共 14 個元素,全由 1、位移、加減構成
                };
    for(i=0; i<14; ++i) printf("%c", n[i]);
}

原理:完全不使用數字字面常數,僅用 1、位移、加減運算計算出每個字元的 ASCII 碼。

hello6.cpp:C++ 專屬,前後置 ++/-- 混淆

#include <stdio.h>
#define _(_) putchar(_);
int main(void){
    int i = 0;
    _(++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++i)
    _(++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++i)
    _(++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++i)
    _(--++i)_(++++++i)_(------------------------------------------------i)
    _(----------------i)_(++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++i)
    _(----------------i)_(++++++i)_(------------i)_(--------i)_(-------------------------------i)
    _(----------------------i)_(-------------------------------i) return i;
}

原理:利用大量 ++/-- 對 i 累加/遞減至目標 ASCII 碼,putchar 輸出。需 C++ 編譯器支援連續 ++(C 標準不允許對同一變數連續副作用)。

鑑賞與警示

  • 這些程式碼展示了 C/C++ 語法的極限彈性,屬於「國際混淆 C 語言大賽」(IOCCC)風格作品。
  • 切勿在生產碼中模仿:可讀性、可維護性歸零,純屬智力遊戲與語言特性壓力測試。
  • 理解其原理有助於深入掌握前處理器、運算子優先序、遞迴、指標算術、ASCII 編碼等核心概念。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved