C語言基礎:多文件編譯
多文件編譯,在我們最早編寫Hello World程序時我們就將程序寫在了一個後綴名為.c的文本文件里,然後通過gcc編譯器對其編譯並運行。在本節我們將學習如何編寫多個源文件的程序
一、頭文件header與源文件source
通常我們會在頭文件中一些類型的定義、結構體定義、宏定義、函數聲明、include包含等內容。而在源文件中編寫實際的功能實現。
例如我們可以在頭文件hello.h中寫入如下內容
/* hello.h */#include <stdio.h>void print_hello(void);
其中包含了標準輸入輸出頭文件,類型定義,函數的聲明等內容,而我們再編寫一個hello.c的源文件:
/* hello.c */#include "hello.h"void print_hello(void){ printf("Hello World!
");}
源文件中包含了hello.h這個頭文件,於是在這個hello.c文件中就可以使用這些在頭文件中定義的內容,可以使用自定義類型、自定義函數、標準輸入輸出函數等。在使用gcc編譯代碼時只需要指定hello.c即可編譯器會根據#include "hello.h"找到這個頭文件,注意hello.h和hello.c要存放在同一個目錄下。
值得詳細講述的還有include的路徑問題,當使用<>來指定包含的頭文件時,編譯器會從系統頭文件庫中進行查找,而使用""來包含的頭文件,編譯器將會從當前程序目錄進行查找。在include時被包含文件可以是絕對路徑,也可以是相對路徑,總之,只要頭文件的存放路徑與當前源文件的關係正確即可。
另外include不僅僅能包含.h類型的頭文件,理論上它可以包含任意類型的文件,例如包含一個.c文件等,但我們通常都用於包含.h類型的頭文件。
二、多文件編譯
在前面我們已經學習了如何編寫頭文件與源文件,但這還只是停留在單一文件的編譯方法上。多數大型的工程的頭文件和源文件非常多,我們也不可能把所有的代碼都寫在同一個文件里,這樣也不方便代碼的閱讀與維護,通常都會根據不同的功能將代碼分別書寫到多個源文件與頭文件中。例如我們可以編寫一個簡單的日曆程序:
/* ioput.h */#include <stdio.h>//讀取用戶輸入的年份int input_year(void);//讀取用戶輸入的月份int input_month(void);//顯示日曆void output_days(int year, int month, int week, int is_leap_year);/* ioput.c */#include "ioput.h"//讀取用戶輸入的年份int input_year(void){ int year; printf("Enter the year:"); scanf("%d", &year); return year;}//讀取用戶輸入的月份int input_month(void){ int month; printf("Enter the month:"); scanf("%d", &month); return month;}//顯示日曆void output_days(int year, int month, int week, int is_leap_year){ //月份與星期的名稱及每個月的天數 char* month_name[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; char* week_name[7] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" }; int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; //閏年二月29天 if (is_leap_year) { days[1] = 29; } printf("
"); //顯示年、月和星期 printf(" %s %d
", month_name[month], year); for (int j = 0; j < 7; j++) { printf("%2s ", week_name[j]); } printf("
"); //顯示每月1日前的空白 for (int i = 0; i < week % 7; i++) { printf(" "); } //循環顯示日期 for (int i = 1; i <= days[month]; i++) { printf("%2d ", i); //顯示7個數後換行 if ((i + week) % 7 == 0) { printf("
"); } } printf("
");}/* calc.h */#include "ioput.h"//蔡勒公式計算星期,只適合於1582年10月15日之後的日期int calc_week(int year, int month, int day);//計算閏年int calc_leap_year(int year);//日曆核心函數void calc_core(void);/* calc.c */#include "calc.h"//蔡勒公式計算星期,只適合於1582年10月15日之後的日期int calc_week(int year, int month, int day){ if (month <= 2) { month += 12; year--; } int century = year / 100; year %= 100; int days = (year + year / 4 + century / 4 - 2 * century + 26 * (month + 1) / 10 + day - 1) % 7; while (days < 0) { days += 7; } return days;}//計算閏年int calc_leap_year(int year){ if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { return 1; } return 0;}//日曆核心函數void calc_core(void){ do { int year = input_year(); if (year <= 1582) { break; } int month = input_month(); if (month <= 0 || month >= 13) { break; } int is_leap_year = calc_leap_year(year); int week = calc_week(year, month, 1); month--; output_days(year, month, week, is_leap_year); } while (1);}/* main.c */#include "calc.h"int main(int argc, char *argv[]){ calc_core(); return 0;}
接受用戶的輸入和輸出為一對頭文件與源文件ioput.h和ioput.c,而計算閏年和計算某日期是星期幾則在另一對頭文件當中calc.h和calc.c,最後主函數main所在的源文件main.c中包含了這幾個相關的頭文件,並將這幾個源文件一同編譯:
gcc -o calc main.c ioput.c calc.c
gcc將這3個源文件一同編譯成一個可執行文件。我們來看看這個文件的運行結果。
Enter the year: 2017Enter the month: 11 November 2017Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
關於此例只是為了讓讀者清楚多文件編譯的過程和方法,如果讀者對例子中的程序有不清楚的地方請參見《顯示精美日曆》。
歡迎關注公眾號:編程外星人
推薦閱讀: