---
# System prepended metadata

title: 6.C Arrays
tags: [學校門口大樹石頭下, 程設好難, 他的課就很好睡阿]

---

6.C Arrays
===
## 6.1-6.3 Defining Array
### Array(陣列):
一群具有相同資料型別及名稱的連續記憶體位置群組
- 示意圖：
   ![](https://i.imgur.com/oVXPeZU.png)
### 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)，
==索引必須為整數或整數運算式==
- []是Ｃ的運算子，其優先順序與函式呼叫運算子()一樣是**最高**

|排名|運算子| 型別|
|:--:|:--|:--:|
|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差在哪ＱＱ)
`
`
除錯技巧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個字元，==Ｃ的所有字串都會以'\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)**
![](https://i.imgur.com/gblzwxu.png)

`
小蜜蜂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>