--- 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] 其實還有三維、四維等陣列,概念和二維陣列是相同的。 而至於他們的組成…… 你有聽過…指標的指標嗎?