- 零基礎學C語言(第4版)
- 康莉 李寬
- 1982字
- 2020-06-17 18:23:11
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。
- Learn TypeScript 3 by Building Web Applications
- 構建移動網站與APP:HTML 5移動開發入門與實戰(跨平臺移動開發叢書)
- INSTANT FreeMarker Starter
- Apache Karaf Cookbook
- Learning Python Design Patterns(Second Edition)
- 自制編程語言
- C專家編程
- 零基礎學Scratch 3.0編程
- Apache Solr PHP Integration
- WordPress Search Engine Optimization(Second Edition)
- Oracle Database XE 11gR2 Jump Start Guide
- Spring Data JPA從入門到精通
- 分布式系統架構與開發:技術原理與面試題解析
- Mastering Unity 2017 Game Development with C#(Second Edition)
- Python程序設計現代方法