【不是惡夢的開始,而是再度的精進。】
我們已經學會了如何宣告並初值化基本型態的變數,每個變數可以儲存特定型態的單一資料項,
就是我們可以儲存整數、浮點數的變數,或是儲存字元的變數,而陣列則可以儲存相同型態的數個資料項,可以是整數陣列、浮點數陣列或是字元陣列。
或許你會說難道我不可以直接設許多個變數來儲存一系列數值嗎?
答案是可以的,只是會造成複雜的程式碼,不容易事後閱讀,或者說是不夠精簡。
又或者說當你要記錄某種關係的變化時,使用陣列來儲存時,只要移動索引值就可以找到需要的資料,但是用變數儲存資料時就不容易處理了,譬如記錄每分鐘的溫度,你就可以很容易的改變一下索引值來找尋某時間點的溫度。
宣告一個陣列,其格式如下:
資料型態 陣列名稱[個數];
例:
int heighti[5];
int 表示這是一個儲存整數值的陣列
heighti 表示這個陣列的名稱,就像是變數名稱一樣
5 表示這個陣列有5個元素,但這5個元素的順序是從0到4,也表示陣列的大小。
例:heighti[0] 就是第一個元素,heighti[4] 就是第五個元素,而這0到4的整數
就是索引值,為什麼索引值會從0開始呢?因為索引值所表示的是它離第一個元素的距離,
所以第一個元素離第一個元素的距離就是0,第二個元素離第一個元素的距離就是1,
以此類推。
所以當你宣告 int heighti[5]; 時,編譯器就會配置5個連續儲存整數型態的值的空間出來,至於佔用記憶體到底是多大呢?就看你的電腦在 int 型態的值需要多少個位元組了,若你的電腦在 int 型態的值需要4個位元組,那麼這個陣列就會佔用20個位元組。
例:求班上學生的平均身高
#include<iostream>
using namespace std;
int main(int argc, char** argv)
{
int heighti[5];
int count = 0;
char yn = 0;
do
{
cout << "\n 輸入以公分整數值為計的身高 : ";
cin >> heighti[count++];
cout << "\n 繼續輸入嗎? (y 或 n )";
cin >> yn;
}while(count < 5 && tolower(yn) == 'y');
if(count == 5)
cout << "\n 已達可輸入的限制 \n";
double averaged = 0.0;
for(int i = 0; i < count; i++)
averaged += heighti[i];
averaged /= count;
cout << "\n 平均身高為: " << averaged << "公分 \n";
return 0;
}
我們再回頭看一下 int heighti[5]; 這當中的數字5,一般會被程式老鳥稱為神秘數字。
雖然程式是你寫的,可是多年以後你再回頭看這程式時,恐怕你也要猜一下5是什麼?
或許我們可以註解一下
int heighti[5]; // 陣列最大範圍為5
但....
while(count < 5 && tolower(yn) == 'y');
if(count == 5)
這兩行你可能又要想一下5代表什麼?尤其是當程式變得很龐大時...((( 誰能告訴我5是什麼!!! )))
這時候我們可以將陣列的大小宣告並初值化為常數
const int totalstudents = 5;
就用totalstudents 取代5,這樣整個程式是不是就明朗多了,當然啦,這個 totalstudents 要取什麼名稱就隨你高興囉。
修改後的程式碼如下:
#include<iostream>
using namespace std;
int main(int argc, char** argv)
{
const int totalstudents = 5;
int heighti[totalstudents];
int count = 0;
char yn = 0;
do
{
cout << "\n 輸入以公分整數值為計的身高 : ";
cin >> heighti[count++];
cout << "\n 繼續輸入嗎? (y 或 n )";
cin >> yn;
}while(count < totalstudents && tolower(yn) == 'y');
if(count == totalstudents)
cout << "\n 已達可輸入的限制 \n";
double averaged = 0.0;
for(int i = 0; i < count; i++)
averaged += heighti[i];
averaged /= count;
cout << "\n 平均身高為: " << averaged << "公分 \n";
return 0;
}
執行結果:
輸入以公分整數值為計的身高 : 125
繼續輸入嗎? (y 或 n )y
輸入以公分整數值為計的身高 : 133
繼續輸入嗎? (y 或 n )y
輸入以公分整數值為計的身高 : 142
繼續輸入嗎? (y 或 n )y
輸入以公分整數值為計的身高 : 127
繼續輸入嗎? (y 或 n )y
輸入以公分整數值為計的身高 : 139
繼續輸入嗎? (y 或 n )y
已達可輸入的限制
平均身高為: 133.2公分
還有要注意一點,int heighti[totalstudents];的宣告是沒有給定初始值,因此會有5個記憶體內的殘留值,至於殘留值是多少呢?無法預估。你可以寫個迴圈給印出來,就知道無意義的殘留值是多少了。
建議先給個預設值,int heighti[totalstudents] = {0};
把你宣告的陣列清空殘留值,你也可以驗證一下有沒有清空殘留值。
當陣列遇上 while 迴圈或 do-while 迴圈時或許就有陣列界線的問題了,好巧的是 c++ 並不會檢查索引值的大小,也就是說當索引值超過陣列長度時,糟糕的是 c++ 並不會因此不讓使用者繼續使用該陣列,而是將多餘的資料放在陣列以外的記憶體中,如此一來很可能蓋掉其他的資料或是程式碼,事情就大條了。
因為這種錯誤是在程式執行時才會發生的,因此編譯器程式是無法提出任何的警告,所以陣列界線的檢查就落在程式工程師的身上了。
上面的範例中我們將 count 當作陣列的索引值使用,
while(count < totalstudents && tolower(yn) == 'y');
因此就算沒有
if(count == totalstudents)
cout << "\n 已達可輸入的限制 \n";
這兩行程式碼的提示也不會出問題。
如果 while() 迴圈檢查並不是陣列索引值而是陣列內容時,就很容易產生超出陣列長度了,
這時候就需要在 while() 迴圈內加入陣列大小的檢查。
if(count == totalstudents)
{
cout << "\n 已達可輸入的限制 \n";
break;
}
另外,我們可以用初值串列來定義陣列大小,讓編譯器來幫你決定陣列的大小。其格式如下:
資料型態 陣列名稱[] = {初值0, 初值1, ... , 初值n-1};
例:
int valuei[] = {2,7,5,3};
這樣就等同 int valuei[4] = {2,7,5,3};
一旦讓編譯器來決定陣列大小的時候,我們可以用 sizeof valuei / sizeof valuei[0]; 求出陣列大小。
如果你一開始就宣告陣列大小了,例如 int valuei[7];而你給的初值個數不是7個的時候,會發生甚麼是呢?
當初值個數比宣告的陣列元素少,剩餘為這值的空間會填入0,當初值個數比宣告的陣列元素多,
編譯器則會出現警告或是錯誤訊息。
我們再來看一例:找最大值與最小值
#include<iostream>
using namespace std;
int main(int argc, char** argv)
{
int valuei[] = {2,7,5,3};
int i = 0;
int min = valuei[0], max = valuei[0];
int length = sizeof(valuei) / sizeof(valuei[0]);
cout << "陣列內元素 : ";
for(i = 0; i < length; i++)
{
cout << valuei[i] << " ";
if(valuei[i] > max)
max = valuei[i];
if(valuei[i] < min)
min = valuei[i];
}
cout << "\n\n 最大值為: " << max;
cout << "\n\n 最小值為: " << min << "\n\n";
system("pause");
return 0;
}
執行結果:
陣列內元素 : 2 7 5 3
最大值為: 7
最小值為: 2
我們再來看另一例:修改陣列內某元素的值
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
int main(int argc, char** argv)
{
int a[] = { 18, 25, 31, 42, 55, 63};
int b = sizeof(a) / sizeof(a[0]);
char str[10];
memset(str, 0, sizeof(str)); //全部的值都是 0
cout << "輸入要刪除的陣列第幾個元素 = ";
gets(str);
cout << "\n原始陣列:\n";
for(int k = 0; k < b; k++)
{
cout << a[k];
if(k < (b - 1))
cout << ",";
}
cout << "\n\n";
int c = atoi(str);
a[c - 1] = 0; // 將陣列內某元素的值 重設定為 0
cout << "新陣列:\n";
for(int p = 0; p < b; p++)
{
cout << a[p];
if(p < (b - 1))
cout << ",";
}
cout << "\n\n";
return 0;
}
執行結果:
輸入要刪除的陣列第幾個元素 = 2
原始陣列:
18,25,31,42,55,63
新陣列:
18,0,31,42,55,63
字元陣列
字元型態的陣列有雙重的特性
1.它可以只是單純的字元陣列,每個元素儲存一個字元
例: char grade[5] = {'a','b','c','d','e'};
2.它也可以代表一個字串,字串的每個字元會儲存在不同的陣列元素中,而且在字串結束時會用
一個特殊的字串結束字元'\0',這個結束字元稱為空字串。
例: char name[11] = "Boris West";
這個幾乎可以看成 char name[11] = {'B','o','r','i','s','','W','e','s','t','\0'};
我們再來對比一次
char grade[5] = {'a','b','c','d','e'};
char name[11] = "Boris West";
單純的字元陣列會有一對大括號{},還有單引號''以及用逗號分隔。
而代表字串時只需要用一對雙引號""括住就可以了,而系統會自動地補上空字串。
所以 char name[] = {'a'}; 和 char name[] = "a"; 所佔的記憶體空間是不一樣的。
當 char name[] 代表一個字串時,它不僅可以儲存英文名字,也可以儲存中文名字
例:
#include <iostream>
using namespace std;
int main() {
char name[] = "許功蓋 林嘉賓";
cout << name << "\n";
cout << "\nname陣列大小為: " << sizeof(name) << "\n";
return 0;
}
執行結果:
許功蓋 林嘉賓
name陣列大小為: 14
為什麼 name陣列大小為: 14。因為一個中文字佔兩個位元,加上有一個空格,以及字串結束字元'\0'。
如果你執行時沒辦法顯示出"許功蓋 林嘉賓",而有亂碼產生時,
請參考
中文亂碼的解決方法 傳說中的許功蓋
這篇
多維陣列
我們所宣告的陣列如果只有一個索引值來選擇元素的,稱為一維陣列。然而我們也可以宣告兩個以上的索引值來參考元素的陣列,就稱為多維陣列。若我們宣告兩個索引值來參考元素的陣列就稱為二維陣列。
宣告一個二維陣列,其格式如下:
資料型態 陣列名稱[個數][個數];
例: int record[30][5];這樣就可以登錄30個學生的五科成績。
int students = 30;
int subject = 5;
int record[students][subject]=
{
{85,92,73,81,96},
{68,76,84,92,96},
.....
};
例:int box[][3];就可以登錄盒子的長寬高尺寸了
int size = 3;
int box[][size] =
{
{300,250,700},
{350,180,500),
{620,330,900},....
};
多維陣列也可以是多維字元陣列,常常會用來儲存名字
例: char membership[][80] =
{
"Alison",
"Diane",
"Iris",
"Kaia",
"Matthew",
"Liam",
"Rod",
....
}
.
留言列表