C語言程式設計 學習筆記 12.3 多個原始碼檔案、標頭檔案、宣告
我們經常在做“分而治之”的事情(多個.c檔案): 1.main()裡的程式碼太長了適合分成幾個函式 2.一個原始碼檔案太長了適合分成幾個檔案 3.兩個獨立的原始碼檔案不能編譯成可執行的程式
對於(1),我們可以舉以下例子: 有個主函式main.c,有另外一個函式max.c(求最大值)
#include<stdio.h>
int max(int a,int b);
int main(){
int a=5;
int b=6;
printf("%d\n",max(a,b));
return 0;
}
對於上面直接編譯,系統會報錯:
undefined reference to `max(int, int)’
未定義的max引用
int max(int a,int b){
return a>b?a:b;
}
對於上面編譯,會說沒有main();
要想將這兩個聯合在一起,需要做如下操作(DEV C++環境下) 檔案-新建-專案-console application-取名叫max-選擇C專案-確定-儲存在剛剛main和max專案的目錄中 然後能在如下介面-專案管理中看到剛剛建立的max,右鍵將自己的兩個cpp/c檔案放入
加入後進行編譯,編譯通過,就可以使用了。
標頭檔案 在剛剛的程式碼中,main函式中事先聲明瞭max:
int max(int a,int b);
如果我們不想將多個函式宣告也寫在主函式中,就像#include<string.h>一樣直接使用,那麼可以自己手寫一個頭檔案,在需要的時候引用它: 1.新建原始檔(devc++會提示你是否在專案中增加新單元,選擇yes,否則就是麻煩一點而已) 2.將我們宣告的函式原型放入其中,並取名,檔案格式是
max.h
然後將其分別在main函式、max函式中include進去
#include "max.h"
編譯通過,可以執行。
全程式碼如下:
//main.cpp
#include <stdio.h>
#include "max.h"
int main(){
int a=5;
int b=6;
printf("%d\n",max(a,b));
return 0;
}
//max.cpp
#include "max.h"
int max(int a,int b){
return a>b?a:b;
}
//max.h
int max(int a,int b);
標頭檔案
#include #include是一個編譯預處理指令,和巨集一樣,在編譯之前就處理了 它把那個檔案的全部文字內容原封不動地插入到它所在的地方,所以也不是一定要在.c/.cpp檔案的最前面#include
" "和< > #include有兩種形式來指出要插入的檔案 " "要求編譯器首先在當前目錄(.c/.cpp檔案所在的目錄)尋找這個檔案,如果沒有,到編譯器的指定的目錄去找 < >讓編譯器只在指定的目錄去找 編譯器本身自己知道自己的標準庫標頭檔案在哪裡(string.h、iostream等) 環境變數和編譯器命令列引數也可以指定尋找標頭檔案的目錄
#Include的誤區 #include不是用來引入庫的 stdio.h裡只有printf的原型,printf的程式碼在另外的地方,某個lib(windows)或.a(unix)中 現在的C語言編譯器預設會引入所有的標準庫 #include<stdio.h>只是為了讓編譯器知道printf函式的原型,保證你呼叫時給出的引數值是正確的型別
標頭檔案 在使用和定義這個函式的地方都應該有#include這個標頭檔案 一般的做法是任何.c都有對應的同名的.h,把所有對外公開的函式的原型和全域性變數的宣告都放進去
不對外公開的函式 在函式前面加上static就使得它成為只能在所在的編譯單元中被使用的函式 在全域性變數前面加上static就使得它成為只能在所在的編譯單元中被使用的全域性變數
宣告 如果我想在某檔案中使用一個全域性變數(A檔案中使用B檔案的一個變數) 那麼可以在.h檔案中宣告該變數
extern int gAll;
變數的宣告
int i;
是變數的定義
extern int i;
是變數的宣告
宣告的定義 宣告是不產生程式碼的東西(函式原型、變數宣告、結構宣告、巨集宣告、列舉宣告、型別宣告、inline函式) 定義是產生程式碼的東西
標頭檔案 只有宣告可以被放在標頭檔案中,否則會造成一個專案中多個編譯單元裡有重名的實體(某些編譯器允許幾個編譯單元中存在同名的函式,或者用weak修飾符來強調這種存在)
重複宣告 同一個編譯單元裡,同名的結構不能被重複宣告 如果你的標頭檔案裡有結構的宣告,很難這個標頭檔案不會再一個編譯單元裡被#include多次 所以需要“標準標頭檔案結構” 上述的意思舉例: 我有max.h和min.h兩個標頭檔案,裡面的內容是這樣:
//max.h
int max(int a,int b);
struct Node{
int a;
char *b;
};
//min.h
#include "max.h"
//main.cpp
#include <stdio.h>
#include "max.h"
#include "min.h"
int main(){
int a=5;
int b=6;
printf("%d\n",max(a,b));
return 0;
}
編譯會報
[Error] redefinition of ‘struct Node’
重複定義了struct node。 這在大型專案中經常會遇到這種情況。
所以這時候需要“標準標頭檔案結構” 在max.h中修改程式碼如下:
#ifndef _MAX_H_
#define _MAX_H_
int max(int a,int b);
struct Node{
int a;
char *b;
};
#endif
再次編譯,編譯成功。
意思是,如果(if)我們沒有定義(ndef)MAX_H(名字自己取,取得特殊一點易於和程式碼內容區分開),那麼系統就幫助我們定義_MAX_H_,並附有下面的程式碼,直到#endif為止
所以在我們#include"min.h"的時候,因為之前已經#include"max.h",並定義了_MAX_H_,所以min的ifndef生效,不發動,直到endif為止。
運用條件編譯和巨集,保證這個標頭檔案在一個編譯單元中只會被#include一次 #pragma once也能起到相同的作用,但並不是所有的編譯器都支援