struct 是C中用來包裝資料的關鍵字,當您使用struct來包資料時,您考慮這些件可能擁有的相關性,將之包裝在一起,例如學生會有學號、姓名、住址、電話等, 您可以使用struct來定義一個Student型態,這個型態中包括了學號、姓名、住址、電話等資訊,接著您可以使用Student來宣告新的資料,進 行資料指定或取出等。
舉個實例來說,您可以定義一個「球」的模子,考慮球有各種不同的顏色,以及球最基本的球半徑資訊,您想到這些資訊應該可以定義一個Ball資料型態並從中取得,當您在C中要包裝這些資訊時,您可以如下進行定義:
struct Ball { char color[10]; double radius; };
struct是C中用來定義struct的關鍵字,Ball是自定義的資料型態名稱:
struct Ball { // 成員定義 };
最重要的是別忘了在最後加上分號,初學C的新手很常犯這個錯誤;接下來如果要使用這個Ball的話,可以如下建立實例並初始化:
struct Ball ball1 = {"red", 5.0};
這樣的話,ball1將包括color與radius兩個資料成員,以上的寫法將color成員初始化為"red",而radius初始化為5.0,您也可以先宣告,後來再設定成員資料,例如:
struct Ball ball2; strcpy(ball2.color, "green"); ball2.radius = 10.0;
在存取struct成員時,必須透過所宣告的名稱加上 . 運算子,以下寫個示範程式,看看Ball的使用:
#include <stdio.h> #include <string.h> #include "CustomType.h"
int main(void) { struct Ball ball1 = {"red", 5.0};
struct Ball ball2; strcpy(ball2.color, "green"); ball2.radius = 10.0; printf("ball1: %s,\t%.2f\n", ball1.color, ball1.radius); printf("ball2: %s,\t%.2f\n", ball2.color, ball2.radius); return 0; }
執行結果:
ball1: red, 5.00 ball2: green, 10.00
|
您也可以在定義struct時,直接宣告struct實例,例如:
struct Ball { char color[10]; double radius; } ball1 = {"red", 5.0}, ball2;
如果要宣告struct陣列並初始化每個結構成員,則可以如下:
struct Ball balls[] = {{"red", 3.0}, {"green", 5.0}, {"blue", 10.0}};
int i; for(i = 0; i < 3; i++) { printf("ball1: %s,\t%.2f\n", balls[i].color, balls[i].radius); }
為了方便起見,您可以使用typedef定義structs的名稱,如此一來,宣告並產生實例時,就不用再寫struct關鍵字,例如:
#include <stdio.h> #include <string.h>
struct Ball { char color[10]; double radius; }; typedef struct Ball CBall;
int main(void) { CBall ball1 = {"red", 5.0};
CBall ball2; strcpy(ball2.color, "green"); ball2.radius = 10.0; printf("ball1: %s,\t%.2f\n", ball1.color, ball1.radius); printf("ball2: %s,\t%.2f\n", ball2.color, ball2.radius);
return 0; }
或者是這樣重新命名:
typedef struct { char color[10]; double radius; } Ball;
您可以直接使用指定運算子,將一個struct的實例指定給另一個實例,這會將struct實例的成員值,一個一個「複製」給另一個被指定的對象,例如:
#include <stdio.h>
#include <stdio.h>
typedef struct { char color[10]; double radius; } Ball;
int main(void) { Ball ball1 = {"red", 5.0};
Ball ball2 = ball1; // 指定運算子'='會複製成員值 ball1.radius = 10.0; // 改變ball1成員值並不會改變ball2成員值 printf("ball1: %s,\t%.2f\n", ball1.color, ball1.radius); printf("ball2: %s,\t%.2f\n", ball2.color, ball2.radius); return 0; }
執行結果:
ball1: red, 10.00 ball2: red, 5.00
|
同樣的道理,如果在函式的引數傳遞時,同樣也是將struct的成員值一個一個「複製」給函式上的參數,例如:
#include <stdio.h>
typedef struct { char color[10]; double radius; } Ball;
void foo(Ball);
int main(void) { Ball ball = {"red", 5.0};
foo(ball); printf("ball: %s,\t%.2f\n", ball.color, ball.radius); return 0; }
void foo(Ball ball) { // ball 成員值被複製過來 ball.radius = 100.0; }
在程式的foo()呼叫中,將ball傳遞給foo()上的參數,並在foo()中改變radius,但由於是複製成員值,這並不影響main()當中的ball實例之成員值。
在struct中,也可以再宣告struct,例如:
struct Student { char *name; int number; struct { char *color; double radius; } ball; };
struct Student student1; student1.name = "caterpillar"; student1.number = 1; student1.ball.color = "red"; student1.ball.radius = 5.0;
您也可以看到,定義struct時,不一定要定義struct名稱,而可以直接在定義結構之後,直接宣告實例。
您可以使用structs定義的自訂型態來宣告指標變數,例如:
#include <stdio.h>
struct Ball { char color[10]; double radius; };
int main(void) { struct Ball ball = {"red", 4.0}; struct Ball *ptr; ptr = &ball; printf("ball: %s\t%.2f\n", ptr->color, ptr->radius); return 0; }
這個例子示範了如何宣告struct指標,以及如何使用&對struct實例取位址值,如果使用struct的指標來存取其成員,則必須使用->運算子,執行結果如下:
在 struct 簡介 曾經談過,如果要將struct實例作為引數傳遞,則會直接進行成員值的複製,如果您並不想要複製實例,您可以直接傳遞struct實例的位址值,例如:
struct Ball { char color[10]; double radius; };
void show(struct Ball *ball);
int main(void) { struct Ball ball = {"red", 4.0};
show(&ball);
return 0; }
void show(struct Ball *ball) { printf("ball: %s\t%.2f\n", ball->color, ball->radius); }
執行結果如下:
注意到,以下的語法取得的是struct實例的成員位址值,而不是struct實例的位址值:
struct Ball ball = {"red", 4.0}; printf("%X\n", &ball.color);
為了避免誤會,建議加上括號比較清楚:
struct Ball ball = {"red", 4.0}; printf("%X\n", &(ball.color));
類似的,如果ptr是struct的指標,並儲存某個實例的位址值,那麼以下取得的是實例的成員位址值,而不是實例的位址值,建議還是如第二行的,加上括號比較清楚:
printf("%X\n", &ptr->color); printf("%X\n", &(ptr->color));
struct指標的應用之一,可以參考 堆疊 - 使用鏈結實作(C 語言動態記憶體宣告) 與 佇列 - 使用鏈結實作(C語言動態記憶體宣告),當中的鏈結資料結構,即使用struct指標來連結下一個節點實例。
下面這個例子是個比較進階的例子,程式中使用 函 式指標,讓 structs 實例擁有可操作的函式,而在操作struct實例所擁有的函式時,傳入實例的位址值,如此該函式可以取得實例成員值並進行運算,這是在模擬物件導向中,物件實例擁有屬性及方法(method)的特性:
#include <stdio.h>
typedef struct Ball CBall; struct Ball { char *color; double radius; double (*volumn)(CBall*); // 函式指標 };
// 計算實例體積 double volumn(CBall *this) { double r = this->radius; // 模擬 this 指標 的行為 return r * r * 3.14; }
void initBall(CBall *ball, char *color, double radius) { ball->volumn = volumn; // 連結函式 ball->color = color; ball->radius = radius; }
int main(void) { CBall ball; initBall(&ball, "red", 5.0); printf("ball 實例的體積: %.2f\n", ball.volumn(&ball)); return 0; }
執行結果:
在C中列舉(Enumeration)型別,就是以關鍵字enum開始加上一個列舉名稱,並以大括號括住要群組管理的常數,例如:
enum Action{stop, sit, stand, walk, run};
上例中宣告一個列舉型態Action,大括號中每一個元素稱為列舉元(enumerator),預設上列舉元從第一個開始的實際數值是0,然後依次遞 增,以上例而言,stop為0、sit為1、stand為2、walk為3、run為4,您也可以自行為列舉元設定數值,例如:
enum Action{stop = 1, sit, stand, walk, run};
上例來說,stop為1,下一個列舉元如果沒有設定數值的話,則自動遞增1,所以sit為2、stand為3、walk為4、run為5,列舉的常數值不 需獨一無二,例如下例:
enum Action{stop = 1, sit, stand = 2, walk, run};
下一個列舉元如果沒有設定數值的話,則自動遞增1,所以上例中stop為1、sit為2、stand為2、walk為3、run為4。
宣告列舉之後,您可以用它來宣告列舉變數,例如:
enum Action action = stop;
上例中action可接受的數值範圍為列舉元中的數值範圍,超出的話可能會有不可預期的行為。
在必要的時候,列舉元的數值可用來與其它數值作運算,例如:
enum Action action = stand; printf("%d\n", action + 2);
列舉值的數值被取出再進行加2,所以上例中會在文字模式下顯示數值4。
|
|