官术网_书友最值得收藏!

3.5 #include預處理器指示符

源代碼最終是需要被編譯器處理的。編譯器編譯的過程比較復雜,但一般需要經歷好幾步,第一步是預處理。所謂預處理,就是在編譯前先進行一些預先處理,如代替源代碼中需要代替的部分。#include就是這么一個預處理指示指令。

為了弄清楚#include的作用,現在請讀者思考一個問題:編譯器如何知道有printf這個函數?

3.5.1 函數聲明及其作用

上節中留給讀者的試驗,修改printf為其他單詞,如print_format。在編譯的時候,編譯器會返回以下錯誤:

Warning h \cbook \src\2\2 2-helloword.c:5  missing prototype for print_format
Error :\cbook \src\2\2.2helloworldc 5 undefined reference to _print_format
編譯和連接          耗時   :  3.3秒  返回代碼        : 1

“Warning h\cbook\src\2\2 2-helloworl.c:5 missing prototype for print_format”這句話表明,缺了print_format的函數原型。這僅僅是一個警告。“Error:\cbook\src\2\2.2helloworldc 5 undefined reference to_print_format”這句話表明,出現一個錯誤,調用了一個沒有定義的函數print_format。

簡單解釋一下函數原型(prototype)的概念。回顧上節提到過的函數定義,函數定義由4部分組成:返回類型、函數名、參數表、函數體。前面的3部分合起來稱為函數原型。如下:

返回類型 函數名(參數表)

函數在被調用之前,一定要讓編譯器知道函數原型,這樣編譯器才知道有哪些函數名,該函數需要些什么樣類型的參數,返回什么樣類型的值。告訴編譯器函數原型的動作稱為函數聲明。如下:

返回類型 函數名(參數表);

注意 函數聲明是一條語句,要用分號表示結束。

函數聲明和函數定義中的返回值類型、參數表、函數名都要一致。雖然C語言提供了很多庫函數,但是對于編譯器來說還是不確定庫函數的位置。所以即使使用的是C語言系統的庫函數,也必須向編譯器聲明。

因為在本實驗中print_format函數并沒有向編譯器聲明過其函數原型,編譯器就提出抗議——一條警告。這條警告只是提醒程序員而已,如果程序員忘記了向編譯器聲明函數原型,編譯器會自己生成一個默認的函數聲明。然而代碼中實際上調用了一個根本不存在也就是沒有定義的函數,編譯器當然就要罷工了——一條錯誤提示。

3.5.2 試驗尋找#include的作用

代碼3-1中,函數printf的聲明在哪里呢?請讀者再做一個試驗:將代碼3-1中的第一行代碼刪除掉。就是去掉“#include <stdio.h>”,再編譯看出現什么現象。整個文件代碼如下:

01      void main(void)                                                         /*主函數,入口點*/
02      {                                                                                       /*函數開始*/
03              printf("\nHello World!");                                       /*打印字符串*/
04              getchar();                                                              /*等待用戶敲入回車*/
05      }

【代碼解析】是不是編譯器又提示缺少函數原型了呢?

Warning d:\cbook\src\2\2.2-helloworld.c: 3  missing prototype for printf
Warning d:\cbook\src\2\2.2-helloworld.c: 4  missing prototype for getchar
編譯和連接 耗時:0.3秒 返回代碼:0

可以推測printf和getchar兩個函數的聲明一定在stdio.h文件里。

沒錯,在CodeBlocks的安裝目錄下,有一個include文件夾。讀者可以在Windows的文件瀏覽器中定位到CodeBlocks的安裝文件夾中,去看看include下有些什么文件。是不是在include文件夾下可以搜索到stdio.h文件?用記事本或者任意一個文本編輯器打開該文件,截取該文件中的一部分如下:

int getchar(void);
char * gets(char *);
int _getw(FILE *);
int _pclose(FILE *);
#define pclose _pclose
FILE * popen(const char *, const char *);
#define _popen popen
int printf(const char *, ...);
int dprintf(const char *, ...);
int putc(int, FILE *);

看見了“int getchar(void);”和“int printf(const char *,...);”兩行嗎?它們就是這兩個函數的聲明。

請讀者再做一個試驗:修改代碼3-1如代碼3-2所示。

代碼3-2 去掉#include語句自行添加函數聲明DeclareSelf

<-----------------------------文件名:DeclareSelf.c ---------------------------->
01      int getchar(void);
02      int printf(const char *, ...);
03
04      void main(void)                                                         /*主函數,入口點*/
05      {                                                                                       /*函數開始*/
06              printf("\nHello World!");                                       /*打印字符串*/
07              getchar();                                                              /*等待用戶按回車鍵*/
08      }                                                                                       /*函數結束*/

【代碼解析】此時編譯,順利通過。還記得初中時學過的等價交換嗎?#include和什么等價?

3.5.3 #include的作用

本節來解釋#include這行代碼的作用。

#include是C語言預處理器指示符。#和include之間可以有多個空格。#也不一定要頂格,但是一定是第一個非空白字符。#include的作用是告訴編譯器,在編譯前要做些預先處理:將后面< >中的文件內容包含到當前文件內。所謂包含,是指將< >中列出的文件的內容復制到當前文件里。

注意 #一定要是第一個非空白字符,否則編譯器會提示錯誤,并且錯誤信息和出錯原因完全不匹配。

因為getchar和printf兩個函數的聲明位于stdio.h文件中,所以用#include把stdio.h文件包含進來,自然就把getchar和printf兩個函數的聲明包含進來了。

說明 函數聲明只是向編譯器登記有這么一個函數,聲明了函數而不調用這個函數是被容許的。這就是為什么包含了整個stdio.h文件(里面聲明了很多其他函數),但實際沒有使用這些函數而編譯器又不提示的原因。

讀者可能要問,stdio.h文件是個什么文件呢?std是標準(standard)的縮寫,io是Input/Output的縮寫,聯合起來就是“標準輸入輸出”的意思,一般就是與屏幕輸出和鍵盤輸入相關的內容。“.h”是C語言頭文件擴展名。所謂頭文件,就是該文件都是些函數的聲明、變量的聲明等內容,“.c”文件是C語言實現文件,是真正做事情的文件。

為了使讀者對“包含”的意思有個更明確的概念,再做一個試驗:

修改代碼3-1為代碼3-3,主要的修改就是把main函數中的“printf("\nHello Wolrd")”:刪除,但是把它移到文件string.txt中。

代碼3-3 #include的試驗AboutInclude

<----------------------------文件名:AboutInclude.c--------------------------->
01      #include <stdio.h>                                                /*包含該頭文件的目的是使用了函數printf*/
02                                                                                      /*空行,主要是為了分隔,編譯器忽略*/
03      void main(void)                                                 /*主函數,入口點*/
04      {                                                                               /*函數開始*/
05              #include "string.txt"                                   /*將.txt文件包含到程序中來*/
06              getchar();                                                      /*等待用戶按回車鍵*/
07      }                                                                               /*函數結束*/

【代碼解析】第5行代碼將一個.txt文件包含到程序中來。讀者大概可以想到,里面包含了一些函數。在AboutInclude.c同一個文件夾下面,新建一個.txt文件:string.txt。文件內容如下:

printf("\nHello World");

編譯代碼3-3,代碼順利通過,運行效果同代碼3-1。

主站蜘蛛池模板: 蛟河市| 兴宁市| 鹿泉市| 达孜县| 高碑店市| 延津县| 色达县| 澜沧| 昭苏县| 双城市| 蕉岭县| 夏津县| 集安市| 句容市| 会泽县| 巴林左旗| 石狮市| 涡阳县| 长兴县| 丁青县| 潮安县| 衡阳市| 开化县| 孙吴县| 永川市| 都昌县| 新河县| 陕西省| 象山县| 新宁县| 永清县| 通许县| 昭通市| 兰坪| 白沙| 滕州市| 永川市| 竹北市| 皮山县| 余干县| 汉源县|