C Primer Plus 第9章 函數 9.4 多源代碼文件程序的編譯

9.4.1  UNIX編程

首先假定UNIX系統下安裝了標準的UNIX C 編譯器cc。文件file1.c和file2.c中包含有C的函數。下面的命令將把這兩個文件編譯在一塊兒並生成可執行文件a.out;windows

cc file1.c file2.c函數

另外還將生成兩個目標文件file1.o和file2.o。若是隨後只更改了file1.c而沒有改變file2.c,可使用如下命令編譯第一個文件並將其連接到第二個文件的目標代碼:ui

cc file1.c file2.o命令行

在UNIX系統下有一個make命令能夠自動管理多文件程序,本處不對此深刻討論。code

9.4.2  LINUXci

首先假定Linux系統下安裝了GNU C 編譯器gcc。文件file1.c和file2.c中包含有C的函數。下面的命令將把這兩個文件編譯在一塊兒並生成可執行文件a.out;資源

gcc file1.c file2.cget

另外還將生成兩個目標文件file1.o和file2.o。若是隨後只更改了file1.c而沒有改變file2.c,可使用如下命令編譯第一個文件並將其連接到第二個文件的目標代碼:原型

gcc file1.c file2.o

9.4.3  DOS命令行編譯器

大多數DOS命令行編譯器的工做機制同UNIX系統下的cc命令相似。一個不一樣之處在於DOS系統下目標文件的擴展名是.obj而不是.o。並且有些編譯器並不生成目標代碼文件,而是生成彙編語言或其餘特殊代碼的中間文件。

9.4.4  windows和Macintosh編譯器

Windows和Macintosh系統下的編譯器是面向工程的。工做(project)描述了一個特定的程序所使用的資源。這此資源中包括源代碼文件。使用這種編譯器運行單文件程序時,必須建立工程。而對於多文件程序 ,須要使用一個菜單命令將源代碼文件加入到一個工程之中。並且工程必須包含全部的源代碼文件(擴展名爲.c的文件)。可是,頭文件不能包含在工程之中。由於工程只管理所使用的源代碼文件,而使用哪些頭文件須要由源代碼文件中的#include指令肯定。

9.4.5  頭文件的使用

若是把main()函數放在第一個文件中而把自定義函數放在第二個文件中實現,那麼第一個文件仍要使用函數原型。若是把函數原型放在一個頭文件中,就沒必要每次使用這些函數時輸入其原型聲明瞭這也正是C標準庫的作法,好比把輸入/輸出函數的原型聲明放在stdio.h中,把數學函數的原型聲明放在math.h中。對於包含自定義函數的文件也能夠這麼作。

編寫程序的過程當中,須要常用C的預處理器定義常量。而定義的常量只能用於包含相應#define語句的文件。若是程序中的函數分別放在不一樣的文件之中,那麼就必須使定義常量的#define指令對每一個文件均可用。而直接在每一個文件中鍵入該指令的方法既耗時又容易出錯,同時也會帶來一個維護上的問題:即若是修改一個使用#define定義的數值,那麼必須在每個源代碼文件中對其進行修改。比較好的解決方法是把全部的#define指令放在一個頭文件中,而後在每一個源代碼文件中使用#include語句引用該頭文件。

總之,把函數原型和常量定義放在一個頭文件中是一個很好的編程習慣。

咱們考慮這樣一個例子,假設須要管理4個連鎖的旅館,每個旅館都有不一樣的收費標準,可是對於一個特定的旅館,其中的全部房間都符合同一種收費標準。對於預約住宿時間超過一天的人來講,第2天的收費是第一天的95%,而第3天的收費則是第2天的95%,等等。咱們須要這樣一個程序,即對於指定的旅館和總的住宿天數能夠計算出收費總額。同時,程序中要實現一個菜單,從而容許用戶反覆進行數據輸入直到選擇退出。

