在上一篇文章中,我們介紹了「陣列」的基本使用方式。本篇將帶你深入探討 C 語言中字串的運作原理,了解如何以陣列形式儲存字串。此外,我們還會介紹如何將英文字母透過 ASCII 表轉換成數值,並說明其在電腦中的實際應用。最後,我們將解析 Command Line Argument(命令列引數)的使用方法,並探討程式結束時的 Exit Status(退出狀態)。讓我們一起揭開這些概念的神秘面紗,往電腦科學靠近!
「HI! 」在 C 語言中其實是以字元陣列的方式被儲存,每一個字母在 C 語言的資料型別是 char
。
#include <stdio.h>
int main(void)
{
char c1 = 'H';
char c2 = 'I';
char c3 = '!';
printf("%c%c%c\n", c1, c2, c3);
}
成功輸出 HI!
但如果改成以整數輸出,會有什麼結果呢?
#include <stdio.h>
int main(void)
{
char c1 = 'H';
char c2 = 'I';
char c3 = '!';
printf("%i%i%i\n", c1, c2, c3);//改成輸出int資料型別
}
這些 727333 是對照 ASCII Table 所產生的對應數字,之後再轉成 0 和 1 的方式存在電腦。
實際上不須要一個一個給字母資料型態,在 CS50 的環境中,加入 <cs50.h>
就會有string
表示字串。這個資料型別背後的原理是「陣列」,陣列是連續的資料儲存格式,就跟實際上的字串寫法一樣。以下示範用字元陣列的概念輸出字串,方便你了解儲存方式,但平常只需要打 %s
就可以輸出字串了。
#include <stdio.h>
#include <cs50.h>
int main(void)
{
string s = "HI!";
printf("%c%c%c\n", s[0],s[1],s[2]);
}
如果改成整數,並多顯示一格會發生什麼事呢?
#include <stdio.h>
#include <cs50.h>
int main(void)
{
string s = "HI!";
printf("%i%i%i%i\n", s[0],s[1],s[2],s[3]);
}
在 ASCII Table 中,輸出 0 代表 NULL
空值,這裡沒有值的意思,並不是使用者輸入 0 這個數字。補充說明,字元陣列的結束會用 NUL
代表字串結束,在電腦中會用 \0
多留一個空間,不過使用者並不會看到。
既然字串是以陣列的形式表示,也可以用陣列的方式計算字串的長度。背後的概念是讀陣列的索引值,如果讀到\0
就代表已經結束。
#include <cs50.h>
#include <stdio.h>
int string_length(string s);
int main(void)
{
string name = get_string("Name: ");
int length = string_length(name);
printf("%i\n", length);
}
int string_length(string s) // 寫一個長度的功能餵字串進去
{
int n = 0;
while (s[n] != '\0')
{
n++;
}
return n;
}
不過,C 語言裡有已經寫好的標頭檔,加入 <string.h>
,可以直接使用 strlen
即可,不需要再寫迴圈一個一個讀值。
綜合前面提到的 ASCII 表與陣列的概念,我們來實作凱薩密碼。凱薩密碼是經典的加密方法,由 Julius Caesar 提出,雖然現今這種方法很容易被破解,但仍是密碼學的入門經典題型。凱薩密碼主要概念是讓英文字母往後位移特定的數值,例如:
位移量 3,加密訊息為 "HELLO":
因此,"HELLO" 經過凱薩密碼加密後就會變成 "KHOOR"。
#include <cs50.h>
#include <ctype.h> //注意有<ctype.h>,後面的islower從這裡來
#include <stdio.h>
#include <string.h>
int main()
{
string s = get_string("input: ");
int move = get_int("偏移值: ");
for (int i = 0, n = strlen(s); i < n; i++)
{
if (islower(s[i]))//判斷小寫
{
printf("%c", (s[i] + move - 'a') % 26 + 'a');
}
else
{
printf("%c", (s[i] + move - 'A') % 26 + 'A');
}
}
}
在 C 語言,可以使用 <ctype.h>
的函式庫,就有判斷小寫的函數。
這裡也是要應用 ASCII 表的值,注意大小寫都固定相差 32 ,所以我可以利用這個特性,把所有的小寫變成大寫。
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
string s = get_string("Before: ");
printf("After: ");
for (int i = 0, n = strlen(s); i < n; i++)//起始值i與終點值n
{
if (s[i] >= 'a' && s[i] <= 'z') //
{
printf("%c", s[i] - 32);//每個字元都差32,用('a'-'A')也可以
}
else
{
printf("%c", s[i]);
}
}
printf("\n");
}
當字串本身就是一個陣列,而你想把多個字串組成一個陣列時,就會形成「大陣列包小陣列」的結構。
#include <stdio.h>
#include <cs50.h>
int main()
{
string words[2];
words[0]="HI!";
words[1]="BYE!";
printf("%c%c%c\n",words[0][0],words[0][1],words[0][2]);
printf("%s\n", words[1]);
}
words[0][0]
代表,陣列 words[0]
「HI!
」的第 0 個字母 H 。由此可知,陣列可以是多維陣列(Multi-dimensional Array),多維陣列通常用來表示像是表格、圖像或矩陣等資料結構。
#include <stdio.h>
int main()
{
// 定義一個 3x3 的二維陣列
int array[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
// 輸出陣列的所有元素
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d ", array[i][j]);
}
printf("\n"); // 換行,讓輸出看起來像一個表格
}
}
在這個例子中,array
是一個二維陣列,它包含三個子陣列,每個子陣列裡面有三個元素。我們可以用兩個索引來存取每個元素。例如:
array[0][0]
會得到 1
(第 0 列第 0 行),0 是索引值,陣列的起點。array[1][2]
會得到 6
(第 1 列第 2 行)Command Line Argument(命令列引數),其實就是指你在終端機或命令列中執行一個程式時,可以額外輸入的值或訊息,這些值會被餵給程式,幫助程式運作。
以前我們要執行打招呼的程式,需要再請使用者輸入名字,才可以打招呼。
使用者可以直接在下指令時,輸入「bicky」 ,就會跑出「 hello, bicky」。原因是我已經預留一個陣列空間,表示程式運行時就要被餵入一個值。
但我如果輸入 bicky yang 只會跑出 bicky,因為我指定輸出 argv[1]
也就是第一個引數。
argc
是什麼?argc
是一個整數,代表命令列引數的數量(arguments count),也就是當你執行這個程式時,總共傳入了幾個引數。argc
的值至少會是 1,因為它會包含程式本身的名稱作為第一個引數。argv[]
是什麼?argv[]
是一個字串陣列(array of strings),它儲存了所有的命令列引數。每個引數都是以字串形式儲存的。argv[0]
通常是執行的程式名稱,在這個程式 argv[0]
表示 ./greet
,argv[1]
、argv[2]
等則是依次代表你在命令列中輸入的其他引數。來點有趣的,如果你直接打 cowsay
加某個詞,就會出現一隻牛說出你輸入的詞。
一般沒有問題的程式,程式碼會 return 0
表示成功,只是使用者在終端機看不到,如果顯示 return 其他數字就表示程式出現錯誤。return 其他數字可由軟體開發工程師自行定義,每個數字表示異常的代表。例如,工程師可以設定如果 return 127
表示command not found
。在 C 語言 的環境中,可以輸入 echo $?
查上一個命令的狀態。
不過,網頁常出現的 404 not found 是 HTTP status,用以表示網頁伺服器超文字傳輸協定回應狀態的 3 位數字代碼,全世界統一,不能由工程師自行更改。