C 語言基礎教程(我的C之旅開始了)[十]

來源:互聯網
上載者:User

27. 運算式(Expression)

運算式由運算子和運算元組成。單獨一個運算元是最簡單的運算式。請看以下例子:

9
-4
+5
3 + 6
a * ( b/c - d )
e = 9 / 3
f = ++e % 3

運算式中的運算式稱為子運算式。例如:b/c 是 a * ( b/c - d ) 的子運算式。

每個運算式都有一個值,這是 C 語言的一個重要屬性。顯而易見的,9 和 -4 的值就是 9 和 -4,3 + 6 的值就是 9 。e = 9 / 3 的值是 3 ,也就是是 = 號左邊的變數 e 被賦予的值。我們再來看看下面這個運算式:

8 - (a = 2 * 3)

想想看,它的值是多少?沒錯,就是 2 。但是,不推薦使用這種運算式,因為可讀性太差。

28. 數組基礎(上)

1. 數組簡介

數組(Array)由一系列同種資料類型的元素組成。編譯器可以從數組聲明中知道數組中元素的數目,以及這些元素的資料類型。例如:

double dbl[20]; /* 包含 20 個 double 類型元素的數組 */
int c[12]; /* 包含 12 個 int 型元素的數組 */
char ch[40]; /* 包含 40 個 char 型元素的數組 */

方括弧 [] 表明它們是數組,[] 裡的數字表明數組包含的元素數目。

數組中的元素是相鄰的,初始化之前,元素的值可能是隨機的。形象地表現了這種相鄰關係。

使用數組名和下標(subscript number 或 index)就可以訪問特定的元素。下標始於 0,止於 n - 1。例如:c[0] 是數組 c 的第一個元素,而 c[11] 是它的最後一個元素,也就是第 12 個元素。

2. 初始化

int c[12] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

如上所示,我們使用大括弧中一系列逗號分隔的值來初始化數組。我們把這個稱之為初始化列表。大括弧是必須的!逗號和值之間的空格可有可無。初始化後,數組 c 的第一個元素 c[0] 的值為 0,以此類推。

下面的小程式輸出數組 iarr 中所有元素的值。

#include <stdio.h>

int main(void)
{
int iarr[4] = { 0, 1, 2, 3 };
int i = 2;

printf("%d\n", iarr[0]); /* 輸出 0 */
printf("%d\n", iarr[1]); /* 輸出 1 */
printf("%d\n", iarr[i]); /* 輸出 2 */
printf("%d\n", iarr[1+2]); /* 輸出 3 */

return 0;
}

如上所示,訪問數組元素時,[] 裡的可以是常量,可以是變數,也可以是運算式。[] 裡還可以是傳回值為整型的函數調用。總之,只要 [] 裡的值是整數類型都可以。

注意,上面的程式,如果把 int iarr[4] = { 0, 1, 2, 3 }; 改成 int iarr[4];(即沒有初始化),則它裡面的元素的值是隨機的,也就是本來就存在於那段記憶體空間的值。如果改成 int iarr[4]; 後再把它放在 int main(void) 之前,則它裡面的元素的值都是 0。具體原因我在後續的教程會說明。

如果初始化列表中的值的個數少於數組元素個數,則餘下的元素都會被初始化為 0。例如:

int iarr[4] = { 0, 1 };

iarr[0] 和 iarr[1] 分別為 0 和 1;iarr[2] 和 iarr[3] 都被初始化為 0。注意,初始化列表中的值的個數可以少於數組元素個數,但是超過數組元素個數卻是不對的!

初始化數組時,[] 裡可以留空。例如:

int iarr[] = { 1, 2, 3 };

編譯器會算出初始化列表中的值的個數,然後構造包含那麼多個元素的數組。如上例,編譯器會算出列表中一共有 3 個值,然後把 iarr 構造成包含 3 個元素的數組。例如:

#include <stdio.h>

int main(void)
{
int iarr[] = { 1, 2, 3 };

printf("%d\n", iarr[0]); /* 輸出 1 */
printf("%d\n", iarr[1]); /* 輸出 2 */
printf("%d\n", iarr[2]); /* 輸出 3 */

return 0;
}

我們可以用以下運算式算出 iarr 中元素的個數:

sizeof iarr / sizeof iarr[0]

其中,sizeof iarr 算出數組 iarr 佔用的記憶體大小,sizeof iarr[0] 算出 iarr[0] 佔用的記憶體大小(也就是數組 iarr 中每個元素佔用的記憶體大小),它們相除就得出 iarr 的元素個數。sizeof 是一個運算子,具體用法我以後會說。

29. 數組基礎(下)

1. 指派初始值(Designated Initializers)

