10.1 數組git
數組由一系列相同的元素構成。數組
數組聲明中包括數組元素的數目和元素的類型。函數
一些數組聲明的例子:優化
float candy[365]; /*365個浮點數的數組*/code
char code[12]; /*12個字符的數組*/對象
int states[50]; /*50個整數的數組*/索引
方括號表示標識符爲數組,方括號的數字指明瞭數組所包含元素數目。內存
要訪問數組中的元素,能夠使用下標數字來表示單個元素。下標數字也稱索引(index),是從0開始計數的。所以,candy[0]表示candy首元素,candy[364]是第365個元素,也就是最後一個元素。編譯器
10.1 初始化it
程序中一般使用數組來存儲數據。
C爲數組初始化引入如下新語法:
int powers[8] = {1,2,4,6,8,16,32,64}; /*只有ANSI C 支持這種初始化方式*/
從上例中能夠看出,可使用花括號括起來的一系列數值來初始化數組。數值之間用逗號隔開在數值和逗號之間可使用空格符。
程序清單10.1 day_mon1.c程序
#include <stdio.h> #define MONTHS 12 int main (void) { int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31}; int index; for(index=0;index<MONTHS;index++) printf("MONTH %d has %d days.\n",index+1,days[index]); return 0; }
注意本例採用標識符常量MONTHS來表明數組的大小。這是一種咱們所推薦的作法。若是須要修改數組大小 ,只須要修改#define語句便可,無須查找並修改程序中每一處使用數組大小的地方。
對數組使用const的方法
有時須要使用只讀數組,也就是程序從數組中讀取數值,可是不向數組中寫數據。在這種狀況下聲明並初始化數組時,建議使用關鍵字const。咱們對程序清單10.1的一部分進行優化,以下:
const int days[MONTHS] = {31,28,......};
使用未經初始化的數組會出現什麼狀況呢?
與普通變量類似,在初始化以前數組元素的數值是不定的。編譯器使用的數值是存儲單元中已有的數值。
*************************************************
存儲類解釋
和其餘變量類似,數組 能夠被定義爲多種存儲類(stotage class),第12章將詳述此主題。目前,只須要了解本章的數組屬於自動存儲類。也就是說,數組 是在一個函數內聲明的,而且聲明時沒有使用關鍵字static。到目前爲止,本書所用的變量和數組都是自動類型的。
如今提起存儲類的緣由是,不一樣存儲類有時具備不一樣的屬性,所以不能把配音的知識推廣到其餘存儲類。例如,若是沒有進行初始化,一些存儲類的變量和數組會把它們的存儲單元設置爲0.
************************************************
初始化列表中的元素數目應該和數組大小一致。若是兩者不一致,會出現什麼狀況?
當數值數目少於數組元素數目時,多餘的數組元素被初始化爲0。也就是說,若是不初始化數組,數組元素和未初始化的變量同樣,其中存儲的是無用數值;但若是部分初始化數組,未初始化的元素則被設置爲0。
若是初始化列表中項目的個數大於數組大小,編譯器會絕不留情的認爲這是一個錯誤。然而,能夠採用另一種形式以免受到編譯器的這種奚落:您能夠省略括號中的數字,從而讓編譯器自動匹配數組大小和初始化列表中的項目數目。
程序清單10.4 day_mon2.c程序
#include <stdio.h> int main (void) { int days[] = {31,28,31,30,31,30,31,31,30,31,30}; int index; for(index=0;index<sizeof days / sizeof days[0];index++) printf("MONTH %d has %d days.\n",index+1,days[index]); return 0; }
程序清單10.4 中有兩點須要注意:
一、當使用空的方括號對數組進行初始化時,編譯器會根據列表中的數值數目來肯定數組大小。
二、注意for循環的控制語句。因爲人工計算容易出錯,所以可讓計算機來計算數組的大小。運算符sizeof給出其後的對象或類型的大小(以字節爲單位)。所以sizeof days是整個數組的大小(以字節爲單位),sizeof days[0]是一個元素的大小(以字節爲單位)。整個數組的大小除以單個元素的大小就是數組中元素的數目。
三、自動計數的缺點:初始化的元素個數有誤時,咱們可能意識不到(上例中初始化項目只有10個,應該是12個)。
10.1.2 指定初始化項目
c99增長了一種新特性:指定初始化項目。
此特性容許選擇對某些元素進行初始化。在初始化列表中使用帶有方括號的元素下標能夠指定某個特定的元素:
int arr[6] = {[5]=212}; //把arr[5]初始化爲212
對於一般的初始化,在初始化一個或多個元素後, 未經初始化的元素都將被設置爲0。
程序清單10.5 designate.c程序
#include <stdio.h> #define MONTHS 12 int main (void) { int days[MONTHS] = {31,28,[4]=31,30,31,[1]=29}; int index; for(index=0;index<MONTHS;index++) printf("MONTH %d has %d days.\n",index+1,days[index]); return 0; }
輸出結果以下:
1 31 2 29 3 0 4 0 5 31 6 30 7 31 8 0 9 0 10 0 11 0 12 0
從輸出結果能夠看出指定初始化項目有兩個重要特性。
第一,若是在一個指定初始化項目後跟有不止一個值,例如在序列[4]=31,30,31中這樣,則這些數值將用來對後續的數組元素初始化。也就是說,把31賦給days[4]以後,接着把30和31分別賦給days[5]和days[6]。
第二,若是屢次對一個元素進行初始化,則最後一次有效。例如,前面把days[1]初始化爲28,然後面的指定初始化[1]=29覆蓋了前面的值,因而days[1]的數值最終爲29。
10.1.3 爲數組賦值
聲明完數組後,能夠藉助數組的索引(下標)對數組成員進行賦值。
/*數組賦值*/ #include <stdio.h> #define SIZE 50 int main (void) { int counter,evens[SIZE]; for (counter = 0;counter < SIZE;counter++) evens[counter]=2*counter; ... }
注意這種賦值的方式是使用循環對元素逐個賦值。C不支持把數組做爲一個總體來進行賦值,也不支持用花括號括起來的列表形式進行賦值(初始化的時候除外):
/*無效的數組賦值*/ #define SIZE 5 int main (void) { int oxen[SIZE] = {5,3,2,8}; /*這裏是能夠的*/ int yaks[SIZE]; yaks = oxen; /*不容許*/ yaks[SIZE]=oxen[SIZE]; /*不正確*/ yaks[SIZE]={5,3,2,8}; /*不起做用*/
10.1.4 數組邊界
使用數組的時候,須要注意數組索引不能超過數組邊界。也就是說,數組索引應該具備對於數組來講有效的值。
程序清單 10.6 bounds.c程序
//bounds.c --超出數組的邊界 #include <stdio.h> #define SIZE 4 int main (void) { int value1=44; int arr[SIZE]; int value2=88; int i; printf("value1 = %d,value = %d\n",value1,value2}; for (i = -1;i<=SIZE;i++) arr[i] = 2*i+1; for (i = -1;i<7;i++) printf("%2d %d\n",i,arr[i]); printf(value1 = %d,value2 = %d\n",value1,value2); return 0; }
程序不檢查索引的合法性。
在標準C中,若是使用了錯誤的索引,程序執行結果是不可知的。也就是,程序也許可以運行,可是運行結果能夠很奇怪,也可能會異常中斷程序的執行。咱們使用Digital Mars8.4運行程序,其輸出結果以下:
value1 = 44,value2=88 -1 -1 0 1 1 3 2 5 3 7 4 9 5 5 6 1241520 value1=-1,value2=9
注意,咱們使用的編譯器看起來是value2正好存儲在數組後面的那個存儲單元中,把value1存儲在數組前面的那個存儲單元中(其餘編譯器可能採起不一樣的順序在內存中存儲數據)。這樣arr[-1]就和value1對應同一存儲單元,arr[4]就和value2對應同一存儲單元。
所以,使用超出數組邊界的索引會改變其餘變量的數值。對於不一樣編譯器,輸出結果可能不一樣。
一件須要記住的簡單事情就是,數組的計數是從0開始的。避免出現這個問題比較簡單的方法是:在數組聲明中使用符號常量,而後程序中須要使用數組大小的地方都直接引用符號常量:
#define SIZE 4 int main(void) { int arr[SIZE]; for(i=0;i<SIZE;i++) ... }
這樣作的好處是保證整個程序中數組大小始終一致。
10.1.5 指定數組大小
在前面提到的例子中,咱們聲明數組時使用的是整數常量。
還容許使用什麼?直到C99標準出現以前,聲明數組時在方括號內只能使用整數常量表達式。整數常量表達式是由整數常量組成的表達式。sizeof表達式被認爲是一個整數常量,而一個const值卻不是整數常量。而且該表達式的值必須大於0:
int n=5; int m=8; float a1[5]; //能夠 float a2[5*2+1]; //能夠 float a3[sizeof(int)+1]; //能夠 float a4[-4]; //不能夠,數組大小必須大於0 float a5[0]; //不能夠,數組大小必須大於0 float a6[2.5] //不能夠,數組大小必須是整數 float a7[(int)2.5]; //能夠,把float類型指派爲int類型 float a8[n]; //C99以前不能夠 float a9[m]; //C99以前不能夠
遵循C90標準的C編譯器不容許最後兩個聲明。而c99容許這兩個聲明,但這建立了一種新數組,稱爲變長數組(variable length array),簡稱VLA。
VLA有些限制,例如,聲明時不能進行初始化。