6.C Arrays
===
## 6.1-6.3 Defining Array
### Array(陣列):
一群具有相同資料型別及名稱的連續記憶體位置群組
- 示意圖:

### 1.陣列的變數定義
- 元素資料型態 陣列變數名稱[元素個數];
```c=
int big_duck[10];
```
>變數名稱=記憶體位置
```c=
printf("%p", array);//取array第一個元素位址
printf("%p", &array[0]);//跟上面相同結果
```
### 2.初始值
- 給定房間數
```c=
int big_duck[10]={1,2,3,4,5,6,7,8,9,10};
```
- 不給定房間數,但給定人數
```c=
int big_duck[]={1,2,3,4,5,6,7,8,9,10};
```
以上兩種方式皆能開啟10個房間,
後者是依據你給了幾個人頭,去決定要給你幾個房間
- 初始值為0
```c=
int big_duck[10]={0};
```
若要將整個陣列初始為0,只要將第一個元素設為0就好,剩餘的元素在初始值沒補滿陣列的時候會自動補0
>==但有些時候因編譯器之間的差異,並不會完全補0,debug的時候可以檢查陣列裡是不是都是0==
`
小蜜蜂6.1:不要忘了為應該初始化的陣列指定初始值
`
`
小蜜蜂6.2:在陣列初始值上提供比房間還多的初始值是一種語法錯誤
`
>e.g.int var[2]={1,2,3}-error
- 位置編號(position number)
電腦是以0為起始點,與人類習慣的以1為起始點不同,
所以{元素1}會存在房間[0]
```c=
int big_duck[10]={1,2,3,4,5,6,7,8,9,10};
//big_duck[0]=1, big_duck[9]=10
```
### 3.索引與運算子優先原則
```c=
var[5+7]
```
- []內的位置編號正式名稱為索引(index)或下標(subscript),
==索引必須為整數或整數運算式==
- []是C的運算子,其優先順序與函式呼叫運算子()一樣是**最高**
|排名|運算子| 型別|
|:--:|:--|:--:|
|1|[],()|最高|
|2|+, -|加法|
|3|<,<=,>,>=|關係|
|4|==,!=|相等|
|5|&&|AND|
|6||||OR|
|7|=, +=, -=...|指定|
以上整理自原文書p.248一些重要的比較
### 4.陣列用途
- **取代多個變數定義**
>假設今天想設變數來儲存五位同學的英文和國文成績,一般而言我們會直接逐一變數定義
```c=
int aEn, bEn, cEn, dEn, eEn;//English
int aCh, bCh, cCh, dCh, eCh;//Chinese
```
>若開陣列的話,就只要兩個變數定義就好
```c=
int En[5], Ch[5];
```
這樣不僅增加程式的可讀性,在6.4的部分也會介紹陣列的變數定義使其可以簡化多行的程式碼
- **循序存取**
以6.4.1為例
## 6.4 Array examples
### 1.以迴圈初始化陣列
>一般而言,我們要初始多個變數值的話,要這樣一一列出
```C=
int a=1,b=2,c=3,d=4,e=5;
```
>但如果是陣列,可以直接開一個迴圈下去搞
```c=
int grade[5];
for(int i = 0; i < 5; i++)
{
grade[i]=i+1;
}
```
~(雖然這個例子感覺沒有比較簡潔,但如果是超多變數定義的話,簡潔度就會噴出來ㄌ)~
### 2.#define 符號常數來指定陣列大小
```c=
#include<stdio.h>
#define SIZE 5
```
- ==**Symbolic Constant(符號常數)**==
- 符號常數是一種識別字,當程式進行前置處理時,所有出現符號常數SIZE的位置都會代換成++代換文字++5(replacement text)
- 利用符號常數來指定陣列大小,可使程式更具**可修改性(modifiable)**
```c=
#include<stdio.h>
#define SIZE 5
//可改成10,20,1000等,就不用更改第6和7行的數值了
int main()
{
int s[SIZE];//s[5]
for(int i = 0; i < SIZE; i++)//i < 5
{
s[i] = i+1;
}
return 0;
}
```
`
小蜜蜂6.3:不可在#define前置處理器命令後加上分號,前置處理器命令並不是C敘述式
`
- [ ] #define SIZE 5; ~(所有SIZE位置都會被替換成5;)~
- [x] #define SIZE 5
`
小蜜蜂6.4:不可將數值指定給symbolic constant,會造成語法錯誤
`
`
工程師觀點6.1:推薦使用symbolic constant來定義每個陣列大小,使程式更具modifiable
`
`
良好習慣6.1:使用全部大寫字母來命名symbolic constant,以提醒自己symbolic constant不是一般的變數
`
```c=
#define SIZE 5
#define STUDENT 5
```
`
良好習慣6.2:在多個字組成的symbolic constant中,以底線_來分開每個字
`
```c=
#define SIZE_STUDENT 5
```
`
小蜜蜂6.5:不可存取一個超出陣列範圍的元素(我不知道跟小蜜蜂6.2差在哪QQ)
`
`
除錯技巧6.1:陣列的索引不能<0但要<陣列元素個數,在迴圈繼續條件中加入上述檢查
`
## 6.5 Character Array; Manipulate Strings
### 1.初始字元陣列
```c=
char string1[] = "first";
```
```c=
char string1[] = {'f', 'i', 'r', 's', 't', '\0'}
```
以上兩種宣告是相同的,注意前者"first"裡自動包含一個'\0'空字元(null character), 因此陣列string1實際包含6個字元,==C的所有字串都會以'\0'作為結束==,如後者的形式。
(p.s. 如果要得到一個字串長度可以用sizeof)
```c=
char str[]="12345";
printf("%d",sizeof str);
//會輸出6,然後str[5]='\0',str[4]='5'
```
### ==2.scanf至字元陣列==
```c=
char string2[20];//一個最多能塞19個字元+1個空字元的陣列
scanf("%19s", string2);
```
- **使用%Xs做字元轉換詞**:
%19s使scanf最多讀入19個字元,就不會有使用者輸入20個字元以上,造成緩衝器溢位(buffer overflow)的問題了
- **字元陣列在scanf裡不需在變數名稱前加&**,除非你是要特地讀入某個位置的字元陣列
```c=
scanf("%c", &string2[5]);
```
~(但因為只讀入一個’字元‘,所以要用%c)~
- scanf會一直讀入字元,直到遇見==空白、tab、newline或EOF==為止
>使字元陣列能放得下鍵盤所鍵入之字元是你的責任!
>且要注意是否考慮到了空字元的空間
>(p.s.如果要讀入一整行包括空格&換行的話可以用gets(陣列名字))
>(p.s.getchar()是讀入字元,不論空格)
### 3.printf出字元陣列
```c=
char string2[20];
printf("%s\n", string2);
```
- **轉換指定詞:**%s
- 如同scanf,printf不會檢查字元陣列有多大,只會一路印印印,直到遇見**結束空字元**為止。
## 6.6 Static Local v.s. Automatic Local
### 靜態區域陣列Static
```c=
static int array1[3];
```
- **記得**上一次呼叫後的數值結果
- 若未明確指定初始值,static一律**自動指定為0**
```c=
#include<stdio.h>
void static_array(void);
int main()
{
puts("第一次呼叫:");
static_array();
puts("\n第二次呼叫:");
static_array();
return 0;
}
void static_array(void)
{
static int array1[3];
puts("初始值:");
for(int i = 0; i < 3; i++)
{
printf("array1[%d]=%d\t",i, array1[i]);
}
puts("\narray1[i] + 5:");
for(int i = 0; i < 3; i++)
{
printf("array1[%d]=%d\t", i, array1[i]+=5);
}
}
```
```
Output---
第一次呼叫:
初始值:
array1[0]=0 array1[1]=0 array1[2]=0
array1[i] + 5:
array1[0]=5 array1[1]=5 array1[2]=5
第二次呼叫:
初始值:
array1[0]=5 array1[1]=5 array1[2]=5
array1[i] + 5:
array1[0]=10 array1[1]=10 array1[2]=10
```
>==static原本的解釋是靜電==
>[name=佐任]
### 自動區域陣列Automatic
```c=
int array2[3]={0};
```
- **不記得**上次呼叫結果
- **要給定初始值**,不像static,沒給就指定給0
```c=
#include<stdio.h>
void auto_array(void);
int main()
{
puts("第一次呼叫:");
auto_array();
puts("\n第二次呼叫:");
auto_array();
return 0;
}
void auto_array(void)
{
int array2[3]={0};
puts("初始值:");
for(int i = 0; i < 3; i++)
{
printf("array2[%d]=%d\t",i, array2[i]);
}
puts("\narray2[i] + 5:");
for(int i = 0; i < 3; i++)
{
printf("array2[%d]=%d\t", i, array2[i]+=5);
}
}
```
```
Output---
第一次呼叫:
初始值:
array2[0]=0 array2[1]=0 array2[2]=0
array2[i] + 5:
array2[0]=5 array2[1]=5 array2[2]=5
第二次呼叫:
初始值:
array2[0]=0 array2[1]=0 array2[2]=0
array2[i] + 5:
array2[0]=5 array2[1]=5 array2[2]=5
```
`
小蜜蜂6.6:static的值不像auto一樣,每次呼叫都會清除至初始值,不要搞混!
`
## 6.7 Passing Arrays to Function
### 1.函式呼叫陣列
#### 呼叫整個陣列
- 傳參考呼叫(**call by reference**):
將陣列第一個元素==位置==及==大小==傳至函式
```c=
void function(int b[], int size);//函式原型
function(array_name, array_size);//函式呼叫
```
>**陣列名稱**即此陣列**第一個元素位址**
>函式得到陣列第一個元素位址後,再依後面給的陣列大小,即可找到整個陣列
>函式內b[]**不需指定大小**
~(可以跑跑看原文書p.264的程式,驗證陣列名稱是否是其第一個元素位址)~
#### 呼叫一個陣列元素
- 傳值呼叫(**call by value**):
將元素的==數值複製==一份給函式
```c=
void function(int x);
function(a[3]);
```
#### const(常數)(唯讀)
- 用途:避免呼叫函式更改陣列的值
>看到傳參考呼叫的例子,我們是得到陣列的位址並直接對原始元素值進行動作、修改,在很多種狀況下,函式不可以更改陣列中的元素,const就用於此種情況
```c=
void function(const int b[])
{
b[0] /= 2;//error
b[1] /= 2;//error
}
```
>函式中每個嘗試修改此陣列元素的動作都會導致編譯器錯誤
## 6.8 Sorting Arrays
#### 氣泡排序(bubble sort/sinking sort)
- 想像成打擂台,從第一關開始打,打贏就換位子,打輸就固定位子,換下一人挑戰
> 課本p.267
## 6.10 Searching Arrays
- 從陣列中搜尋是否有一個符合**關鍵值(key value)** 的數值
#### 線性搜尋 $O(n)$
- 從第0個元素==逐一比較==(檢查)至找到為止~(可能到最後一位)~
#### 二分搜 $O(log n)$(要會ㄟ)
- 前提:元素**已排序**過
- 搜尋方法:每搜尋(比較)一次,就==排除一半的元素==不考慮
>先找出陣列的==中間元素==並與key做比較,若x>key就捨棄下半部,搜索上半部,反之亦然,以此類推
- 任何一個陣列的**最大比較次數**,可以由大於此陣列元素個數的第一個2的==次方數==來決定
>課本p.273
## 6.11 Multidimensional Arrays
### 二維陣列(==Row Major==)
- 多重索引:第一個索引為**列(row)**,第二個為**行(column)**

