---
tags: 2021CRC Extra Resources
title: 社課額外補充教材:陣列 + 迴圈複習
---
# 社課額外補充教材 : 陣列 + 迴圈複習
如果你覺得社課還算輕鬆,這裡提供你一些額外的補充教材。
這些是我們在備課的時候覺得稍微進階,或是篇幅太多,而沒有放在正式社課的內容。
---
這邊有目錄,左邊也有,可以跳到自己想看的地方
[TOC]
---
建議必看的地方 :
* 陣列的動態宣告
* 陣列的遍歷
* 常見的陣列
* 一些有趣的用法
* 陣列的陣列
---
## 陣列的動態宣告
我們知道陣列必須在宣告時就指定範圍,可是當不知道大小時就很頭痛。
這時候就要用到動態宣告。
### 使用方式
```cpp=
int size;
int array[size]; //宣告時的大小可以換成變數
```
#### 注意:
裡面的變數只能是整數(滿合理因為沒有"小數個東西"),
而且<font color="#f00">在舊的 C++ 中不支援這種使用方式。</font>
當時只能使用像是 Array 或是 Vector(之後會教) 之類的資料結構實現。
---
## 陣列的遍歷
陣列的遍歷指的是針對陣列的每一項進行動作,也就是走過整個陣列。
雖然現在的陣列都能通過變數的形式得知長度,但之後可能會遇到能夠擴充的陣列,這時候就要使用這些方法遍歷陣列。
```cpp=
int l = sizeof(a); //a是目標陣列,不知道長度
int i=0;
while(i<l){
std::cout<<a[l]<<' ';
i++;
}
//
//也等於
for(int i=0;i<sizeof(a);i++)
std::cout<<a[i]<<' ';
```
不過,sizeof函數不能判斷當成資料傳入函數中的陣列,也就是說,它必須要和陣列的宣告位置處在同一函數中(例如:main函數。
所以,像是std::vector或std::array都有內建回傳陣列長度的函數,盡量用那些函數取得長度。
除此之外,c++也有其他的方式遍歷陣列,不過都要求比較新的c++版本(雖然也沒有多新。
```cpp=
for(auto i:a)
//盡量用auto,因為它抓的資料型態很可能不是你想得那個
{
std::cout<<i<<' '; //記住不能用a[i]
}
```
---
## 到底什麼是陣列?
<font color="#f00" size=4>**警告:以下提到的觀念牽扯到指標,非相關人員請立即撤離**</font>
你是否有想過為什麼 int,char,bool ,甚至連不是保留字的 std::string 都有陣列?
這是因為陣列不是一種資料型態,他是一種函式。
所謂陣列,是在宣告時替變數拉長他的空間,好放下更多同類型的資料。
也就是說 int a[2] 的大小會比 int a大兩倍
```cpp=
#include<iostream>
int main(){
int a[2];
int b;
std::cout << sizeof(a) << ' ' << sizeof(b);
}
//會輸出8 4
```
你可能有注意到我在 sizeof() 中的 a 後面並沒有中括號,很明顯不符合之前說的陣列使用。
其實不盡然,所以我們接下來就要談談中括號的意義是什麼,以及它為什麼從0開始。
先說說陣列長什麼樣子,
以上面的a[2]為例:
| 記憶體位置 | 0 | 1 |
| ---------- | ---- | ---- |
| 對應到的變數 | a[0] | a[1] |
| 指標的表示 | a | a+1 |
雖然實際上的記憶體位置不會那麼剛好從 $0$ 開始,但他們確實會連在一起。
對不知道指標是什麼的人先簡單說明一下:
指標宣告時並不會像變數一樣被分配空間,但是他們可以被指定位置。
```cpp=
#include<iostream>
int main(){
int *a; //宣告指標
int b = 0; //宣告變數b為0
a = &b; //&會回傳位置,這行的意思是將a的位置設成b的位置
//換句話說,就是將a指向b
*a = 4; //*是指標專用的運算字,目的是設置該指標的值(而非位置)
//而因為a,b在同一位置上,所以b也會變成4
std::cout << *a << ' ' << b; //因為我們要輸出a 的值所以也要加*
//會輸出4 4而非4 0
}
```
也就是說,除了以變數的方式儲存資料,我們也可以用指標指向一塊地方後存資料。
但是,既然指標一開始不會被分配空間,那要怎麼使用?
這裡其實有兩種方式,但我們這邊只提到第二種,也就是陣列。
複習一下a[2]的模樣:
| 記憶體位置 | 0 | 1 |
| ---------- | ---- | ---- |
| 對應到的變數 | a[0] | a[1] |
| 指標的表示 | a | a+1 |
沒錯,陣列在宣告時會創建和變數同名的指標,之後給它分配空間。
而因為空間是連在一起的,所以沒有必要再創立一個指標,只要用原本的指標呼叫即可。
也就是說
```cpp=
#include<iostream>
int main(){
int a[2]; //同時會創建叫做a的指標
a[0] = 1; //第一項,也就是指標本身
a[1] = 3; //第二項,也就是指標後一塊
std::cout << *a << ' '; //先輸出指標本身,也就是第一項
std::cout << *(a+1); //再輸出第二項,也就是指標後一塊
//一定要加括號,否則會變成第一項的值加一
}
//輸出會是1 3
```
你可能會發現,a[1] 的呼叫方式剛好就是 *(a+1)。
沒錯,中括號內加的數字就是指標後幾格(宣告時例外,指的是要多少項)。
那你有沒有想過, a[-1] 會發生什麼?會是錯誤嗎?
試試這個:
```cpp=
#include<iostream>
int main(){
int a[2];
std::cout << a[-1];
//或你要寫
//std::cout<<*(a-1);
//也可以
}
//輸出是一個奇怪的數字,通常會很大
```
#### 你如果發現輸出是零,這是因為你的編譯器對你很好
當嘗試調出未初始化的記憶體空間時,它會回傳一個亂碼,
這也是為什麼會輸出亂碼的原因。
### 整理一下
- 陣列在宣告時會創立一個指標
- 之後會依據中括號的數值建立項數
- 在使用時[]表示的是指標的偏移
```cpp=
a[0] == *a
a[1] == *(a+1)
a[-1] == *(a-1)
```
---
## 常見的陣列
這裡其實在[之前的補充講義](https://hackmd.io/@Tony041010/ExtraResourcesVarAndComputing)有稍微的提到,在const那邊
```cpp=
"hello world" 的資料型態是const char[12]
//不知道為什麼是11+1的回去重看
"CRC" 的資料型態是const char[4]
```
---
## 一些有趣的用法
我現在要給你五個數字,請把他們依據除以3的餘數分類,
並輸出餘數分別為0,1,2的數量。
用if寫:
```cpp=
#include<iostream>
int main(){
int zero = 0;
int one = 0;
int two = 0;
int input;
int times = 5;
while(times--)
{
std::cin>>input;
switch(input%3){
case 0:
zero++;
break;
case 1:
one++;
break;
case 2:
two++;
break;
}
}
std::cout<<"餘數為零的有"<<zero<<"個"<<'\n';
std::cout<<"餘數為一的有"<<one<<"個"<<'\n';
std::cout<<"餘數為二的有"<<two<<"個"<<'\n';
}
```
雖然這題並不困難,但總覺得很麻煩不是嗎?
試試不用if寫寫看吧,你可能會想:這怎麼可能?
答案是可能的喔
:::spoiler 解答
```cpp=
#include<iostream>
int main(){
int input;
int times = 5;
int mod[3];
while(times--){
std::cin >> input;
mod[input % 3] ++;
}
std::cout<<"餘數為零的有"<<mod[0]<<"個"<<'\n';
std::cout<<"餘數為一的有"<<mod[1]<<"個"<<'\n';
std::cout<<"餘數為二的有"<<mod[2]<<"個"<<'\n';
}
```
:::
沒錯,中括號內的值不一定要是常數,所以可以用這種方式寫。
---
## 陣列的陣列
既然在之前有提到任何資料型態都有陣列,那有沒有陣列的陣列?
有,這被叫做二維陣列。
使用方式:
```cpp=
int a[2][2]; //宣告
a[0][0] = 1; //第一項的賦值
a[0][1] = 2; //第二項
a[1][0] = 3; //第三項
a[1][1] = 4; //第四項
```
為什麼會長這樣呢?
如果我們用大括號表示陣列
```cpp=
int a[2] = {1,2};
```
那這就是二維陣列的樣子
```cpp=
int a[2][2] = {{1,2},{3,4}};
int b[3][2] = {{1,2},{3,4},{5,6}};
```
也就是說,a的前面中括號指的是裡面有幾個一維陣列,
而第二個中括號則是每個一維陣列內有幾項。
所以也可以用這樣的方式表示(以a[2][2]當示範)
| 陣列\內容 | 第一項 | 第二項 |
| -------- | -------- | -------- |
| 第一個陣列 | 1 | 2 |
| 第二個陣列 | 3 | 4 |
所以,第二項是a[0][1],而第三項是a[1][0]
其實還有三維、四維等陣列,概念和二維陣列是相同的。
而至於他們的組成……
你有聽過…指標的指標嗎?