第一步,寫個簡單的框架
#include<stdio.h>
int main(){
printf("#include<stdio.h>\nint main(){\n\tprintf();\n\treturn 0;\n}");
return 0;
}
printf的東西先留空。
這時你會發現printf裡面的東西需要是這句東西本身,如果把這句話複製進去,你會發現最內層還存在一個空的括號,反反覆覆下去就無底洞了。所以需要換個思路:
用變數。
第二步,考慮把printf的東西扔到變數裡,然後使用格式符輸出。
#include<stdio.h>
int main(){
char *s="#include<stdio.h>\nint main(){\n\tprintf(s);\n\treturn 0;\n}";
printf(s);
return 0;
}
有人要問了,你這不還沒在字串s裡面定義如何寫char嘛,如果在字串裡面把s寫出來,那不又成剛才那樣的無底洞了?
且慢,由於s此時是printf的第一個引數,我們完全可以利用好printf的功能——%s嘛。有了這招,我們就不用那樣寫無底洞了,用%s引數把s放進去就行了。
#include<stdio.h>
int main(){
char *s="#include<stdio.h>\nint main(){\n\tchar *s=%s;\n\tprintf(s,s);\n\treturn 0;\n}";
printf(s,s);
return 0;
}
這裡有個小技巧,printf(s,s),第一個s格式描述符把程式框架輸出,%s的位置交給第二個s把字串s本身再輸出一遍。
且慢...輸出的結果貌似不太對?
答案是由於跳脫字元的緣故。其中我們以明文形式寫的\n、\t等跳脫字元,在輸出的時候會變成換行,製表符。我們希望它輸出程式框架的時候是正常輸出製表和換行的,而在輸出s內容的時候直接保留原文...
第三步,繼續利用printf的引數
如果用%c,然後用字元的ASCII碼代替跳脫字元,就沒這問題了。
逐一把跳脫字元寫成ASCII碼,堆在printf的後面,寫完後把字串s內容中的printf引數也這樣換一遍,就大功告成了。
printf的引數會變得很多,不過能正常執行就行。
#include<stdio.h>
int main(){
char *s="#include<stdio.h>%cint main(){%c%cchar *s=%c%s%c;%c%cprintf(s,10,10,9,s,10,9,10,9,10);%c%creturn 0;%c}";
printf(s,10,10,9,34,s,34,10,9,10,9,10);
return 0;
}
成功執行,撒花!