C語言 - 最初的起點

從Hello World說起

本文為那些年我們追過的語言之C語言篇。第一個C語言程序是Hello World, 創(chuàng)作者Brian W. Kernighan, The C Programming Language 的作者之一。同時, 這是本文推薦的第一本關(guān)于C語言的書籍, 它幾乎涵蓋C中所有的基礎(chǔ)語法和注意事項. 現(xiàn)在, 請隨我一起重溫這段經(jīng)典代碼.
#include <stdio.h>

int main(int argc, char* argv[]){
  printf("Hello World\n");
  return 0;
}

對于每個C程序, 系統(tǒng)運行的入口就是main. 那么, 從系統(tǒng)接口傳入的參數(shù)自然要傳至其參數(shù)int argc, char* argv[]中. 其中, argc表示參數(shù)個數(shù), argv以字符指針數(shù)組的形式保存各個參數(shù). 為驗證真實傳參情況, 編寫如下程序test.c以輸出argcargv:
#include <stdio.h>

int main(int argc, char* argv[]){
  int i;
  printf("%d argument(s):", argc);
  for(i=0; i<argc; ++i) printf(" %s", argv[i]);
  return 0;
}

接著, 我們編譯test.c為可執(zhí)行文件test, 并帶參運行它:

yogy@Kali:~/PL$ gcc -o test test.c
yogy@Kali:~/PL$ ./test foo bar baz qux
5 argument(s): ./test foo bar baz qux

結(jié)果表明, 系統(tǒng)將收到的5個字符串都作為參數(shù)傳入了main函數(shù), 包括./test.

面向過程的C

有人說, C語言之于編程語言, 好似內(nèi)功心法之于武學(xué). 在此, 我們只談對個人的影響, 不論江湖地位. C語言作為國內(nèi)大學(xué)普遍入門語言 (國外多為Python和Java), 也是我學(xué)習(xí)的第一門編程語言. 當(dāng)年使用的書是 Programming in C, 對于新人還是著力推薦它, 通俗易懂, 性價比高. 作為一門面向過程的語言, C對我這四年的編程思路影響太深, 以至于無論使用C++還是Python, 普遍遵循了C的風(fēng)格, 失去了面向?qū)ο蟮奶匦? 當(dāng)然, 這其中包含一直學(xué)習(xí)算法的關(guān)系. 我始終認(rèn)為, 算法是一種是面向過程的邏輯思維方式, 使用封裝一定程度上犧牲了它的效率. 總之, 想入門C, 踏實看書, 學(xué)會像程序一樣思考, 思路比語法重要. 另, C進(jìn)階書籍推薦:

后兩本只是為了讓你不會感到無趣, 同時一定程度滿足IT面試的需求, 并非系統(tǒng)算法學(xué)習(xí).

黑魔法

1. Hello World

#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> 
_____

上面這段Hello World代碼, 使用預(yù)處理#define的迭代操作, 結(jié)合了十六進(jìn)制的ASICC碼.
#include <stdio.h>
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))),(((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)))-((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>>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))-((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))-((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)<<(1<<1))+(1<<(1>>1))),(((1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1))) + (1<<(1>>1)))};
for(i=(1>>1);i<=((1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)));++i) printf("%c",n[i]);
}
移位操作<<>>是很棒的操作, 相比于乘除2的冪, 效率高很多. 這段Hello World, 湊各種2次冪也是微醉, 作者不詳. 重要提醒: 移位操作符優(yōu)先級低于+-, 請時刻牢記是否需要括號.

2. ++--

先看stackoverflow上的一個討論What is the name of the “-->” operator?.
#include <stdio.h>
int main() {
int x = 10;
while (x --> 0) {
printf("%d ", x);
}
}
上述代碼輸出結(jié)果為9 8 7 6 5 4 3 2 1 0, 提問者對第4行中的-->表示困惑, 是否它應(yīng)理解為”趨向于”. 問題本身很好理解, x --> 0應(yīng)該看成(x--) > 0, 即先與0比較再自減.

自加++和自減--最經(jīng)典的討論就是其位置置于變量的前或后. 接下來, 讓我們從匯編角度做出分析. 原分析作者不詳, 如有問題請聯(lián)系我.

i = i2++的反匯編代碼分析:

  1. 將dword ptr [i2](即i2中的內(nèi)存單元)中的數(shù)據(jù)拷貝到eax寄存器中
  2. 將eax寄存器中的數(shù)據(jù)(即i2)拷貝到dword ptr [i](即i的內(nèi)存單元)中
  3. 將dword ptr [i2](即i2中的內(nèi)存單元)中的數(shù)據(jù)拷貝到ecx寄存器中
  4. 將ecx寄存器中的內(nèi)容自增1
  5. 將ecx寄存器中的內(nèi)容(ecx加1后的數(shù)據(jù))拷貝到dword ptr [i2]

i = ++i2的反匯編代碼分析:

  1. 將dword ptr [i2](即i2中的內(nèi)存單元)中的數(shù)據(jù)拷貝到eax寄存器中
  2. 將eax寄存器中的內(nèi)容自增1
  3. 將eax寄存器中的內(nèi)容(eax加1后的數(shù)據(jù))拷貝到dword ptr [i2](即i2中的內(nèi)存單元)
  4. 將dword ptr [i2](即i2中的內(nèi)存單元)中的數(shù)據(jù)拷貝到ecx寄存器中
  5. 將ecx寄存器中的內(nèi)容(ecx加1后的數(shù)據(jù))拷貝到dword ptr [i](即i所在的內(nèi)存單元)

由上述分析可知, 自加自減對內(nèi)建數(shù)據(jù)類型的情況,效率沒有區(qū)別。

另, 與自加自減功能類似, -~i表示i+1, ~-i表示i-1. 但由于它們是通過位運算辦到的, 放在變量之后沒有意義.

3. Quine

Quine以哲學(xué)家Willard van Orman Quine命名, 表示一個可以輸出他自己的完全源代碼的程序, 詳見Quine_(computing). 下面, 請欣賞彩蛋:

上述代碼使用C編譯 (gcc -ansi) 均得到y(tǒng)oumu, 而使用C++編譯 (g++) 都輸出yuyuko. 這種程序稱為Polyglot, 詳見Polyglot_(computing). 上述彩蛋便是使用特定的3字符片段來區(qū)分C和C++.

結(jié)束語

"Hello, World!" 開始, 從 World:" Hello"! 走向另一個開始.

C
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容