講師: 李根逸 (Ken-Yi Lee), E-mail: feis.tw@gmail.com
陣列與字串
【第六講】
!193
課程⼤大綱
陣列介紹 [P.195]
陣列的使⽤用 [1] - 多個同型變數 [P.196]
陣列的初始化 [P.198]
陣列的使⽤用 [2] - 循序存取 [P.199]
陣列的使⽤用 [3] - 隨機存取 [P.200]
陣列的複製 [P.203]
在函式間傳送陣列 [P.204]
字串 [P.208]
⼆二維陣列 [P.213]
!194
陣列
陣列是將⼀一群相同資料型態的變數放置在同⼀一個名稱 下的結構
在記憶體中,陣列會使⽤用⼀一段連續的空間
陣列宣告⽅方式:
資料型態 陣列名稱
[
元素個數];
陣列與⼀一般變數的差別 :
宣告⼀一般變數
:
int var;
!
宣告陣列
:
int var[3];
(int)
??
var
(int)
??
var[0]
(int)
??
var[1]
(int)
??
var[2]
!195
2293620 2293624 2293628
2293620
陣列的使⽤用 [1] : 多個同型變數
int a = 3;
int b = 5;
int c = 0;
int max = a;
if (b > max) { max = b;
} if (c > max) { max = c;
}
printf(“最⼤大值是 %d.\n”, max);
求三數的最⼤大值 (⼀一般版)
int v[3];
v[0] = 3;
v[1] = 5;
v[2] = 0;
int max = v[0];
if (v[1] > max) { max = v[1];
}
if (v[2] > max) { max = v[2];
}
printf(“最⼤大值是 %d.\n”, max);
求三數的最⼤大值 (陣列版1)
!196
【範例】max3_2.cpp
【範例】max3_1.cpp
【範例】公正骰⼦子統計
統計電腦⽤用亂數產⽣生 6000 次骰⼦子點數後,1 ~ 6 各點數分別出現了幾次。請試著⽤用陣列去改寫。
使⽤用 <stdlib.h> 裡的 rand() 函式產⽣生亂數 每次骰⼦子的點數可以⽤用 rand() % 6 + 1 得到
原本我們需要六個整數變數 (計數器) 去記錄各個點數 已經出現了幾次,現在可以⽤用⼀一個六元素的整數陣列
!197
【範例】dice_5.cpp
陣列的初始化
陣列宣告時具有⼀一個特殊的初始化語法 :
資料型態 陣列名稱
[
元素個數] = {第⼀一個元素的值, 第
⼆二個元素的值
, 第三個元素的值, ... , 最後⼀一個元素的
值};
在初始化時未指定值的元素會被⾃自動指定為 0
例如
:
!
!
在有初始化的時候,陣列的⻑⾧長度可以省略不寫,此時陣 列⻑⾧長度就是有初始化的個數
int v[3] = {3, 5};
v[0] v[1] v[2]
int v[] = {3, 5, 0};
(int)
3 (int)
5 (int)
0
!198
相當於 int v[3] = {3, 5, 0};
2293620 2293624 2293628
陣列的使⽤用 [2] : 循序存取
int v[3] = {3, 5, 0};
int max = v[0];
if (v[1] > max) { max = v[1];
}
if (v[2] > max) { max = v[2];
}
printf(“最⼤大值是 %d.\n”, max);
求三數的最⼤大值 (陣列版2)
int v[3] = {3, 5, 0};
int max = v[0];
for (int i = 1; i < 3; ++i) { if (v[i] > max) {
max = v[i];
} }
printf("最⼤大值是 %d.\n", max);
求三數的最⼤大值 (陣列 X 迴圈版)
!199
迴圈與陣列是絕配!使⽤用 陣列時請搭配迴圈服⽤用
因為陣列的索引是由 0 開始數,所以 迴圈的計數器範圍跟直覺會有所不同
【範例】dice_6.cpp
【範例】max_3.cpp
陣列可以做隨機存取,可以⽤用某個運算式來做索引:
陣列可以看成是⼀一個表格,我們利⽤用不同的索引來讀取 或寫⼊入表格内不同格⼦子的值
陣列的使⽤用 [3] : 隨機存取
!200
相當於⼀一⾏行 count[dice-1]++;
【範例】dice_7.cpp
if (dice == 1) { count[0]++; } if (dice == 2) { count[1]++; } if (dice == 3) { count[2]++; } if (dice == 4) { count[3]++; } if (dice == 5) { count[4]++; } if (dice == 6) { count[5]++; }
【範例】統計⾦金額
試寫⼀一程式,使⽤用陣列讓使⽤用者先輸⼊入賣出商品的總 數後,分別輸⼊入每個賣出商品的編號。輸⼊入完後,顯
⽰示賣出的總⾦金額
商品編號 商品⾦金額
1 90
2 75
3 83
4 89
5 71
請輸⼊入商品個數: 5 1 5
5 4 1
總共 411 元
!201
price[5]
【範例】coin.cpp
【範例】⻑⾧長條圖
試寫⼀一程式讓使⽤用者輸⼊入學⽣生個數後,接著分別輸⼊入 每個學⽣生的成績。輸⼊入完後,顯⽰示學⽣生成績的統計圖
:
級距為 (0~59, 60~69, 70~79, 80~89, 90~100)
請輸⼊入有幾位學⽣生: 5 第 1 位的成績是: 80 第 2 位的成績是: 85 第 3 位的成績是: 90 第 4 位的成績是: 95 第 5 位的成績是: 75 0~ 59 :
60~ 69 : 70~ 79 : * 80~ 89 : **
90~100 : **
!202
【範例】hist.cpp
陣列的複製
因為複製陣列需要⽐比較多的空間和時間,所以陣列型 態的資料並不能⽤用直接的⽤用指定運算⼦子複製:
!
!
!
!
真的要複製陣列需要使⽤用迴圈將元素⼀一個⼀一個複製 (deep copy) :
!
!
!
【補充】或者要使⽤用 <string.h> 內 memcpy() 這類的記憶體複 製函式:
int v[5] = {1, 2, 3, 4, 5};
int n[5];
n = v; // 語法錯誤
for (int i = 0; i < 5; ++i) { n[i] = v[i];
}
memcpy(n, v, 5*sizeof(int));
!203
【範例】copy.cpp
在函式間傳送陣列 [1]
我們可以將陣列作為函式參數,傳送陣列時⽤用陣列名 稱作為引數傳送:
!204
void print(int n[3]) {
for (int i = 0; i < 3; ++i) { printf(“%d ”, n[i]);
} }
int main() {
int v[3] = {1, 2, 3};
print(v);
printf(“\n”);
return 0;
}
【範例】print_1.cpp n 是 v 的複製品嗎?陣列可以直接複製嗎?
在函式間傳送陣列 [2]
在呼叫函式時,如果傳⼊入的引數是陣列,則陣列是不 會被複製的!
!205
void clear(int n[3]) {
for (int i = 0; i < 3; ++i) { n[i] = 0;
} }
int main() {
int v[3] = {1, 2, 3};
clear(v);
for (int i = 0; i < 3; ++i) { printf(“%d “, v[i]);
} printf(“\n”);
return 0;
} 【範例】clear.cpp
void clear(int n) { n = 0;
}
int main() { int v = 1;
clear(v);
printf(“%d\n”, v);
return 0;
}
因為沒有真的複製陣列,如果 陣列內容被修改的話,原本陣 列的內容就真的被修改了!
在函式間傳送陣列 [3]
要如何不⽤用真的複製陣列?
C / C++ 語⾔言設計讓陣列型態可以轉型為⼀一個指向陣 列起始元素的記憶體位址 (在後⾯面章節稱之為指標):
當函式呼叫要傳送陣列時,複製的是陣列起始元素位址
如果知道陣列元素的型態與起始元素的記憶體位址,那可以知道陣 列中任意元素的位址嗎?
⼀一維陣列作為函式參數可以不宣告⼤大⼩小:
存取到陣列實際陣列⼤大⼩小以外的是未定義⾏行為
(int)
??
var[0]
(int)
??
var[1]
(int)
??
var[2]
2293620
(int) var == 2293620
!206
2293624 2293628
int var[3];
void print(int n[]);
【範例】print_3.cpp
【範例】print_2.cpp
【範例】求最⼤大值
試寫⼀一個可以讓使⽤用者輸⼊入五個整數,回傳最⼤大值的 函式 maxv1:
int
maxv1( int [5] );試寫⼀一個可以讓使⽤用者輸⼊入任意個整數,回傳最⼤大值 的函式 maxv2:
int
maxv2( int [], int );!207
【思考】為什麼不⽤用指定陣列⼤大⼩小,還有 為什麼我們要傳⼊入參數 N 來表⽰示⼤大⼩小?
【範例】max.cpp
字串 (string)
C 字串是⼀一種⽤用 ‘\0’ 表⽰示結尾的字元 (char) 陣列
字元常數表⽰示語法 (單引號): ‘A’
字串初始化時表⽰示語法 (雙引號): “ABC”
“ABC” 相等於 { ‘A’, ‘B’, ‘C’, ‘\0’ } (\0 字元表⽰示字串結尾)
宣告字串變數 (字元陣列) :
char 字串名稱[字元陣列⻑⾧長度] = “字串內容” ; 在有初始化的時候,字串的⻑⾧長度可以省略不寫
字元陣列的⻑⾧長度應該是字串內容的⻑⾧長度加⼀一 (因為要加上⼀一個 ‘\0’)
使⽤用 scanf 與 printf 處理字串
scanf 跟 printf 可以⽤用 %s 來讀取或顯⽰示字串
char string[] = “Hello world”;
printf(“%s”, string);
scanf(“%s”, string);
當 scanf 讀⼊入字串時,遇到 空⽩白就會以為是字串的結尾
(char)
‘A’ (char) ‘B’ (char) ‘C’ (char) ‘\0’
!208
[使⽤用 scanf 時不⽤用 &]
2293620...21 ...22...23
【思考】為什麼要
⽤用 ‘\0’ 表⽰示結尾?
【範例】字串⻑⾧長度
試寫⼀一程式讀取⼀一字串後顯⽰示該字串的⻑⾧長度
‘\0’ 字元表⽰示字串結束
‘\0’ 表⽰示編號為 0 號的字元,在 C 稱為 NULL 字元
!209
請輸⼊入⼀一個字串 (最⼤大⻑⾧長度為 100): len
“len” 的⻑⾧長度為 3
【範例】strlen.cpp
【補充】 C 字串函式庫
C 字串函式庫 <string.h> 中提供了許多字串處理 相關的函式 :
字串⼀一 strcpy(字串⼀一, 字串⼆二)
將字串⼆二的內容複製到字串⼀一內
字串⼀一 strcat(字串⼀一, 字串⼆二)
將字串⼆二的內容銜接在字串⼀一後⾯面
int strcmp(字串⼀一, 字串⼆二)
此函式會⽐比較字串⼀一與字串⼆二的⼤大⼩小,相等時回傳 0,字串⼀一較⼤大 時回傳正值,字串⼀一較⼩小時回傳負值
size_t strlen(字串)
此函式會回傳字串的⻑⾧長度
字串的結尾必定是 ‘\0’ 字元,不包含在⻑⾧長度內。
!210
【範例】成績輸⼊入
試寫⼀一程式,讓使⽤用者分別輸⼊入學⽣生的英⽂文姓名與成 績,不停輸⼊入直到當姓名為 quit 時結束,顯⽰示最⾼高 分的學⽣生名字與成績。
Please enter the name : John Please enter the grade: 80 Please enter the name : Mary Please enter the grade: 70
Please enter the name : Stephen Please enter the grade: 90
Please enter the name : David Please enter the grade: 60
Please enter the name : quit Best: Stephen (90)
!211
【範例】max_grade.cpp
【補充】排序
排序 (sort) 是⼀一個常⾒見的⼯工作。要對⼀一個陣列內容 排序有很多種作法。我們下⾯面⽰示範其中⼀一種:
交換或選擇排序法:每次將最⼩小的值換到最左邊
!212
9 7 5 10 3 8 4
3 7 5 10 9 8 4
3 4 5 10 9 8 7
3 4 5 10 9 8 7
i=0 i=1 i=2 i=3 ...
【範例】sort_1.cpp
⼆二維陣列
⼆二維陣列就是⼀一維陣列的陣列
⼆二維陣列跟⼀一維陣列的差別 :
int
var[3] ;!
!
int var[2][3];
(int)
??
var[0] var[1] var[2]
(int)
?? (int)
??
(int)
??
var[0][0] var[0][1] var[0][2]
(int)
??
(int)
??
(int)
??
var[1][0] var[1][1] var[1][2]
(int)
??
(int)
??
(int) var == 2293620
(int) var == 2293620 (int)var[0] == 2293620 (int)var[1] == 2293632
!213
2293620 2293624 2293628
2293620 2293624 2293628
2293632 2293636 2293640
【範例】洗牌與發牌
試寫⼀一程式,將 52 張撲克牌洗牌後依序印出
我們⽤用 S, H, D, C 分別代表四個花⾊色
D12 H1 D4 C3 ...
!214
【範例】card.cpp
習題 [1]
[E0601] 請寫⼀一個程式,讓使⽤用者先依序照座號輸
⼊入⼗十名學⽣生的成績,接著再依序輸⼊入⼀一⼩小⼀一⼤大的整數 後印出所有成績在那兩整數之間的學⽣生座號與成績 [E0602] 請寫⼀一個程式,讓使⽤用者先依序照座號輸
⼊入⼗十名學⽣生的成績,接著再輸⼊入⼀一個整數,請找出所 有成績最接近該數的學⽣生座號與成績 (超過⼀一⼈人時顯
⽰示座號最⼩小的即可)
[E0603] 試寫⼀一程式,讓使⽤用者先輸⼊入三名學⽣生的 英⽂文姓⽒氏與成績。接著當使⽤用者輸⼊入任意學⽣生的英⽂文 姓⽒氏後,顯⽰示該姓學⽣生的成績。如果使⽤用者輸⼊入的英
⽂文 姓 ⽒氏 不 在 名 單 中 , 則 顯 ⽰示 『 找 不 到 』 ( N o t found.)
!215
習題 [2]
[E0604] 試⽤用陣列撰寫⽇日曆顯⽰示程式,讓使⽤用者輸
⼊入某個⽉月份後,顯⽰示⻄西元 2012 該⽉月份的⽇日曆
2012 年 1 ⽉月 1 ⽇日是星期⽇日,2 ⽉月有 29 天
請輸⼊入⽉月份 (1~12): 3 ⽇日 ⼀一 ⼆二 三 四 五 六 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
!216
習題 [3]
[E0605] 試寫⼀一程式,讓兩個玩家輪流玩井字遊戲
123 A B
C Player #1:
Please enter your choice: 2A 123
A O B
C Player #2:
Please enter your choice: 2B
123 A O B X C
!217
習題 [4]
[E0606] 試寫⼀一程式,亂數產⽣生 5 個 0 ~ 9 之間 不重複的整數
[E0607] 試寫⼀一程式,讓使⽤用者輸⼊入⾃自⼰己的英⽂文名 字後,將所有⼩小寫英⽂文字⺟母轉成⼤大寫後顯⽰示
!
提⽰示 :
‘\0’ 字元表⽰示字串結尾
只要⽐比對字元的值是否在 ‘a’ 與 ‘z’ 之間即可知道是否是⼩小寫字⺟母 也可以⽤用 <ctype.h> 內的 islower() 函式
要將⼀一字元從⼩小寫英⽂文字⺟母轉成⼤大寫英⽂文字⺟母可以將 該字元 - ‘a’
+ ‘A’
也可以⽤用 <ctype.h> 內的 toupper() 函式
!218
Please enter your name: Ken-Yi Lee Hello, KEN-YI LEE !