操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    我們會聲明我們的函數,需要用到一個專門的技術: 函數原型 ,英語是 。 表示“函數”, 表示“原型,樣本,模范”。

    就好比你對電腦發出一個通知:“看,我的函數的原型在這里,你給我記住啦!”

    我們來看一下上一課舉的一個函數的例子(計算矩形面積):

    double rectangleArea(double length, double width) {
        return length * width;
    }
    復制代碼

    怎么來聲明我們上面這個函數的原型呢?

    ;

    很簡單吧?現在你就可以把你的函數的定義放在 main 函數后面啦,電腦也會認識它,因為你在 main 函數前面已經聲明過這個函數了。

    你的程序會變成這樣:

    #include 
    #include 
    // 下面這一行是 rectangleArea 函數的函數原型
    double rectangleArea(double length, double width);
    int main(int argc, char *argv[]) {
        printf("長為 10,寬為 5 的矩形面積 = %f\n", rectangleArea(10, 5));
        printf("長為 3.5,寬為 2.5 的矩形面積 = %f\n", rectangleArea(3.5, 2.5));
        printf("長為 9.7,寬為 4.2 的矩形面積 = %f\n", rectangleArea(9.7, 4.2));
        return 0;
    }
    // 現在我們的 rectangleArea 函數就可以放置在程序的任意位置了
    double rectangleArea(double length, double width) {
        return length * width;
    }
    復制代碼

    與原先的程序相比有什么改變呢?

    其實就是在程序的開頭加了函數的原型而已(記得不要忘了那個分號)。

    函數的原型,其實是給電腦的一個提示或指示。比如上面的程序中,函數原型

    double rectangleArea(double length, double width);
    復制代碼

    就是對電腦說:“老兄,存在一個函數,它的輸入是哪幾個參數,輸出是什么類型”,這樣就能讓電腦更好地管理。

    多虧了這一行代碼,現在你的 函數可以置于程序的任何位置了。

    記得:最好養成習慣,對于 C語言程序,總是定義了函數,再寫一下函數的原型。

    那么不寫函數原型行不行呢?

    也行。只要你把每個函數的定義都放在 main 函數之前。但是你的程序慢慢會越來越大,等你有幾十或者幾百個函數的時候,你還顧得過來么?

    所以養成好習慣,不吃虧的。

    你也許注意到了,main 函數沒有函數原型。因為不需要,main 函數是每個 C程序必須的入口函數。人家 main 函數“有權任性”,跟編譯器關系好,編譯器對 main 函數很熟悉,是經常打交道的“哥們”,所以不需要函數原型來“介紹” main 函數。

    還有一點,在寫函數原型的時候,對于圓括號里的函數參數,名字是不一定要寫的,可以只寫類型。

    因為函數原型只是給電腦做個介紹,所以電腦只需要知道輸入的參數是什么類型就夠了,不需要知道名字。所以我們以上的函數原型也可以簡寫如下:

    double rectangleArea(double, double);
    復制代碼

    看到了嗎,我們可以省略 和 width 這兩個變量名,只保留 (雙精度浮點型)這個類型名字。

    千萬不要忘了函數原型末尾的分號,因為這是編譯器區分函數原型和函數定義開頭的重要指標。如果沒有分號,編譯時會出現比較難理解的錯誤提示。

    /?_wv=1027&k=

    3. 頭文件

    頭文件在英語中是 file。 表示“數據頭,頁眉”,file 表示“文件”。

    每次看到這個術語,我都想到已經結婚的“我們的青春”: 周杰倫 的《頭文字D》。

    到目前為止,我們的程序只有一個 .c 文件(被稱為“源文件”,在英語中是 file。 表示“源,源頭,水源”),比如我們之前把這個 .c 文件命名為 main.c。當然名字是無所謂的,起名為hello.c,haha.c 都行。

    一個項目多個文件

    在實際編寫程序的時候,你的項目一般肯定不會把代碼都寫在一個 main.c 文件中。當然,也不是不可以。

    但是,試想一下,如果你把所有代碼都塞到這一個 main.c 文件中,那如果代碼量達到 10000 行甚至更多,你要在里面找一個東西就太難了。也正是因為這樣,通常我們每一個項目都會創建多個文件。

    那以上說到的項目是指什么呢?

    之前我們用 這個 IDE 創建第一個 C語言項目的時候,其實已經接觸過了。

    一個項目(英語是 ),簡單來說是指你的程序的所有源代碼(還有一些其他的文件),項目里面的文件有多種類型。

    目前我們的項目還只有一個源文件:main.c 。

    看一下你的 IDE,一般來說項目是列在左邊。

    如上圖,你可以看到,這個項目(在 一欄里)只有一個文件:main.c 。

    現在我們再來展示一個包含好多個文件的項目:

    上圖中,我們可以看到在這個項目里有好幾個文件。實際中的項目大多是這樣的。你看到那個 main.c 文件了嗎?通常來說在我們的程序中,會把 main 函數只定義在 main.c 當中。

    當然也不是非要這樣,每個人都有自己的編程風格。不過希望跟著這個課程學習的讀者,可以和我們保持一致的風格,方便理解。

    那你又要問了:“為什么創建多個文件呢?我怎么知道為項目創建幾個文件合適呢?”

    答案是:這是你的選擇。通常來說,我們把同一主題的函數放在一個文件里。

    .h 文件和 .c 文件

    在上圖中,我們可以看到有兩種類型的文件:一種是以 .h 結尾的,一種是以 .c 結尾的。

    所以,通常來說我們不常把函數原型放在 .c 文件中,而是放在 .h 文件中,除非你的程序很小。

    對每個 .c 文件,都有同名的 .h 文件。上面的項目那個圖中,你可以看到 .h 和 .c 文件一一對應。

    但我們的電腦怎么知道函數原型是在 .c 文件之外的另一種文件里呢?

    需要用到我們之前介紹過的預處理指令 # 來將其引入到 .c 文件中。

    請做好準備,下面將有一波密集的知識點“來襲”。

    怎么引入一個頭文件呢?其實你已經知道怎么做了,之前的課程我們已經寫過了。

    比如我們來看我們上面的 game.c 文件的開頭

    #include 
    #include 
    #include "game.h"
    void player(SDL_Surface* screen) {
        // ...
    }
    復制代碼

    看到了嗎,其實你早就熟悉了,要引入頭文件,只需要用 # 這個預處理指令。

    因此我們在 game.c 源文件中一共引入了三個頭文件:.h, stdio.h,game.h。

    注意到一個不同點了嗎?

    在標準庫的頭文件(.h,stdio.h)和你自己定義的頭文件(game.h)的引入方式是有點區別的:

    <>
    ""

    我們再來看一下對應的 game.h 這個頭文件的內容:

    看到了嗎,.h 文件中存放的是函數原型。

    你已經對一個項目有大致概念了。

    那你又會問了:“為什么要這樣安排呢?把函數原型放在 .h 頭文件中,在 .c 源文件中用 # 引入。為什么不把函數原型寫在 .c 文件中呢?”

    答案是:方便管理,條理清晰,不容易出錯,省心。

    因為如前所述,你的電腦在調用一個函數前必須先“知道”這個函數,我們需要函數原型來讓使用這個函數的其他函數預先知道。

    如果用了 .h 頭文件的管理方法,在每一個 .c 文件開頭只要用 # 這個指令來引入頭文件的所有內容,那么頭文件中聲明的所有函數原型都被當前 .c 文件所知道了,你就不用再操心那些函數的定義順序或者有沒有被其他函數知道

    例如我的 main.c 函數要使用 .c 文件中的函數,那我只要在 main.c 的開頭寫 # ".h" ,之后我在 main.c 函數中就可以調用 .c 中定義的函數了。

    你可能又要問了:“那我怎么在項目中加入新的 .h 和 .c 文件呢?”

    很簡單,在 里,鼠標右鍵點擊項目列表的主菜單處,選擇 Add Files,或者在菜單欄上依次單擊 File -> New -> File... ,就可以選擇添加文件的類型了。

    引入標準庫

    你腦海里肯定出現一個問題:

    如果我們用 # 來引入 stdio.h 和 .h 這樣的標準庫的頭文件,而這些文件又不是我自己寫的,那么它們肯定存在于電腦里的某個地方,我們可以找到,對吧?

    是的,完全正確!

    如果你使用的是 IDE(集成開發環境),那么它們一般就在你的 IDE 的安裝目錄里。

    如果是在純 Linux 環境下,那就要到系統文件夾里去找,這里不討論了,感興趣的讀者可以去網上搜索。

    在我的情況,因為安裝的是 這個 IDE,所以在 下,我的頭文件們“隱藏”在這兩個路徑下:

    C:\Program Files\CodeBlocks\MinGW\include
    復制代碼

    C:\Program Files\CodeBlocks\MinGW\x86_64-w64-mingw32\include
    復制代碼

    一般來說,都在一個叫做 的文件夾里。

    在里面,你會找到很多文件,都是 .h 文件,也就是 C語言系統定義的標準頭文件,也就是系統庫的頭文件(對 ,macOS,Linux 都是通用的,C語言本來就是可移植的嘛)。

    在這眾多的頭文件當中,你可以找到我們的老朋友:stdio.h 和 .h。

    你可以雙擊打開這些文件或者選擇你喜歡的文本編輯器來打開,不過也許你會嚇一跳,因為這些文件里的內容很多,而且好些是我們還沒學到的用法,比如除了 # 以外的其他的預處理指令。

    你可以看到這些頭文件中充滿了函數原型,比如你可以在 stdio.h 中找到 函數的原型。

    你要問了:“OK,現在我已經知道標準庫的頭文件在哪里了,那與之對應的標準庫的源文件(.c 文件)在哪里呢?”

    不好意思,你見不到它們啦。因為 .c 文件已經被事先編譯好,轉換成計算機能理解的二進制碼了。

    “伊人已去,年華不復,吾將何去何從?”

    既然見不到原先的它們了,至少讓我見一下“美圖秀秀”之后的它們吧…

    可以,你在一個叫 lib 的文件夾下面就可以找到,在我的 下的路經為:

    C:\Program Files\CodeBlocks\MinGW\lib
    復制代碼

    C:\Program Files\CodeBlocks\MinGW\x86_64-w64-mingw32\lib
    復制代碼

    被編譯成二進制碼的 .c 文件,有了一個新的后綴名:.a(在 的情況,它的編譯器是 MinGW。MinGW 簡單來說就是 GCC 編譯器的 版本)或者 .lib(在 C++ 的情況),等。這是靜態鏈接庫的情況。

    你在 中還能找到 .dll 結尾的動態鏈接庫;你在 Linux 中能找到 .so 結尾的動態鏈接庫。暫時我們不深究靜態鏈接庫和動態鏈接庫,有興趣的讀者可以去網上自行搜索。

    這些被編譯之后的文件被叫做 庫文件 或 文件( 表示“庫,圖書館,文庫”),不要試著去閱讀這些文件的內容,因為是看不懂的亂碼。

    學到這里可能有點暈,不過繼續看下去就會漸漸明朗起來,下面的內容會有示意圖幫助理解。

    小結一下: 在我們的 .c 源文件中,我們可以用 # 這個預處理指令來引入標準庫的 .h 頭文件或自己定義的頭文件。這樣我們就能使用標準庫所定義的 這樣的函數,電腦就認識了這些函數(借著 .h 文件中的函數原型),就可以檢驗你調用這些函數時有沒有用對,比如函數的參數個數,返回值類型,等。

    4. 分開編譯

    現在我們知道了一個項目是由若干文件組成的,那我們就可以來了解一下編譯器()的工作原理。

    之前的課里面展示的編譯示例圖是比較簡化的,下圖是一幅編譯原理的略微詳細的圖,希望大家用心理解并記住:

    上圖將編譯時所發生的事情基本詳細展示了,我們來仔細分析:

    預處理指令有好多種,目前我們學過的只有 # ,它使我們可以在一個文件中引入另一個文件的內容。# 這個預處理指令也是最常用的。

    預處理器會把 # 所在的那一句話替換為它所引入的頭文件的內容,比如

    #include 
    復制代碼

    預處理器在執行時會把上面這句指令替換為 stdio.h 文件的內容。所以到了編譯的時候,你的 .c 文件的內容會變多,包含了所有引入的頭文件的內容,顯得比較臃腫。

    編譯器會把 .c 文件先轉換成 .o 文件(有的編譯器會生成 .obj 文件),.o 文件一般叫做 目標文件 (o 是 的首字母,表示“目標”),是臨時的二進制文件,會被用于之后生成最終的可執行二進制文件。

    .o 文件一般會在編譯完成后被刪除(根據你的 IDE 的設置)。從某種程度上來說 .o 文件雖然是臨時中間文件,好像沒什么大用,但保留著不刪除也是有好處:假如項目有 10 個 .c 文件,編譯后生成了 10 個 .o 文件。之后你只修改了其中的一個 .c 文件,如果重新編譯,那么編譯器不會為其他 9 個 .c 文件重新生成 .o 文件,只會重新生成你更改的那個。這樣可以節省資源。

    現在你知道從代碼到生成一個可執行程序的內部原理了吧,下面我們要展示給大家的這張圖,很重要,希望大家理解并記住。

    大部分的錯誤都會在編譯階段被顯示,但也有一些是在鏈接的時候顯示,有可能是少了 .o 文件之類。

    之前那幅圖其實還不夠完整,你可能想到了:我們用 .h 文件引入了標準庫的頭文件的內容(里面主要是函數原型)模塊化程序設計書籍,函數的具體實現的代碼我們還沒引入呢,怎么辦呢?

    對了,就是之前提到過的 .a 或 .lib 這樣的庫文件(由標準庫的 .c 源文件編譯而成)。

    所以我們的鏈接器()的活還沒完呢,它還需要負責鏈接標準庫文件,把你自己的 .c 文件編譯生成的 .o 目標文件和標準庫文件整合在一起,然后鏈接成最終的可執行文件。

    如下圖所示:

    這下我們的示意圖終于完整了。

    這樣我們才有了一個完整的可執行文件,里面有它需要的所有指令的定義,比如 的定義。

    5. 變量和函數的作用范圍

    為了結束這一課,我們還得學習最后一個知識點: 變量和函數的作用范圍(有效范圍) 。

    我們將學習變量和函數什么時候是可以被調用的。

    函數的私有變量(局部變量)

    當你在一個函數里定義了一個變量之后,這個變量會在函數結尾時從內存中被刪除。

    int multipleTwo(int number) {
        int result = 0;  // 變量 result 在內存中被創建
        result = 2 * number;
        return result;
    }  // 函數結束,變量 result 從內存中被刪除
    復制代碼

    在一個函數里定義的變量,只在函數運行期間存在。

    這意味著什么呢?意味著你不能從另一個函數中調用它。

    #include 
    int multipleTwo(int number);
    int main(int argc, char *argv[]) {
        printf("15 的兩倍是 %d\n", multipleTwo(15));
        printf("15 的兩倍是 %d", result);   // 錯誤!
        return 0;
    }
    int multipleTwo(int number) {
        int result = 0;
        result = 2 * number;
        return result;
    }
    復制代碼

    可以看到,在 main 函數中,我們試著調用 這個變量,但是因為這個變量是在 函數中定義的,在 main 函數中就不能調用,編譯會出錯。

    記住:在函數里定義的變量只能在函數內部使用,我們稱之為 局部變量 ,英語是 local 。local 表示“局部的模塊化程序設計書籍,本地的”, 表示“變量”。

    全局變量(請避免使用)

    全局變量的英語是 。 表示“全局的,總體的”。

    能被所有文件使用的全局變量

    我們可以定義能被項目的所有文件的所有函數調用的變量。我們會展示怎么做,是為了說明這方法存在,但是一般來說,要避免使用能被所有文件使用的全局變量。

    可能這樣做一開始會讓你的代碼簡單一些,但是不久你就會為之煩惱了。

    為了創建能被所有函數調用的全局變量,我們須要在函數之外定義。通常我們把這樣的變量放在程序的開頭,# 預處理指令的后面。

    #include 
    int result = 0;  // 定義全局變量 result
    void multipleTwo(int number);  // 函數原型
    int main(int argc, char *argv[]) {
        multipleTwo(15); // 調用 multipleTwo 函數,使全局變量 result 的值變為原來的兩倍
        printf("15 的兩倍是 %d\n", result);  // 我們可以調用變量 result
        return 0;
    }
    void multipleTwo(int number) {
        result = 2 * number;
    }
    復制代碼

    上面的程序中,我們的函數 不再有返回值了,而是用于將 這個全局變量的值變成 2 倍。之后 main 函數可以再使用 這個變量。

    由于這里的 變量是一個完全開放的全局變量,所以它可以被項目的所有文件調用,也就能被所有文件的任何函數調用。

    注:這種類型的變量是很不推薦使用的,因為不安全。一般用函數里的 語句來返回一個變量的值。

    只能在一個文件里被訪問的全局變量

    剛才我們學習的完全開放的全局變量可以被項目的所有文件訪問。我們也可以使一個全局變量只能被它所在的那個文件調用。

    就是說它可以被自己所在的那個文件的所有函數調用,但不能被項目的其他文件的函數調用。

    怎么做呢?

    只需要在變量前面加上 這個關鍵字。如下所示:

    static int result = 0;
    復制代碼

    表示“靜態的,靜止的”。

    函數的 (靜態)變量

    注意: 如果你在聲明一個函數內部的變量時,在前面加上 這個關鍵字,它的含義和上面我們演示的全局變量是不同的。 函數內部的變量如果加了 ,那么在函數結束后,這個變量也不會銷毀,它的值會保持。下一次我們再調用這個函數時,此變量會延用上一次的值。

    例如:

    int multipleTwo(int number) {
        static int result = 0;   // 靜態變量 result 在函數第一次被調用時創建
        result = 2 * number;
        return result;
    }   // 變量 result 在函數結束時不會被銷毀
    復制代碼

    這到底意味著什么呢?

    就是說: 這個變量的值,在下次我們調用這個函數時,會延用上一次結束調用時的值。

    有點暈是嗎?不要緊。來看一個小程序,以便加深理解:

    #include 
    int increment();
    int main(int argc, char *argv[]) {
        printf("%d\n", increment());
        printf("%d\n", increment());
        printf("%d\n", increment());
        printf("%d\n", increment());
        return 0;
    }
    int increment() {
        static int number = 0;
        number++;
        return number;
    }
    復制代碼

    上述程序中,在我們第一次調用 函數時, 變量被創建,初始值為 0,然后對其做自增操作(++ 運算符),所以 的值變為 1。

    函數結束后, 變量并沒有從內存中被刪除,而是保存著 1 這個值。

    之后,當我們第二次調用 函數時,變量 的聲明語句( int = 0; )會被跳過不執行(因為變量 還在內存里呢。你想,一個皇帝還沒駕崩,太子怎么能繼位呢?)。

    我們繼續使用上一次創建的 變量,這時候變量的值沿用第一次 函數調用結束后的值:1,再對它做 ++ 操作(自加 1), 的值就變為 2 了。

    依此類推,第三次調用 函數后 的值為 3。第四次 的值為 4。

    所以程序的輸出如下:

    1
    2
    3
    4
    復制代碼

    一個文件中的局部函數(本地函數或靜態函數)

    我們用函數的作用域來結束我們關于變量和函數的作用域的學習。

    正常來說,當你在一個 .c 源文件中創建了一個函數,那它就是全局的,可以被項目中所有其他 .c 文件調用。

    但是有時我們需要創建只能被本文件調用的函數,怎么做呢?

    聰明如你肯定想到了:對了,就是使用 關鍵字,與變量類似。

    把它放在函數前面。如下:

    static int multipleTwo(int number) {
        // 指令
    }
    復制代碼

    現在,你的函數就只能被同一個文件中的其他函數調用了,項目中的其他文件中的函數就只“可遠觀而不可褻玩焉”

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有