`
小蜜蜂6.7:不要把a[x][y]寫成a[x,y],這是個邏輯錯誤,電腦會將其視為a[y](逗號被視為運算子)
`
>把二維陣列想像成是一棟大樓。先放樓,再放房。
>[name=佐任]
```c=
b[樓][房];
```
### 初始值
```c=
int b[2][2]={{1, 2}, {3, 4}};
//b[0][0]=1 b[0][1]=2 b[1][0]=3 b[1][1]=4
//內部一個{}表一個子串列,{1,2}在第一列,{3,4}在第二列
```
- 如同一維陣列,若初始值沒補滿陣列,就會補零,同樣的要檢查是不是真的有補0
```c=
int b[2][2]={1,2,3};
/*
第一列:1 2
第二列:3 0
*/
```
- 若子串列沒有加大括號,則數值會先塞滿第一列,接著塞第二列,空缺補0
## 6.12 Variable-Length Arrays
### 可變長度陣列
- 使用於在編譯時期還==不知道陣列大小==的情況
```c=
#include<stdio.h>
int main()
{
int arraysize;//儲存陣列大小的變數
scanf("%d", &arraysize);//輸入你想要的大小
int array[arraysize];//宣告陣列
printf("sizeof(array) yields array size of %lu bytes.\n", sizeof(array));//檢驗陣列是否如自己所設的空間一樣
return 0;
}
```
```
Output--
6
sizeof(array) yields array size of 24 bytes.
```
- 注意到輸入的大小為6,輸出的大小卻是24。
這是因為我們陣列的資料型態是int,**int佔用了4個位元組**,所以開了6個int房間=6*4=24
(p.s.有時候會因為編譯器的問題產生錯誤)
# Chapter 6 結束
###### tags: `程設好難` `學校門口大樹石頭下` `他的課就很好睡阿`
<style>
.navbar-brand::after { content: " × 老葉的程式設計"; }
</style>