程式環境和預處理

sum_er發表於2020-12-25

一、程式環境
我們知道計算機是沒有辦法認識除了二進位制之外的語言的,所以我們現在編出來的程式碼要想讓計算機認識並實現,就必須把它轉換成二進位制語言。我們把這個過程叫做程式的翻譯過程。
程式的翻譯過程又分為四個階段:
1、預處理:
在這個階段,它做了四個工作:巨集替換、去註釋、標頭檔案展開和條件編譯。
2、編譯:
在這個階段,它只做了一件事情,那就是將c語言編譯成為組合語言。
3、彙編:
在這個階段,它也只是做了一件事情,把組合語言變為目標二進位制。
4、連結:
最後一步,將目標二進位制檔案和庫函式連結形成可執行程式。

那麼,就會有人問了,為什麼不直接將c語言轉換成二進位制呢?
這就要從c語言的發展來說起了。起初,是沒有程式語言的,有的只是紙段,紙段上打眼來編寫程式,放入計算機中來計算,這種方法往往效率低並且計算複雜。而後,有了組合語言,人們拜託了紙段的束縛。但是,人們嫌組合語言還是不夠精簡,也就是使用複雜,所以才出現了c語言。這就解釋了為什麼從c語言到二進位制語言翻譯的時候,先翻譯成了組合語言,再翻譯成二進位制了。因為歷史是這麼發展的,二進位制->組合語言->c語言,所以從c語言到二進位制也是應該從後到前一步一步來。
二、預處理詳解
1、預定義符號

__FILE__  //進行編譯的原始檔
__LINE__  //檔案當前的行號
__DATE__  //檔案被編譯的日期
__TIME__  //檔案被編譯的時間
__STDC__  //如果編譯器遵循ANSI C,其值為1,否則未定義

這些預定義符號都是語言內建的。舉個例子:

printf("file:%s line:%d\n",__FILE__,__LINE__);

2、#define
(1)#define定義識別符號

#define name stuff //把name替換為stuff

一個小問題:define定義識別符號的時候,要不要在後面加上“;”?
比如:#define MAX 1000;,不建議加“;”,因為會報錯,c語言會單純地把1000;都賦給MAX。
(2)#define定義巨集

#define name(parament-list) stuff //其中的parament-list是一個由符號隔開的符號表,它們可能出現在stuff中。

**注意:**引數列表的左括號必須與name緊鄰。如果兩者之間有任何空白存在,引數列表就會被解釋為stuff的一部分。
如:

#define SQUARE(x) x*x

這個巨集接收一個引數x。如果在上述宣告之後,你把

SQUARE(5)

置於程式中,前處理器就會用下面這個表示式替換上面的表示式:

5*5

那如果我們輸入:

SQUARE(5+1)

結果會是多少呢?會是36嗎?
答案是11。為什麼呢?因為(5+1)當成了x,就成為了5+1*5+1,按照運算規則,結果就是11。
所以我們要想讓它輸出36,就必須加上小括號。

SQUARE(x) (x)*(x)

提示:當我們需要用巨集計算時,須在能加上小括號的地方都加上小括號。
(3)#和##
a.#的作用:把一個巨集引數程式設計對應的字串。
b.##的作用:可以把位於它兩邊的符號合成一個符號。它允許巨集定義從分離的文字片段建立識別符號。
(4)巨集和函式的對比
巨集通常用來執行簡單的運算。比如在兩個數中找出較大的一個:

#define MAX(a,b) ((a)>(b))?(a):(b))

巨集相對於函式的優點-------巨集計算的速度快,並且巨集還可以傳型別。
巨集相對於函式的缺點-------巨集會增加程式的長度。巨集沒有辦法除錯。巨集由於與型別無關,也就不夠嚴謹。巨集由於優先順序的問題,導致程式容易出錯。

提示:我們優先用函式。
(5)條件編譯
條件編譯語句的特徵就是語句前面有一個“#”。
常用的:

#ifndef __TEST_H__
#define __TEST_H__
//標頭檔案的內容
#endif //__TEST_H__

這個是用來避免標頭檔案的重複引入。
(6)標頭檔案的包含
標頭檔案可以用雙引號,也可以用尖括號。
如:

#include "stdio.h"//本地檔案包含

查詢策略:先在原始檔所在目錄下查詢,如果該標頭檔案未找到,編譯器就像查詢庫函式標頭檔案一樣在標準位置查詢標頭檔案。如果找不到就提示編譯錯誤。效率低。

#include<stdio.h>//庫檔案包含

查詢標頭檔案直接去標準路徑下去查詢,如果找不到就提示編譯錯誤。

相關文章