指派初始值這個特性是 C99 增加的,它允許我們直接初始化數組中特定的元素。C99 以前,如果我們要初始化數組中的某個元素,如第三個元素,必須同時初始化它之前的元素。例如:

int iarr[10] = { 0, 0, 300 };

而 C99 中,我們可以這樣初始化特定的元素:

int iarr[10] = { [2] = 300 }; /* 指派初始化 iarr[2] 為 300 */

其餘的元素都會被初始化為 0 。下面我們來看一個小程式。

#include <stdio.h>

int main(void)
{
int iarr[5] = { 6, 3, [3] = 1, 5, [1] = 8};

printf("%d\n", iarr[0]);
printf("%d\n", iarr[1]);
printf("%d\n", iarr[2]);
printf("%d\n", iarr[3]);
printf("%d\n", iarr[4]);

return 0;
}

輸出為:

6
8
0
1
5

從中可以看出兩點:

A. 如果指派初始值後面還有值,則後面的值會被用於初始化後續的元素。上例中,
iarr[3] 被初始化為 1 ,它後續的元素 iarr[4] 被初始化為 5。

B. 如果初始化列表中多次出現對某元素的初始化,則以最後一次為準。上例中,
iarr[1] 先被初始化為 3,然後被 [1] = 8 指派初始化為 8。

2. 給數組元素賦值

我們可以利用下標給特定的元素賦值。例如:

int iarr[5];
iarr[0] = 100; /* 賦值給第一個元素 */
iarr[4] = 120; /* 賦值給第五個元素 */
iarr[2] = 180; /* 賦值給第三個元素 */

C 不允許直接使用數組對別的數組進行賦值,也不允許使用初始化列表對數組進行賦值。例如:

int iarr_1[5] = { 1, 2, 3, 4, 5 }; /* 正確 */
int iarr_2[5];

iarr_2 = iarr_1; /* 錯誤! */
iarr_2[5] = { 3, 4, 5, 6, 7 }; /* 錯誤! */
iarr_2[5] = iarr_1[5]; /* 越界! */

最後一個語句發生了越界!因為這兩個數組都只有 5 個元素,而使用下標 5 訪問的是第六個元素!

3. 數組界限(array bounds)

使用下標時,我們必須確保下標沒有越界。例如:

int iarr[46];

這個數組的下標範圍是 0 到 45,確保下標沒有超出這個範圍是我們的責任,因為編譯器不會對下標越界進行檢測!

C 標準沒有定義下標越界的後果,也就是說,當我們寫的程式中出現下標越界的問題,程式可能正常工作,也可能異常退出,還有可能出現其它奇怪的情況。

#include <stdio.h>

int main(void)
{
int var_1 = 20;
int arr[5];
int var_2 = 40;

printf("var_1: %d, var_2: %d\n", var_1, var_2);

arr[-1] = -1;
arr[5] = 5;

printf("%d %d\n", arr[-1], arr[5]);
printf("var_1: %d, var_2: %d\n", var_1, var_2);

return 0;
}

上述程式使用 Dev-C++ 4.9.9.2 編譯啟動並執行輸出為:

var_1: 20, var_2: 40
-1 5
var_1: 20, var_2: -1

可見,下標越界可能改變其它變數的值。這是因為 gcc(dev-c++ 使用的 C 編譯器)把 var_2 儲存於數組 arr 之前的記憶體空間,所以對 arr[-1] 賦值正好改變了 var_2 的值。不同的編譯器編譯運行該程式可能會有不同的輸出,也可能會異常退出。

C 語言的哲學是信任程式員,而且不檢測越界程式運行更快。程式編譯時間有些下標的值仍然是不可知的,所以如果要檢測下標越界的話,編譯器必須在產生的目標代碼中加入額外的代碼用於程式運行時檢測下標是否越界,這就會導致程式運行速度下降。故而,為了運行效率,C 不檢測下標是否越界。

4. 指定數組元素數目

C99 之前,聲明數組時,[] 中的值必須是大於零的整數常量。C99 中,聲明數組時,[] 中可以是變數。這就是所謂的變長數組(variable-length array,簡稱 VLA)。聲明 VLA 時,不能對其進行初始化。在後續的教程中,我會對 VLA 進行詳細講解。

int n = 99;
double dbl_1[4]; /* 正確 */
double dbl_2[8/2 + 4]; /* 正確 */
int iar_1[-5]; /* 錯![] 中的值必須大於 0 */
int iar_2[0]; /* 錯![] 中的值必須大於 0 */
int iar_3[9.2]; /* 錯![] 中的值必須是整數類型 */
char ch[n]; /* C99 之前不支援! */

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.