2012年12月9日 星期日

C語言:結構的宣告&定義


struct

結構 / 結構體


struct 其實就是使用者自訂型態。(利用基本型態再組合成一個自訂型態)

struct sName;  //結構宣告

結構宣告:只是宣告存在一個叫做 sName 的 struct
還沒有表明 struct 具體內容長得如何

struct sName {  //結構定義
    int i;
    double d;
    char c;
};

結構定義:表明了 struct 的具體內容、具體結構
但是尚未分配記憶體空間

定義好了 struct ,就可以把它當成一個型態來使用,例如:
int var1;
struct sName var2; //定義實體var2
struct sName array1[10];
struct sName *ptr;

可以把「struct sName」看成是一種型態
此時系統才會分配記憶體空間
我們把分配了記憶體空間的這些,例如:var2、array1 通稱為「實體」






如果把結構定義寫在.c

我們把變數和函式的宣告寫在.h檔,定義寫在.c檔
可是結構的定義卻建議寫在.h檔,為什麼?
寫在.c檔也可以啦,但是你在每個用到結構的.c都要寫一份結構定義,否則是編譯不會過的

s.h
//s.h
struct s;     //結構宣告

extern struct s entity;  //結構實體宣告

s.c
//s.c
#include "s.h"    

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

struct s entity;     //定義了結構實體

source1.c
//source1.c
#include "s.h"    

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

void function1(void)
{
    entity.integer = 1;
  //需要結構定義
    return;
}


source2.c
//source2.c
#include "s.h"    

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

void function2(void)
{
    entity.integer = 2; //需要結構定義
    return;
}


source3.c
//source3.c
#include "s.h"    

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

void function3(void)
{
    entity.integer = 3; //需要結構定義
    return;
}



用到 struct 都要寫一份同樣的結構定義,將來若要修改這個 struct 要一個一個改,若漏掉沒改到 struct 就不一致了,將產生問題









【標題】結構的定義寫在 .h 要注意:
先說在一個.c檔內 struct 定義也必須是唯一的。
所以例如試試看 struct s { int i }; 寫兩次,編譯是不會過的。
// file.c
struct s { int i };
struct s { int i }; //重複定義

為了避免寫在 .h 檔以後,經過多重的引用以後可能出現"重複定義"的錯誤,
我們使用稱為 header guard 的寫法
,把它加上巨集防範 (#include 防範)

// file.h
#ifndef _FILE_H_
#define _FILE_H_
 內容
 。
 。
 。
#endif

_FILE_H_ 必須確保此名稱是『唯一的』,名稱重複就沒救了,所以我們常用檔名加底線做為唯一名稱
ifndef 意思是 if not define 如果沒定義過 _FILE_H_,也就是第一次碰到就做中間的內容,第二次再碰到就會跳過不做,就樣就可以避免重複定義






完整 struct 寫法

// s.h
#ifndef _S_H_
#define _S_H_

struct s {
     // 結構定義寫在 .h 檔
    int integer;
    float f; 
};

extern struct s entity;  // entity實體宣告

#endif


// s.c
#include "s.h"

struct s entity;  // entity實體定義 (為entity分配空間)


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

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

.........
參考

http://stackoverflow.com/questions/6316987/should-struct-definitions-go-in-h-or-c-file

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

2012年12月7日 星期五

C陷阱:變數的宣告&定義



宣告 (Declarations)

變數宣告,讓程式得知某個變數的型態名稱(不配置空間)。(宣告可以出現一次以上,只要內容一致)。
A declaration makes known the type and name of the variable to the program.

寫法/例子:

extern int i;

//只做宣告,不配置空間


定義 (Definitions)

定義,也是一種宣告。變數的定義除了宣告型態與名稱外,也為這個變數配置空間,也可能給予初始值。然而程式中一個變數只能有一次定義,不可以重複定義。
A definition is also a declaration. When we define a variable, we declare its name and type. A deinition of a variable allocates storage for the variable and may also specify an initial value for the variable. There mush be one and only one definition of a variable in a program.

寫法/例子1:

int i;

//定義,可以只配置空間,尚不決定初始值。

寫法/例子2:

int i = 1 ;

//定義,配置空間並給予初始值。

寫法/例子3: 若用extern宣告變數同時又給初始值的話,則會被視為一個定義。

extern int i = 1 ;

//只要設初值,就會變成定義。


使用變數
  1. 我們在使用變數之前,必須先宣告定義一次,然後需要的話給它一個初始值。
  2. 若此變數要在多個檔案使用(外部連結性全域變數),請在某一個檔案定義它,而其他每個用到該變數的檔案都要宣告該變數。

各種錯誤

變數不能只有宣告,而沒有定義,會發生 linking error
(函式就可以只有宣告,沒有定義)

變數若定義不只一次,會發生重複定義的錯誤!