日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C语言程序设计 | 程序环境和预处理:翻译环境和执行环境、宏、条件编译

發布時間:2024/4/11 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言程序设计 | 程序环境和预处理:翻译环境和执行环境、宏、条件编译 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

程序環境和預處理:

  • 翻譯環境和執行環境
  • 條件編譯

翻譯環境和執行環境

在ANSI C的任何一種實現中,存在兩種不同的環境。
第一種是翻譯環境,在這個環境中源代碼被轉換成可執行的機器指令。第二種是執行環境,它用于實際執行代碼。

翻譯環境

我們寫的代碼是如何從一開始的.c文件變化為可執行的.exe文件,就是在這一步完成的。

  • 組成一個程序的每個源文件通過編譯過程分別轉換成目標代碼
  • 每個目標文件由鏈接器捆綁在一起,形成一個單一而完整的可執行程序
  • 鏈接器同時也會引入標準C函數庫中任何被該程序所用到的函數,而且還可以搜索本地的程序庫,找到需要的函數鏈接進程序中

下面就來詳細講一下編譯的具體過程

編譯分為三個階段,預處理,編譯,匯編。

1.預處理

步驟:

  • 展開頭文件
  • 宏替換
  • 去掉注釋
  • 條件編譯

經過上述步驟后將.c文件處理成為.i文件

2.編譯

步驟:

  • 語法檢查
  • 將代碼轉換為匯編代碼

這時.i文件會被處理成.s文件

3.匯編

步驟:

  • 將匯編代碼轉換成為機器所能識別的二進制

這一步后,s文件被處理成為.o文件。

編譯器的工作完成以后,就到了鏈接器的工作

鏈接

步驟:

  • 將聲明卻又未定義的函數在其他文件中找到

這時候,多個.o文件將被處理成為可執行的exe文件。


運行環境

程序執行的過程:

  • 程序必須載入內存中。在有操作系統的環境中:一般這個由操作系統完成。在獨立的環境中,程序的載入必須由手工安排,也可能使通過可執行代碼置入只讀內存來完成。
  • 程序的執行便開始。接著調用main函數
  • 開始執行程序代碼。這個時候程序將使用一個運行時堆棧,存儲函數的局部變量和返回地址。程序同時也可以使用靜態內存,存儲于靜態內存中的變量在程序的整個執行過程中一直保留它們的值。
  • 終止程序。正常終于main函數;也有可能是意外終止。
  • #define 定義宏

    #define允許我們將一個文本替換另一個文本,也允許我們將參數替換到文本當中,這種實現通常稱之為宏

    #define 替換 被替換

    例如:

    #define type int #define num 100 int main() {int a = 100;type b = num;printf("%d %d\n", a, b); }


    它們運行后是一樣的,宏的作用就是將一個參數或者標識符在預處理階段就可以替換成我們想要的名字。

    宏也可以當作函數使用
    聲明方法:

    #define name( parament-list ) stuff

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

    如果是對宏不太了解的人,可能會遇到下面這種情況

    #define SQUARE( x ) x * x int main() {int a = 5;printf("%d\n", SQUARE(5));printf("%d\n", SQUARE(5 + 1)); }


    第一個答案是我們意料中的,但是第二個答案卻讓人感到奇怪,明明我們的參數是5 + 1,得到的應該是6的平方36,為什么會是這個奇怪的值呢?

    之前我提到過,宏是單純的替換,而非計算后再替換,所以替換后的公式如下


    所以如果我們要使用宏函數,就必須要考慮到優先級問題,所以要在定義宏時加上括號

    #define SQUARE( x ) ( x ) * ( x )

    #define ADD(x) (x) + (x) int main() {int a = 5;printf("%d\n", 10 * ADD(a)); }


    對于這個新的宏,我們按照之前的做法加上了括號,但是得到的答案還是錯的,我們再次將宏替換進代碼中

    這個問題還是優先級的問題,所以我們需要再在外面加上一層括號

    #define ADD(x) ((x) + (x))


    總結:所以用于對數值表達式進行求值的宏定義都應該用這種方法加上括號,避免在使用宏時由于參數中的操作符或臨近操作符之間不可預料的相互作用

    #define的替換規則

    在程序中拓展#define定義符號和宏時,需要涉及幾個步驟

  • 在調用宏時,首先對參數進行檢查,看看是否包含任何由#define定義的符號。如果是,它們首先被替換。
  • 替換文本隨后被插入到程序中與原來文本的位置。對于宏,參數名被它們的值替換
  • 最后再次對結果文件進行掃描,看看是否包含任何由#define定義的符號。如果是,就重復上述處理過程。
  • 注意:

    • 宏參數和#define定義中可以出現其他#define定義的變量。但是對于宏,不能出現遞歸。
    • 當預處理器搜索#define定義的符號的時候,字符串常量的內容并不被搜索

    宏與函數的對比

    優點:

  • 程序規模和速度更勝一籌
  • 宏的參數與類型無關
  • 缺點:

  • 宏替換時會重復將代碼插入程序,造成代碼冗余。
  • 宏無法進行調試
  • 宏與類型無關,所以不夠嚴謹
  • 宏如果使用不當容易出現運算符優先級問題
  • 條件編譯

    條件編譯,顧名思義,就是滿足條件才編譯某些語句,不滿足則不編譯。

    在我們編寫代碼的時候,可能存在一種情況,這段語句在某個環境下是需要的,在另一個環境下卻又不需要,這個時候我們如果要在它不用的時候將它刪除,就會十分麻煩,而使用條件編譯就可以解決這個方法。

    例如當我們一個項目中多次引用一個頭文件,造成在最終程序中出現了多份這個頭文件的內容,導致了文件內容重復的情況,這個時候我們就需要這個條件編譯

    #ifndef __TEST_H__ #define __TEST_H__ #endif

    這就是一種條件編譯

    常見的條件編譯如下:
    1.

    #if (常量表達式)#endif
  • 多個分支的條件編譯

    #if (常量表達式) #elif (常量表達式) #else #endif
  • 判斷是否被定義

    #if defined(x) #ifdef x#if !defined(x) #ifndef x
  • 嵌套指令

    #if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif#endif

    總結

    以上是生活随笔為你收集整理的C语言程序设计 | 程序环境和预处理:翻译环境和执行环境、宏、条件编译的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。