# 一維陣列、字串與字元 ## 一維陣列 ### 宣告一維陣列 我們都學過`int`, `string`, `long long`...等,資料儲存類型,這些的特點都是**一個變數只能存一筆資料**。但如果今天有很多筆資料,像是有40筆資料,總不可能宣告40個變數來儲存,<span style="color:red; font-weight:bold">超級麻煩</span>。 所以有一個資料結構是**陣列(array)**,宣告一個以**索引(index)作為識別**,宣告方式: ``` 資料型別 陣列名稱[n] = {預設值}; ``` 他會開好一個可以**存n筆資料的陣列**,固定大小為n**不可修改**,裡面存的資料型別都要跟宣告的型別**一樣**。 可以修改大小的是vector (動態陣列)。 ------- 像這樣宣告大小同為10,但型別不同的陣列: ```cpp= int number[10]; // 宣告 10 個元素的整數陣列 double score[10]; // 宣告 10 個元素的浮點數陣列 char ascii[10]; // 宣告 10 個元素的字元陣列 ``` 也可以後面接大括號直接**預設初始值**,後面接的資料比數不可以超過宣告的大小: ```cpp= int number[5] = {0, 1, 2, 3, 4}; double score[5] = {87.0, 78.0, 99.5, 69.5, 82.5}; char ascii[5] = {'A', 'B', 'C', 'D', 'E'}; ``` ------- 如果宣告大小為5的陣列,只預設前兩項值,則後面空房間都會預設為**0**或**空字串**、**空字元**: ```cpp= int number[5] = {0, 1}; // 實際長這樣 {0, 1, 0, 0, 0}; double score[5] = {87.0, 78.0, 99.5}; // {87.0, 78.0, 99.5, 0.0, 0.0}; char ascii[5] = {'A', 'B', 'C', 'D'}; // {'A', 'B', 'C', 'D', '\0'}; ``` 或是今天想要預設每一格值都是一樣的值,大括號包**一筆資料**: ```cpp= int number[10] = {0}; // 十個位置都是0 double score[10] = {0.0}; char ascii[10] = {'\0'}; // '\0' 空字元 ``` 但如果今天: ```cpp= int number[10] = {1}; // 他會變成 {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ``` ------- 你今天懶得宣告大小也可以**不宣告大小**直接**設初始值**,編譯器會自己判斷大小: ```cpp= int number[] = {0, 1, 2, 3, 4}; // 自動宣告大小為 5 的陣列 double score[] = {87.0, 78.0, 99.5, 69.5, 82.5}; char ascii[] = {'A', 'B', 'C', 'D', 'E'}; ``` ### 索引值 陣列的每一個房間都有**自己的編號**,第一間房間(元素)編號是0、第二間是1...依此類推,第n間房間編號是n-1。這種陣列稱為**0-based陣列(零基索引陣列)**。 當要存取陣列中的值時,可以使用 **下標(Subscript運算子)[]** 加上 **索引(Index)**,指定要存取的陣列元素,如下。 ![image](https://hackmd.io/_uploads/Byla4akf-g.png) 下面這個簡單的程式示範如何**使用索引存取陣列元素**: ```cpp= #include<iostream> using namespace std; int main() { int n; cin >> n; // 有 n 筆資料 int a[n]; for (int i=0; i<n; i++) { cin >> a[i]; // 從 0 數到 n-1 對應索引值 } for (int i=0; i<n; i++) { cout << a[i] << ' '; // 輸出加空格 } cout << endl; // 換行 } ``` ### 小練習 班上有40個同學,小明想要知道11號到20號的分數各是多少,以及這十位同學的平均分數,請你幫幫他吧,每個人的分數用空格分開,第二行輸出一個數字,代表十位同學的平均分數。 * 輸入為一行含有40個數字,代表班上1~40號的分數,之後是一個換行字元。 * 請輸出11號到20號的分數,每個分數空格分開,第二行輸出一個**浮點數**,代表十位同學的平均分數。 輸入: ```cpp= 95 11 38 21 67 31 14 82 12 55 22 90 32 49 70 13 12 23 25 33 88 64 14 79 24 41 34 73 99 15 38 29 47 19 10 29 69 39 53 12 ``` 輸出: ```cpp= 22 90 32 49 70 13 12 23 25 33 36.9 ``` <details> <summary>解答</summary> ```cpp= #include<iostream> using namespace std; int main() { int a[40], total = 0; for (int i=0; i<40; i++) { cin >> a[i]; } for (int i=10; i<20; i++) { cout << a[i] << ' '; total += a[i]; } cout << endl; double ans = total / 10.0; cout << ans << endl; } ``` </details> ## 字串與字元 ### 宣告 **字元(char)** 顧名思義,他是一個元素,只能有**一個字**,字元以**單引號**表示` ' ' `。 ```cpp= 'A' 'B' '0' 'w' '\n' ... ``` 而**字串(string)** 跟**陣列**很像,每個字串都是一間一間的房間組成,而每一間房間都**存著一個字元**,所以字串就是一連串的字元組合而成。字串以**雙引號**表示` " " `。 字串幾乎**沒有長度限制**,就算有幾億個字元也都可以存,所以超過`int`, `long long`範圍的數字可以用`string`存。 ```cpp= "hello world" "apple" "A" ... ``` 因此字串也可以索引存取值、也是**0-based**,如下。 ![image](https://hackmd.io/_uploads/SJd8i7SMWx.png) 字元A跟字串A雖然輸出長一樣,但他們型別不同。 字串是字元陣列,而且尾端會有`\0`**(null terminator)**,所以實際上字串A有兩個字元。 ```cpp= char c = 'A'; string s = "A"; cout << c << ' ' << s; // 輸出 A A ``` ------- 字元aaa是不合法的 **(multi-character character constant)**,在 C++ 標準裡,這種寫法雖然有定義(會轉成一個整數值),但幾乎沒人用,因為不可讀又不直覺。 ```cpp= char c = 'aaa'; cout << c; // 可能會輸出 6381921 ``` ### string的索引值 因為string的每個房間都是char,所以當我們索引取值的時候: ```cpp= string s = "hello"; char c = s[0]; // 取出之值是字元 cout << c; // 輸出 字元h ``` 也可以直接修改字串: ```cpp= string s = "Hello World"; s[0] = 'h'; // 用字元修改 s[6] = 'w'; cout << s; // 輸出 hello world ``` ### 字串長度 C++ 有提供一個函式,可以取得字串有效字元的長度,使用方法: ```cpp= int len = 字串變數名稱.length(); ``` 字串尾端的` \0 `不是有效字元,所以不會計入長度。 ```cpp= string s = "apple"; cout << s.length(); // 輸出 5 ``` ------- 所以今天想要知道字串最後一個字元是什麼,可以: ```cpp= string s = "world"; cout << s[s.length()-1]; // 0-based 最後一項是 字串長度-1 // 輸出 d ``` ### 小練習 小美想要練習翻轉字串,輸入一個字串,把字串全部倒過來。 * 第一行輸入一個n,代表有n筆字串,接下來有n行,每行皆有一個字串,保證不包含空格。 * 輸出有n行,1~n行代表第幾筆翻轉過的字串。 > hint: 使用for迴圈從字串尾端遍歷回去。 輸入: ```cpp= 3 i like school ``` 輸出: ```cpp= i ekil loohcs ``` <details> <summary>解答</summary> ```cpp= #include<iostream> using namespace std; int main() { int n; cin >> n; for (int i=0; i<n; i++) { string s; cin >> s; for (int j=s.length()-1; j>=0; j--) { cout << s[j]; } cout << endl; } } ``` </details> ## ASCII ### ASCII 是什麼 **ASCII(American Standard Code for Information Interchange)** 是一種**字元編碼標準**,把字元對應到數字。範圍0~127,共**128個編碼**。 - 例如: - 'A' → 65 - 'a' → 97 - '0' → 48 - '\n' → 10 (換行) ![IMG_1851](https://hackmd.io/_uploads/Hkx55NBzbe.png) ### 取得字元ASCII值 隱式轉型: ```cpp= char c = 'A'; int ascii = c; cout << ascii; // 輸出 65 ``` 顯式轉型: ```cpp= #include<iostream> using namespace std; int main() { cout << (int)'A'; // 輸出 65 } ``` ### ASCII 的應用 1. 字母轉大寫 小寫字母-32會變成大寫(大小寫差值)。 ```cpp= char c = 'a'; char upper = c - 32; // 'A' ``` 2. 字母轉小寫 反過來是+32。 ```cpp= char c = 'A'; char lower = c + 32; // 'a' ``` 3. 判斷是否是數字 如果字元c的ascii介於0~9的ascii值之間,則為數字。 ```cpp= char c = '7'; if (c >= '0' && c <= '9') { cout << "digit"; } else { cout << "not digit"; } // 輸出digit ``` 4. 字母差值 a~c的距離為2 ```cpp= cout << ('c' - 'a'); // 輸出 2 ``` 5. 字元數字轉整數 數字ascii差值剛好為實際數字 ```cpp= char c = '9'; cout << c - '0'; // 輸出 9 ``` ### 小練習 智明想要知道一個數字所有位數的值加起來是多少,請你幫他計算。 > 這個數字會有700位數,請使用字串處理。 * 輸入僅有一個數字。 * 輸出此數字所有位數之合。 輸入: ```cpp= 8319735824609581720364957201948756109238745610928374561029384756109283745610928374561092837456109283746501928374659102837465908172364598127364598172364598172364598172364589712364589712364589712364589712364589712364589712364589712364589712364589712364589712364589712364589712364589712364589712364589712364589765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321012345678901234567890123456789012345678901234567890123456789012345678901789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 ``` 輸出: ```cpp= 3249 ``` <details> <summary>解答</summary> ```cpp= #include<iostream> using namespace std; int main() { string s; cin >> s; int total = 0; for (int i=0; i<s.length(); i++) { total += s[i] - '0'; } cout << total << endl; } ``` </details> ## Zerojudge [c290. APCS 2017-0304-1秘密差](https://zerojudge.tw/ShowProblem?problemid=c290) [g595. 1. 修補圍籬](https://zerojudge.tw/ShowProblem?problemid=g595)