CodeGym /課程 /C# SELF /進階陣列操作

進階陣列操作

C# SELF
等級 7, 課堂 2
開放

1. string 類型的陣列

簡單說一下 string 類型的陣列。

就像之前說過的,陣列可以是任何型別。所以你可以建立 string 類型的陣列。假如我們要寫一個「從鍵盤輸入 10 行字串並反向輸出」的程式,程式碼大概會長這樣:

string[] array = new string[10];	// 建立一個 10 個元素的陣列物件
for (int i = 0; i < 10; i++)		// 0 到 9 的迴圈
{
   array[i] = Console.ReadLine();	// 從鍵盤讀取一行字串,存進陣列
}
for (int i = 9; i >= 0; i--)		// 9 到 0 的迴圈
{
    Console.WriteLine(array[i]);	// 輸出陣列的每個元素
}

程式碼幾乎沒變!只要把陣列型別從 int 換成 string 就好。其他都一樣!

2. string 類型的陣列在記憶體中的樣子

還有一個實用小知識。來看三張圖:

圖 1. string 物件在記憶體中的樣子:

string 類型的陣列在記憶體中的樣子

注意,字串的內容不是直接存在變數裡:它會被分配到一塊獨立的記憶體空間。而 string 型別的變數裡存的是指向那個物件的位址(參考)。

圖 2. int 陣列在記憶體中的樣子:

int array in memory

這張圖你應該也看過。

圖 3. string 陣列在記憶體中的樣子:

How a string array is stored in memory

左邊是 string[] 陣列變數(存的是陣列物件的位址)。
中間是 string 類型的陣列物件。
右邊是存放文字的 string 物件。

string 類型的陣列物件裡存的不是字串本身(內容),而是它們的位址(參考)。就像 string 變數裡存的是字串(內容)的位址一樣。

3. C# 陣列的快速初始化

陣列超級實用,所以 C# 的開發者讓它用起來超方便。首先就是 簡化陣列初始化,也就是一開始就給它一些值。

很多時候,除了從外部讀取資料,程式還需要一些內部資料。比如我們要存每個月的天數,程式碼可以這樣寫:

int[] months = new int[12];
months[0] = 31; // 一月
months[1] = 28; // 二月
months[2] = 31; // 三月
months[3] = 30; // 四月
months[4] = 31; // 五月
months[5] = 30; // 六月
months[6] = 31; // 七月
months[7] = 31; // 八月
months[8] = 30; // 九月
months[9] = 31; // 十月
months[10] = 30; // 十一月
months[11] = 31; // 十二月

但其實有更短的寫法——感謝 C# 的設計師:

// 一年每個月的天數
int[] months = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

直接用逗號把所有值列出來就好!

是不是很方便?但還不只這樣。

其實編譯器可以根據變數型別自動判斷陣列的型別,長度也會自動算(看你大括號裡有幾個元素)。

所以這段程式碼還可以更短:

// 一年每個月的天數
int[] months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

是不是很美?🙂

這種寫法叫「快速初始化陣列」。而且不只 int 型別可以這樣用...

// 一年每個月的名稱
string[] months = { "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"};

4. foreach 迴圈

遍歷陣列所有元素超常見,所以有專門的迴圈!它叫 foreach,寫法長這樣:


foreach (int score in scores)
{
    Console.WriteLine($"分數: {score}");
}
        
用 foreach 遍歷陣列元素

這個迴圈會自動幫你遍歷元素——不用自己寫 index。不能直接改元素(在迴圈裡只能讀),但要單純「走一遍」超方便。

編譯器會把 foreach 迴圈轉成下面這種寫法:


for (int i = 0; i < scores.Length; i++)
{
    int score = scores[i];
    Console.WriteLine($"分數: {score}");
}
        
foreach 背後的原理

所以沒什麼魔法啦。這也解釋了為什麼你不能用 score 變數直接改陣列內容。

再來一個例子:算所有分數的總和。


int sum = 0;
foreach (int score in scores)
{
    sum += score;
}
Console.WriteLine($"所有分數總和: {sum}");
        
用 foreach 累加陣列元素

什麼時候用哪種迴圈?

  • 如果 需要 index 或 要改元素——用 for 迴圈。
  • 如果只是單純走一遍——foreach 比較快又乾淨。

5. 從後面開始的索引:^ 運算子

最近 C# 可以直接從陣列尾端取元素。以前如果要拿最後一個元素,要這樣寫:


int lastScore = scores[scores.Length - 1];
        
傳統寫法取得最後一個元素

現在更簡單了。只要在 index 前面加 ^,就會從後面數:


int lastScore = scores[^1];        // 最後一個元素
int penultimate = scores[^2];      // 倒數第二個元素
        
用 ^ 從後面開始索引
  • ^1 —— 倒數第一個(最後一個)。
  • ^2 —— 倒數第二個。

視覺化

索引 scores 陣列的值 一般索引 從後面開始的索引
0 10 0 ^5
1 15 1 ^4
2 8 2 ^3
3 22 3 ^2
4 17 4 ^1

score[^1] = 17, score[^2] = 22 —— 超直觀。

注意! 從後面開始的索引是從 1 開始,不是從 0。

6. 範圍:.. (range) 運算子

有時候你會想從原本的陣列裡拿出一個 子陣列。C# 有個超方便的範圍運算子 ..,可以很簡潔地拿到你要的「slice」。

這種子陣列有時候叫 slice(英文就是 slice),它有兩個邊界:開始跟結束。
重點: 右邊界不包含在結果裡!

範例


int[] scores = { 10, 15, 8, 22, 17 };
int[] top3 = scores[0..3]; // 取 0 到 2 的元素: {10, 15, 8}
        
用 .. 取得陣列 slice

  • scores[0..3] —— 取 index 0(包含)到 index 3(不包含)。
  • 結果陣列會有 index 0、1、2 的元素。

簡寫

如果 沒寫左邊界,就是從頭開始:


int[] firstTwo = scores[..2]; // 跟 scores[0..2] 一樣,就是 {10, 15}
        

如果 沒寫右邊界,就是到最後:


int[] fromThird = scores[2..]; // {8, 22, 17}
        

負索引

用 ^ 可以從後面開始算 index:


int[] lastTwo = scores[^2..]; // {22, 17}
        

這裡 ^2 就是「倒數第二個」。

你也可以兩邊都用負索引,拿一段範圍:


int[] mid = scores[1..^1]; // {15, 8, 22}(從第二個到倒數第二個)
        

要記得什麼?

  • 右邊界永遠不包含——寫 a..b,就是拿 a 到 b-1。
  • 如果邊界超出陣列範圍,會丟出 IndexOutOfRangeException。
  • slice 是 新陣列,原本的沒變。你改新陣列不會影響原本的。

實用範例

int[] arr = { 1, 2, 3, 4, 5, 6, 7 };
int[] arr = { 1, 2, 3, 4, 5, 6, 7 };

int[] start = arr[..3];      // {1, 2, 3}
int[] end = arr[^2..];       // {6, 7}
int[] middle = arr[2..5];    // {3, 4, 5}
int[] all = arr[..];         // {1, 2, 3, 4, 5, 6, 7}(完整複製)
2
任務
C# SELF, 等級 7, 課堂 2
上鎖
使用從結尾開始的索引和範圍運算子
使用從結尾開始的索引和範圍運算子
2
任務
C# SELF, 等級 7, 課堂 2
上鎖
字串陣列反轉與切片
字串陣列反轉與切片
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION