2012年12月8日 星期六

C陷阱: extern & static & 多檔案、宣告、定義、變數、函式


宣告&定義 ( Declarations & Definitions )

[宣告][定義]的細微不同分辨清楚,不混淆
https://en.wikipedia.org/wiki/Declaration_(computer_programming)

語法;寫法

全局變數
函式
宣告 extern int i;
void function( void );
extern void function( void );
定義 int i; void function( void ) { ...內容... }
  函式宣告 extern 關鍵字可以省略


編譯器行為 compiler behavior:

宣告變數 只告知 compiler 變數的存在,和告知變數的型態。
定義變數 叫 compiler 為變數分配儲存空間。
(當然也得知變數的存在與型態)






若程式只有一個檔案


金句:當程式只有一個檔案,變數就用定義就夠了。
   不需要使用到變數宣告。


變數與函式,使用前必須先定義,而且也只能定義這唯一的一次。
(分配儲存空間當然只能唯一的一次)

正確
// file.c
int main( void )
{
    int var = 1; // 定義,並且設定初始值
    var++; // 將var變數值+1
    return 0;
}


錯誤:未定義
// file.c
int main( void )
{
    var++; // compiler 未分配  var 空間
    return 0;
}


錯誤:重複定義
// file.c
int main( void )
{
    int var = 1; // 定義
    int var = 1; // 重複定義
    var++;
    return 0;
}






若程式有多個檔案

當程式是多個檔案,才有使用宣告的必要。

現代習慣做法,原則為:
把宣告寫在.h檔,把定義寫在.c檔

若沒按照這個原則,在.h檔裡放了定義,
某.h檔有多個.c檔去 include它,就會產生重複定義的錯誤。


可以宣告很多次,但定義必須是唯一的!
(記得宣告只是得知變數的存在與型態,所以可以宣告很多次)
(定義還會分配儲存空間,當然只能唯一的一次)
多檔案(multiple files)下的宣告,編譯器會自動從所有檔案中尋找它的定義




var.h
// var.h
extern int globalVar; // 變數宣告

void function( void ); // 函式宣告

var.c
// var.c
#include "var.h"
#include <stdio.h>

int globalVar = 1; // 變數定義,並且設定初始值

void function( void ) // 函式定義
{
    printf("Variable is : %d\n", globalVar);
}

file.c
// file.c
#include "var.h" // 只要 include .h檔的宣告,
        // 即可使用定義在其他檔案的全局變數

int main( void )
{
    globalVar++;
    function();
    return 0;
}






請在定義變數時,設定初始值!

定義變數,沒有設定初始值,其值是無法保證的。
#include <stdio.h>
int main( void )
{
    int sum;  // 沒有設定初始值
    for( int i = 0 ; i < 5 ; i++ )
        sum++;  // 無法保證數值是什麼
    function( );
    return 0;
}

註:全局變數初始值


不要宣告變數時,設定初始值!

※在宣告變數的同時設定初始值,會轉變成定義

例如:extern int i=1; 是定義,不要這樣寫,容易混淆視聽..
拆開來
..

.h
extern int i; //宣告 i,寫在.h檔

.c
int i = 1; //定義 i,此時設定初始值,寫在.c檔





全局變數&局部變數

全局變數(Global Variable):定義在函式之的變數。
局部變數(Local Variable):定義在大括號 { } 的變數。

int global_var;    //全局變數生命週期直到程式結束,而且跨檔案共享

int main( void )
{
    int block_var;    //局部變數生命週期在大括號 { } 之間
    return 0;
}







static

static 變數 & static 函式


static:是〝私有化〞的概念

static 表示其:
1.存取的範圍:只在定義的文件檔內,不具跨文件檔共享。
2.生命週期:直到程式結束,才會從記憶體消逝。

現在習慣應用在,模組化程式
把大的程式,拆解為功能獨立的小程式,各自寫在不同的.c檔
只有在模組用到的變數函式設為 static讓這些 static 只在模組內的.c檔使用

定義 static 的寫法:
static通常寫在.c 
static int i;
static void function( void ) { ..........內容.......... }

思考:
若想要宣告static在.h檔,則所有include了.h的.c檔都會產生static
必須思考目的為何?是否為想要的效果,be careful。

變數的儲存週期
https://www.ptt.cc/bbs/b97902HW/M.1226692638.A.F2B.html



struct;結構

struct 結構的[宣告]與[定義]

struct S; // declares S
struct S { int a; int b; }; // defines S, S::a, and S::b

私用的 private struct

// s.c
#include <stdio.h>

struct justInFile {     // 結構定義
    int integer;
    float f; 
};

struct justInFile entity;  // 這裡才做出實體entity,分配空間

void function( void )
{
    printf("i=%d\n", entity.integer);
    return;
}


不跨檔案使用的、私用的 struct definition 應該寫在 .c 檔,這很正常
但是跨檔案使用的、公開的 struct definition 建議寫在 .h 檔。why?
思路寫在另一篇
http://ashinzzz.blogspot.tw/2012/12/struct-declaration.html









int a;只能定義一次?
int a;
暫時定義?

重複寫定義,到底?    暫時定義是不會配置記憶體空間的???
參考
神人http://hsian-studio.blogspot.tw/2008/09/blog-post_126.html
神整理http://www.ptt.cc/bbs/C_and_CPP/M.1315949529.A.DD0.html
錯誤http://libai.math.ncu.edu.tw/bcc16/C/C/b-9.shtml

5 則留言:

Unknown 提到...

Nice!!!

匿名 提到...

請問最後結構 為什麼跨檔公開要把定義寫在.h?

提到...

你好,我google後得到的結論是:
若把結構定義寫在.c檔,每個.c檔都要寫一次定義,
當你要做修改,就要一個一個改,很不方便,修改有出錯的風險。
詳細的思路&範例我近期再整理一篇文章好了。

小A 提到...

您好,我利用dev C 這套IDE來練習您的跨檔案變數
我開了三個原始碼,分別存了 var.c var.h file.c
檔名和您取的一樣
我將var.h存檔
然後各別編譯 var.c和file.c
file.c編譯時 提示
未定義的 globalVar
未定義的 function
請問我是漏了什麼步驟嗎?

提到...

猜可能是dev不能處理"中文空格"的關係?
我為了網頁排版 程式縮排,前面都有一些"肉眼看不到的全形的中文空格"
-------------------------------------------------
        // 即可使用定義在其他檔案的全局變數
    globalVar++;
-------------------------------------------------
把前面的空格都刪掉,像以下:
-------------------------------------------------
// 即可使用定義在其他檔案的全局變數
globalVar++;
-------------------------------------------------
可能就可以編譯過了?