程序清單9.九、9.10以及9.11列出了上述程序的源代碼。第一個程序清單包含了main()函數,在mian()函數中能夠看到整個程序的組織結構。第二個程序清單包含所使用的函數,並且咱們假設這些函數放在一個單獨的文件中。最後,程序清單9.11列出了一個頭文件,其中包含了程序的全部源文件使用的自定義常量和函數原型。前面講過,在UNIX和DOS環境下,指令#include "hotels.h"中的雙引號表示被包含的文件位於當前目錄下(該目錄通常包含源代碼)。

程序清單  9.9  usehotel.c控制模塊

/*usehotel.c  --旅館房間收費程序*/
/*與程序清單9.10一塊兒編譯*/
#include <stdio.h>
#include "hotel.h"  /*定義常量、函數聲明*/
int main(void)
{
    int nights;
    double hotel_rate;
    int code;

    while((code=menu())!=QUIT)
    {
        switch (code)
        {
            case 1:hotel_rate = HOTEL1;
            break;
            case 2:hotel_rate = HOTEL2;
            break;
            case 3:hotel_rate = HOTEL3;
            break;
            case 4:hotel_rate = HOTEL4;
            break;
            default:hotel_rate = 0.0;
            printf("Oops!\n");
            break;
        }
        nights = getnights();
        showprice(hotel_rate,nights);
    }
    printf("Thank you and goodbye. ");
    return 0;
}

程序清單  9.10  hotel.c函數支持模塊

/*hotel.c --旅館管理函數*/
#include <stdio.h>
#include "hotel.h"
int menu(void)
{
    int code,status;
    
    printf("\n%s$s\n",STARS,STARS);
    printf("Enter the number of the desired hotel: \n");
    printf("1) Fairfield Arms  2) Hotel Olympic\n");
    printf("3) Chertworthy Plaza  4) The Stockton\n");
    printf("5) quit\n");
    printf("%s%s\n",STARS,STARS);
    while((status=scanf("%d",&code))!=1 || (code<1 || code>5))
    {
        if(status != 1)
            scanf("%*s");  /*在scanf()中,把*放在%和說明符之間,它使用函數跳過相應的輸入項目*/
        printf("Enter an integer from 1 to 5,please.\n");
    }
    return code;
}
int getnights(void)
{
    int nights;
    
    printf("How many nights are needed? ");
    while(scanf("%d",&nights)!=1)
    {
        scanf("%*s");
        printf("Please enter an integer,such as 2.\n");
    }
    return nights;
}
void showprice(double rate,int nights)
{
    int n;
    double total = 0.0;
    double factor = 1.0;
    for (n=1;n<=nights;n++,factor *= DISCOUNT)
        total += rate*factor;
    printf("The total cost will be $%0.2f.\n",total);
}

程序清單  9.11  hotel.h頭文件

/*hotel.h  --hotel.c中的常量定義和函數聲明*/
#define QUIT 5
#define HOTEL1 80.00
#define HOTEL2 125.00
#define HOTEL3 155.00
#define HOTEL4 200.00
#define DISCOUNT 0.95
#define STARS "******************************"

//給出選項列表
int menu(void);
//返回預約的天數
int getnights(void);
//按飯店的星級和預約的天數計算價格並顯示出來
void showprice(double,int);

順便提一下,程序中有幾處頗有特點。好比,函數menu()和getnights()經過檢測scanf()的返回值來跳過輸入的非數字數據,而且調用scanf("%*s")來跳至下一空白字符。請注意menu()中的如下代碼如何檢查出非數字的輸入和超出範圍的數據:

while((status=scanf("%d",&code))!=1 || (code<1 || code>5))

這段代碼運用了C的兩個運算規則:邏輯表達式從左向右運算;而且一旦結果明顯爲假,運算會當即中止。在本例中,只有肯定scanf()已經成功讀取了一個整形數值後, 變量code的數值纔會被檢測。

用函數分別實現各個獨立的功能須要使用這種精練的語句。固然第一次編寫menu()和getnights()時可能只使用了簡單的scanf()函數而沒有數據檢查功能。而後,就能夠根據基本程序的運行狀況對每一個模塊進行改進。

相關文章
相關標籤/搜索