## 結構 structure
[toc]
### Intro
- 如果我們有一些相關的資料,但是其資料類別不一致,我們就可以使用**結構(structure)** 來將這些資料搜集再一起。
- 而一個結構中有許多**欄位(field)**,就如同一個陣列有許多元素一樣。
:::info
:bulb: 陣列用來存很多相同類別的資料、結構用來存很多不同類別的資料。
:::
### 定義
- structure是一種**derived data type(衍生數據類型)**,考慮以下的structure定義:
```c
#define NAMELEN 20
#define PHONELEN 10
#define YEAR 4
struct student{
char name[NAMELEN];
int id;
char phone[PHONELEN];
float grade[YEAR];
int birth_year, birth_month, birth_day;
};
```
- keyword `struct` + structure tag(自己定義的資料類型的名稱,如: student)
- 記得在最後面的大括號後加上分號
#### 宣告類別
如果要宣告一個student類型的變數,如:
```c
struct student john;
```
- 我們可以將struct student看成一個資料類別。
:::info
:bulb:C++中,宣告不同的structure類型變數時不需要加上struct這個keyword
:::
另外也可以這樣宣告:(直接加在structure的定義後)
```c
struct student{
char name[NAMELEN];
int id;
char phone[PHONELEN];
float grade[YEAR];
int birth_year, birth_month, birth_day;
}john;
```
:::info
:bulb:值得注意的是,structure沒有"="運算子,因為structure中的不同變數會存在記憶體中不連續的位址
:::
#### 結構大小
計算`struct student`的位元大小:
$\text{sizeof}(\text{struct student})\\
\quad =30\times\text{sizeof}(\text{char})+4\times\text{sizeof}(\text{int})+4\times\text{sizeof}(\text{float})\\
\quad =30\times 1+4\times4+4\times4\\ \quad=30+16+16\\ \quad=62$
但實際上,`struct student`的位元大小是64,在等等會提到為何會差2。
### 初始化
跟初始化陣列的方法很像,在大括號中、用逗點把不同的初始值分隔開來,如:
```c
struct student john =
{"John Smith", 12345, "1234567",
{4.0,3.9,3.8,3.6}, 2000, 1, 1};
```
另外也可以一個個變數去初始化,如:
```c
john.id = 12345;
john.birth_year = 2000;
strcpy(john.name,"John Smith");
```
:::info
:bulb:除了在宣告時初始化外,也能用=與strcpy等方式設定。
:::
### 欄位
#### 存取
會需要兩種運算子:
- `.`(structure member operator)
- `->`(structure pointer operator or arrow pointer)
主要的差別在:`.`是用在一般的struct變數、而`->`是用在指向struct的指標,如:
```c
printf("%s",aCard.suit);
printf("%s",cardPtr->suit);//equivant to (*cardPtr).suit;
```
#### 記憶體位址
- 各欄位的記憶體位址順序按照宣告順序
- 因為浮點數的記憶體位址必須是四的倍數,因此雖然只需要10位元,實際上還是會開到12,稱為**對齊(alignment)**
::: info
:bulb:結構內各欄位都是按照宣告順序一一出現在記憶體中,但中間可能會有因為資料型態不同而需要對其產生的空隙。
:::
### Using Structure with Functions
- structure member:pass by value(copy一份傳到函式中)
- structure variable:pass by value(copy一份傳到函式中)
- structure pointer:pass by reference(直接修改原本的值)
### typedef
如果覺得要宣告一個structure類型的變數很麻煩,可以用typedef來縮短:
```c
typedef struct{
char name[NAMELEN];
int id;
char phone[PHONELEN];
float grade[YEAR];
int birth_year, birth_month, birth_day;
}Student;
```
因此下次宣告變數時只要:
```c
Student john;//宣告一個Card類型的變數card
```