下鄉掃盲C語言 Chapter 0
最近在看windows相關的一些書,不禁感慨,之前學習過程中經歷的很多困難實質上是缺乏一些基礎知識,而且在國內互聯網論壇上少有關於這類基礎知識的討論和講解。即使有,也大多從老舊的帖子中抄襲而成,多有錯誤和過時的地方。因此我決心寫一個成系統的掃盲文章。第一個短期的計劃是關於C語言的。我假設我的大部分讀者應該是正在上,或者剛剛修過"高級語言程序設計(C語言)"這門課程的大一,大二學生。我希望先簡單解釋一些基礎概念,常見的名詞。然後會用幾篇文章講解編寫C語言程序的幾種工具,我會把重點放在微軟的Visual Studio 2017,完全跳過VC6.0。再之後會有一兩篇文章解釋常見的編譯期錯誤和運行時錯誤,講解如何查錯,如何調試一個程序。最後可能有幾篇面向希望能進一步提高自己水平的讀者的文章。大體就是這樣了。
0.什麽是C語言
C語言是一種高級程序語言,所謂高級程序語言,"In computer science,a high-level programming language is a programming language with strong abstraction from the details of the computer"一種高級語言是抽象的,不涉及計算機硬件細節的語言。這就使得高級語言有兩個特點,一是通過抽象減少了程序員需要思考的範圍,二是通過改變在不同平臺上的實現,使得編寫的程序可以不加修改地在不同的平臺上運行。
我們更關心第二個特點,這表示,同樣的一個helloworld程序,無論在linux還是windows系統,在amd或intel的CPU上運行,都應該在屏幕上打印一個"hello world"。在C語言剛出生時,這其實已經是一個驚人的進步,在那之前,程序員們使用匯編語言寫程序,然而匯編語言完全依賴於你使用的機器,也就是說,你在一臺機器上運行的程序,幾乎不可能在另外一臺使用了不同的硬件的電腦上運行。
疲於奔命的程序員說,要有高級語言,就有了C語言。
------- Genesis 1:3
1.C語言其實有好幾種
這裏我們來講一點歷史。
在20世紀70年代末,Dennis MacAlistair Ritchie與Kenneth Lane Thompson在貝爾實驗室設計,開發了C語言。然而後來的幾年裏,很多公司和個人開發者都不是直接使用最開始的這一版本,而是各自為方便使用而對C語言進行了修改。
1989年,為了避免各開發廠商用的C語言語法產生差異,由美國國家標準局為C語言訂定了一套完整的國際標準語法,稱為ANSI C(又稱 C89),作為C語言的標準。二十世紀八十年代至今的各種程序開發工具(編譯器,調試器,文本編輯器等)
C語言還有不同的版本,但比較重要的只有三個。
ANSI C (C89) 這是被最廣泛接受的一個
C99 這個版本支持了很多新的語法,很多編譯器也實現了這個標準(例如GCC),大部分情況下使用C99新增的特性是沒問題的
C11 這個版本。。。實際上還沒有編譯器完全實現,鑒於筆者壽命有限,不再提及。
2.hello world, hello assembly, hello CST
我想讀者們已經運行過一個hello world程序,大概像這樣
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
你可能已經單步調試過這個程序,在第三行打一個斷點,然後一行行執行,直到第五行,程序結束。
但是,你的程序其實並不是你看到的這麽簡單,它實際上長這樣。(源代碼的部分標為黑體)
1 #include<stdio.h>
2 int main()
3 {
4 00007FF6B7BA0590 push rbp
5 00007FF6B7BA0592 push rdi
6 00007FF6B7BA0593 sub rsp,0E8h
7 00007FF6B7BA059A lea rbp,[rsp+20h]
8 00007FF6B7BA059F mov rdi,rsp
9 00007FF6B7BA05A2 mov ecx,3Ah
10 00007FF6B7BA05A7 mov eax,0CCCCCCCCh
11 00007FF6B7BA05AC rep stos dword ptr [rdi]
12 printf("hello world\n");
13 00007FF6B7BA05AE lea rcx,[string "hello world\n" (07FF6B7BB03E8h)]
14 00007FF6B7BA05B5 call printf (07FF6B7B91528h)
15 return 0;
16 00007FF6B7BA05BA xor eax,eax
17 }
18 00007FF6B7BA05BC lea rsp,[rbp+0C8h]
19 00007FF6B7BA05C3 pop rdi
20 00007FF6B7BA05C4 pop rbp
21 00007FF6B7BA05C5 ret
怎麽樣,是不是仿佛看到了克蘇魯一樣,san值狂降?
這其實是helloworld在VS2017中,經編譯產生的匯編代碼。
這段代碼做了三件事,
一,保存程序上下文(context)
二,調用printf函數(14行)
三,恢復程序上下文(contxet)
有些比較聰明的讀者可能已經立刻明白過來,舉手回答說,代碼的編譯就是把代碼轉換成匯編代碼,匯編代碼可以輕易地轉換成機器代碼,直接運行。這種想法已經相當接近實際情況,但如果只是單純翻譯源代碼,4-10行和18-21行的程序又是從哪兒來的呢?這段代碼其實是編譯器悄悄塞進來的,沒有這些額外的工作,程序是無法正確運行的,涉及到的知識點太多,本文中不再深入了。但在接下來的第一章,我將解釋編譯器對你的代碼做了什麽,ide和編譯器有何不同。
但更重要的是,這個printf函數是打哪來的呢?我們都知道在C語言裏,函數要先聲明,並在某處定義出來,才能調用。
輸入輸出的printf,scanf一族(還有sscanf,sprintf.....)被定義在stdio.h(standard input/output)中,
關註數學運算的pow(),sqrt(),fabs()被定義在math.h中
關於時間的time()等函數,tm等結構體被定義在time.h中
這些頭文件屬於C語言標準,包含了具體實現的文件被稱作C語言標準庫(C Standard Library)
在之後的第二章,將註重於說明,lib,dll,exe等文件的關系,說明C語言標準庫在我們的程序中起什麽作用。
這只是一篇開坑的說明,所以先到此為止了。
親愛的讀者朋友們,明年再見,啊不,下周再見。
下鄉掃盲C語言 Chapter 0