1.C語言數組的概念
在《更加優美的C語言輸出》一節中我們舉了一個例子,是輸出一個 4×4 的整數矩陣,代碼如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a1=20, a2=345, a3=700, a4=22;
int b1=56720, b2=9999, b3=20098, b4=2;
int c1=233, c2=205, c3=1, c4=6666;
int d1=34, d2=0, d3=23, d4=23006783;
printf("%-9d %-9d %-9d %-9d\n", a1, a2, a3, a4);
printf("%-9d %-9d %-9d %-9d\n", b1, b2, b3, b4);
printf("%-9d %-9d %-9d %-9d\n", c1, c2, c3, c4);
printf("%-9d %-9d %-9d %-9d\n", d1, d2, d3, d4);
system("pause");
return 0;
}
運行結果:
20 345 700 22
56720 9999 20098 2
233 205 1 6666
34 0 23 23006783
矩陣共有 16 個整數,我們為每個整數定義了一個變量,也就是 16 個變量。那么,為了減少變量的數量,讓開發更有效率,能不能為多個數據定義一個變量呢?比如,把每一行的整數放在一個變量里面,或者把 16 個整數全部都放在一個變量里面。
我們知道,要想把數據放入內存,必須先要分配內存空間。放入4個整數,就得分配4個int類型的內存空間:
int a[4];
這樣,就在內存中分配了4個int類型的內存空間,共 4×4=16 個字節,並為它們起了一個名字,叫a。
我們把這樣的一組數據的集合稱為
數組(Array),它所包含的每一個數據叫做數組
元素(Element),所包含的數據的個數稱為數組
長度(Length),例如int a[4];就定義了一個長度為4的整型數組,名字是a。
數組中的每個元素都有一個序號,這個序號從0開始,而不是從我們熟悉的1開始,稱為
下標(Index)。使用數組元素時,指明下標即可,形式為:
arrayName[index]
arrayName 為數組名稱,index 為下標。例如,a[0] 表示第0個元素,a[3] 表示第3個元素。
接下來我們就把第一行的4個整數放入數組:
a[0]=20;
a[1]=345;
a[2]=700;
a[3]=22;
這里的0、1、2、3就是數組下標,a[0]、a[1]、a[2]、a[3] 就是數組元素。
我們來總結一下數組的定義方式:
dataType arrayName[length];
dataType 為數據類型,arrayName 為數組名稱,length 為數組長度。例如:
float m[12];
char ch[9];
注意:
1) 數組中每個元素的數據類型必須相同,對於int a[4];,每個元素都必須為 int。
2) 數組下標必須是整數,取值范圍為 0 ≥ index > length。
3) 數組是一個整體,它的內存是連續的,下面是int a[4];的內存示意圖:

①.數組的初始化
上面的代碼是先定義數組再給數組賦值,我們也可以在定義數組的同時賦值:
int a[4] = {20, 345, 700, 22};
{ }中的值即為各元素的初值,各值之間用,間隔。
對數組賦初值需要注意以下幾點:
1)
可以只給部分元素賦初值。當{ }中值的個數少於元素個數時,只給前面部分元素賦值。例如:
int a[10]={12, 19, 22 , 993, 344};
表示只給 a[0]~a[4] 5個元素賦值,而后面5個元素自動賦0值(int 類型變量的默認值就是0)。
2)
只能給元素逐個賦值,不能給數組整體賦值。例如給十個元素全部賦1值,只能寫為:
int a[10]={1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
而不能寫為:
int a[10]=1;
3)
如給全部元素賦值,那么在數組定義時可以不給出數組的長度。例如:
int a[]={1,2,3,4,5};
等價於
int a[5]={1,2,3,4,5};
最后,我們借助數組來輸出一個 4×4 的矩陣:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[4] = {20, 345, 700, 22};
int b[4] = {56720, 9999, 20098, 2};
int c[4] = {233, 205, 1, 6666};
int d[4] = {34, 0, 23, 23006783};
printf("%-9d %-9d %-9d %-9d\n", a[0], a[1], a[2], a[3]);
printf("%-9d %-9d %-9d %-9d\n", b[0], b[1], b[2], b[3]);
printf("%-9d %-9d %-9d %-9d\n", c[0], c[1], c[2], c[3]);
printf("%-9d %-9d %-9d %-9d\n", d[0], d[1], d[2], d[3]);
system("pause");
return 0;
}
2.C語言二維數組
上節講解的數組可以看作是一行連續的數據,只有一個下標,稱為一維數組。在實際問題中有很多量是二維的或多維的,因此C語言允許構造多維數組。多維數組元素有多個下標,以確定它在數組中的位置。本節只介紹二維數組,多維數組可由二維數組類推而得到。
①.二維數組的定義
二維數組定義的一般形式是:
dataType arrayName[length1][length2];
其中,
dataType 為數據類型,arrayName 為數組名,length1 為第一維下標的長度,length2 為第二維下標的長度。例如:
int a[3][4];
定義了一個3行4列的數組,共有3×4=12個元素,數組名為a,即:
a[0][0], a[0][1], a[0][2], a[0][3]
a[1][0], a[1][1], a[1][2], a[1][3]
a[2][0], a[2][1], a[2][2], a[2][3]
在二維數組中,要定位一個元素,必須給出一維下標和二維下標,就像在一個平面中確定一個點,要知道x坐標和y坐標。例如,a[3][4] 表示a數組第3行第4列的元素。
二維數組在概念上是二維的,但在內存中地址是連續的,也就是說存儲器單元是按一維線性排列的。那么,如何在一維存儲器中存放二維數組呢?有兩種方式:一種是按行排列, 即放完一行之后順次放入第二行。另一種是按列排列, 即放完一列之后再順次放入第二列。
在C語言中,二維數組是按行排列的。也就是先存放a[0]行,再存放a[1]行,最后存放a[2]行;每行中的四個元素也是依次存放。數組a為int類型,每個元素占用4個字節,整個數組共占用4×(3×4)=48個字節。
【示例】一個學習小組有5個人,每個人有三門課的考試成績。求全組分科的平均成績和各科總平均成績。
-- | 張 |
王 | 李 | 趙 | 周 |
Math | 80 | 61 | 59 | 85 | 76 |
C | 75 | 65 | 63 | 87 | 77 |
English | 92 | 71 | 70 | 90 | 85 |
可設一個二維數組a[5][3]存放五個人三門課的成績。再設一個一維數組v[3]存放所求得各分科平均成績,設變量average 為全組各科總平均成績。編程如下:
#include <stdio.h>
int main(){
int i, j; //二維數組下標
int sum=0; //當前科目的總成績
int average; //總平均分
int v[3]; //各科平均分
int a[5][3]; //用來保存每個同學各科成績的二維數組
printf("Input score:\n");
for(i=0; i<3; i++){
for(j=0; j<5; j++){
scanf("%d", &a[j][i]); //輸入每個同學的各科成績
sum+=a[j][i]; //計算當前科目的總成績
}
v[i]=sum/5; // 當前科目的平均分
sum=0;
}
average =(v[0]+v[1]+v[2])/3;
printf("Math: %d\nC Languag: %d\nEnglish: %d\n", v[0], v[1], v[2]);
printf("Total:%d\n", average);
return 0;
}
運行結果:
Input score:
80
61
59
85
76
75
65
63
87
77
92
71
70
90
85
Math: 72
C Languag: 73
English: 81
Total:75
程序中首先用了一個雙重循環。在內循環中依次讀入某一門課程的各個學生的成績,並把這些成績累加起來,退出內循環后再把該累加成績除以5送入v[i]之中,這就是該門課程的平均成績。外循環共循環三次,分別求出三門課各自的平均成績並存放在v數組之中。退出外循環之后,把v[0]、v[1]、v[2]相加除以3即得到各科總平均成績。最后按題意輸出各個成績。
①.二維數組的初始化
二維數組的初始化可以按行分段賦值,也可按行連續賦值。
例如對數組a[5][3],按行分段賦值可寫為:
int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };
按行連續賦值可寫為:
int a[5][3]={80, 75, 92, 61, 65, 71, 59, 63, 70, 85, 87, 90, 76, 77, 85};
這兩種賦初值的結果是完全相同的。
【示例】求各科平均分和總成績平均分。
#include <stdio.h>
int main(){
int i, j; //二維數組下標
int sum=0; //當前科目的總成績
int average; //總平均分
int v[3]; //各科平均分
int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };
for(i=0; i<3; i++){
for(j=0; j<5; j++){
sum+=a[j][i]; //計算當前科目的總成績
}
v[i]=sum/5; // 當前科目的平均分
sum=0;
}
average =(v[0]+v[1]+v[2])/3;
printf("Math: %d\nC Languag: %d\nEnglish: %d\n", v[0], v[1], v[2]);
printf("Total:%d\n", average);
return 0;
}
運行結果:
Math: 72
C Languag: 73
English: 81
Total:75
對於二維數組初始化賦值還有以下說明
1)
可以只對部分元素賦初值,未賦初值的元素自動取0值。例如:
int a[3][3]={{1},{2},{3}};
是對每一行的第一列元素賦值,未賦值的元素取0值。 賦值后各元素的值為:
1 0 0
2 0 0
3 0 0
int a [3][3]={{0,1},{0,0,2},{3}};
賦值后的元素值為:
0 1 0
0 0 2
3 0 0
2)
如對全部元素賦初值,則第一維的長度可以不給出。例如:
int a[3][3]={1,2,3,4,5,6,7,8,9};
可以寫為:
int a[][3]={1,2,3,4,5,6,7,8,9};
3)
數組是一種構造類型的數據。二維數組可以看作是由一維數組的嵌套而構成的。設一維數組的每個元素都又是一個數組,就組成了二維數組。當然,前提是各元素類型必須相同。根據這樣的分析,一個二維數組也可以分解為多個一維數組。C語言允許這種分解。
如二維數組a[3][4],可分解為三個一維數組,其數組名分別為:a[0]、a[1]、a[2]。
對這三個一維數組不需另作說明即可使用。這三個一維數組都有4個元素,例如:一維數組a[0]的元素為a[0][0], a[0][1], a[0][2], a[0][3]。必須強調的是,a[0], a[1], a[2]不能當作下標變量使用,它們是數組名,不是一個單純的下標變量。
3.C語言字符數組和字符串
用來存放字符的數組稱為字符數組,例如:
char c[10];
由於char和int可以相互轉換,也可以定義為:
int c[10];
但這時每個數組元素占4個字節的內存。
字符數組也可以是二維或多維數組。例如:
char c[5][10];
字符數組也允許在定義時進行初始化,例如:
char c[10]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a','m'}; // c[9]未賦值,所以為默認值0
當對全體元素賦初值時也可以省去長度說明。例如:
char c[]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm' };
這時c數組的長度自動定為9。
字符數組和普通數組一樣,也是通過下標引用各個元素。
【示例】輸出字符數組中的元素。
#include <stdio.h>
int main(){
int i,j;
char a[][7]={
{'C',' ','L','a','n','g'},
{'P','r','o','g','r','a','m'},
{'F','u','n','n','y'}
};
for(i=0; i<=2; i++){
for(j=0; j<=6; j++){
printf("%c", a[i][j]);
}
printf("\n");
}
return 0;
}
運行結果:
C Lang
Program
Funny
本例的二維字符數組由於在初始化時全部元素都賦以初值,因此一維下標的長度可以不寫。
①.字符串和字符串結束標志
在C語言中沒有專門的字符串變量,沒有string類型,通常用一個字符數組來存放一個字符串。
前面已經教大家用puts和printf函數輸出字符串,例如:
puts("http://c.biancheng.net");
printf("C Language");
在C語言中,字符串總是以'\0'作為串的結束符。上面的兩個字符串,編譯器已經在末尾自動添加了'\0'。
'\0'是ASCII碼表中的第0個字符,用NUL表示,稱為空字符。該字符既不能顯示,也不是控制字符,輸出該字符不會有任何效果,它在C語言中僅作為字符串的結束標志。
字符串處理函數遇到NUL時會認為字符串已經結束,不再繼續操作,例如:
#include <stdio.h>
int main(){
puts("C Lang\0 is funny!");
return 0;
}
運行結果:
C Lang
可以發現,'\0' 后面的字符都沒有輸出。
當把字符串存入字符數組時,也應該把結束符 '\0' 存入數組,並以此作為該字符串是否結束的標志。有了'\0'標志后,就不必再用字符數組的長度來判斷字符串的長度了。
C語言允許用字符串的方式對數組作初始化賦值。例如:
char c[]={'c', ' ','p','r','o','g','r','a','m'};
可寫為:
char c[]={"C program"};
或去掉{}寫為:
char c[]="C program";
用字符串方式賦值比用字符逐個賦值要多占一個字節, 用於存放字符串結束標志 '\0'。上面的數組c在內存中的實際存放情況為:

'\0' 是由編譯器自動加上的。由於采用了 '\0' 標志,所以在用字符串賦初值時一般無須指定數組的長度, 而由編譯器自行處理。
②.字符數組的輸入輸出
在采用字符串方式后,字符數組的輸入輸出將變得簡單方便。除了上述用字符串賦初值的辦法外,還可用printf函數和scanf函數一次性輸出輸入一個字符數組中的字符串,而不必使用循環語句逐個地輸入輸出每個字符。
【示例】使用printf輸出整個字符數組。
#include <stdio.h>
int main(){
char c[]="C Lang\nJava\nC++\nPython";
printf("%s\n", c);
return 0;
}
運行結果:
C Lang
Java
C++
Python
在printf函數中,使用%s輸出字符串。注意在輸出列表中給出數組名即可。不能寫為printf("%s", c[]);。
【示例】使用scanf從控制台輸入一個字符串,然后使用printf將其輸出。
#include <stdio.h>
int main(){
char str[100]; //必須說明長度,不能寫為 char str[];
printf("Input string: ");
scanf("%s", str);
printf("Your string is: %s\n", str);
return 0;
}
運行結果:
Input string: http://c.biancheng.net↙
Your string is: http://c.biancheng.net
由於字符數組長度為100,因此輸入的字符串長度必須小於100,以留出一個字節用於存放字符串結束標志`\0`。
③對程序的幾點說明
1)
對一個字符數組,如果不作初始化賦值,則必須說明數組長度。
2)
當用scanf函數輸入字符串時,字符串中不能含有空格,否則將以空格作為串的結束符。
例如當輸入的字符串中含有空格時,運行結果為:
Input string: c language↙
Your string is: c
可以看出,空格以后的字符都未能輸出。為了避免這種情況,可多設幾個字符數組分段存放含空格的串。程序可改寫如下:
#include <stdio.h>
int main(){
char str1[20], str2[20], str3[20], str4[20];
printf("Input string: ");
scanf("%s %s %s %s",str1, str2, str3, str4);
printf("Your string: %s %s %s %s\n", str1, str2, str3, str4);
return 0;
}
運行結果:
Input string: C C++ Java Python
Your string: C C++ Java Python
3)
在《從鍵盤輸入數據》中講到,scanf 的各個變量前面要加取地址符&,用以獲得變量的地址,例如:
int a, b;
scanf("%d %d", &a, &b);
但是在本節的示例中,將字符串讀入字符數組卻沒有使用&,例如:
char str1[20], str2[20], str3[20], str4[20];
scanf("%s %s %s %s",str1, str2, str3, str4);
這是因為C語言規定,數組名就代表了該數組的地址。整個數組是一塊連續的內存單元,如有字符數組char c[10],在內存可表示為:

C語言還規定,數組名所代表的地址為第0個元素的地址,例如char c[10];,c就代表c[0]的地址。第0個元素的地址就是數組的起始地址,所以稱為
首地址。也就是說,數組名表示數組的首地址。
設數組c的首地址為0X2000,也就是說c[0]地址為0X2000,則數組名c就代表這個首地址。
因為c已經表示地址,所以在c前面不能再加取地址符&,例如寫作 scanf("%s",&c); 是錯誤的。
有了首地址,有了字符串結束符'\0',就可以完整的定位一個字符串了。例如:
printf("%s", c);
printf 函數會根據數組名找到c的首地址,然后逐個輸出數組中各個字符直到遇到 '\0' 為止。
int、float、char 類型的變量表示數據本身,數據就保存在變量中;而數組名表示的是數組的首地址,數組保存在其他內存單元,數組名保存的是這塊內存的首地址。后面我們會講解指針,大家將會有更加深刻的理解。
4.C語言字符串處理函數
C語言提供了豐富的字符串處理函數,例如字符串的輸入、輸出、合並、修改、比較、轉換、復制、搜索等,使用這些現成的函數可大大減輕編程的負擔。
用於輸入輸出的字符串函數,例如printf、puts、scanf、gets等,使用時應包含頭文件stdio.h,使用其它字符串函數則應包含頭文件string.h。
①.字符串輸入輸出函數
printf、puts、scanf 前面已經介紹過了,不再贅述,這里重點講解gets函數。
gets 是 get string 的縮寫,意思是獲取用戶從鍵盤輸入的字符串,語法格式為:
gets(arrayName);
arrayName 為字符數組。從鍵盤獲得的字符串,將保存在 arrayName 中。請看下面的例子:
#include <stdio.h>
int main(){
char str[60];
printf("Input string: ");
gets(str);
puts(str);
return 0;
}
運行結果:
Input string: C Java C++ Python
C Java C++ Python
可以看出當輸入的字符串中含有空格時,輸出仍為全部字符串。說明 gets 函數並不以空格作為字符串輸入結束的標志,而只以回車作為輸入結束,這與 scanf 函數是不同的。
②.字符串長度函數strlen
strlen 是 string length 的縮寫,用來獲得字符串的長度。所謂長度,就是包含多少個字符(不包括字符串結束標志 '\0')。語法格式為:
strlen(arrayName);
strlen 將返回字符串的長度,它是一個整數。請看下面的例子:
#include <stdio.h>
#include <string.h>
int main(){
char str[]="C language";
int len = strlen(str);
printf("The lenth of the string is %d\n", len);
return 0;
}
運行結果:
The lenth of the string is 10
需要說明的是,strlen 會從字符串的第 0 個字符開始計算,直到遇到字符串結束標志 '\0'。將上面代碼中的 str 改為:
char str[]="C \0language";
那么輸出結果就是:
The lenth of the string is 2
③.字符串連接函數 strcat
strcat 是 string catenate 的縮寫,意思是把兩個字符串拼接在一起,語法格式為:
strcat(arrayName1, arrayName2);
arrayName1、arrayName2 為需要拼接的字符串。
strcat 將把 arrayName2 連接到 arrayName1 后面,並刪去 arrayName1 最后的結束標志 '\0'。
這就意味着,arrayName1 的長度要足夠,必須能夠同時容納 arrayName1 和 arrayName2,否則會越界。
strcat 返回值為 arrayName1 的首地址。請看下面的例子:
#include <stdio.h>
#include <string.h>
int main(){
char str1[40]="My name is ";
int str2[20];
printf("Input your name:");
gets(str2);
strcat(str1,str2);
puts(st1);
return 0;
}
運行結果:
Input your name:xiao p
My name is xiao p
④.字符串復制函數strcpy
strcpy 是 string copy 的縮寫,意思是字符串復制,語法格式為:
strcpy(arrayName1, arrayName2);
strcpy 會把 arrayName2 中的字符串拷貝到 arrayName1 中,串結束標志 '\0' 也一同拷貝。arrayName 也可以是一個字符串常量,這時相當於把一個字符串賦值給一個字符數組。請看下面的例子:
#include <stdio.h>
#include <string.h>
int main(){
char str1[15], str2[]="C Language";
strcpy(str1, str2);
puts(str1);
printf("\n");
return 0;
}
運行結果:
C Language
strcat 要求 arrayName1 要有足夠的長度,否則不能全部裝入所拷貝的字符串。
5.對C語言數組的總結以及實例
數組(Array)是一系列相同類型的數據的集合,可以是一維的、二維的、多維的;最常用的是一維數組和二維數組,多維數組較少用到。
①.對數組的總結
1) 數組的定義格式為:
type arrayName[length]
type 為數據類型,arrayName 為數組名,length 為數組長度。
需要注意的是:
- length 必須是一個數值常量,不能是變量,因為代碼編譯期間編譯器要明確知道數組元素的個數,而變量的值是不確定的。
- 數組中的數據保存在一個內存區域A,而數組名保存另外一個內存區域B,數組名中包含的是A的首地址。
2) 訪問數組中某個元素的格式為:
index 為數組下標。
注意 index 的值不能大於 length,否則會發生數組越界,出現意想不到的錯誤。
3) 可以對數組中的單個元素賦值,也可以整體賦值,例如:
// 對單個元素賦值
int a[3];
a[0] = 3;
a[1] = 100;
a[2] = 34;
// 整體賦值(不指明數組長度)
float b[] = { 23.3, 100.00, 10, 0.34 };
// 整體賦值(指明數組長度)
int m[10] = { 100, 30, 234 };
// 字符數組賦值
char str[] = "http://c.biancheng.net";
對數組整體賦值時,可以不指名數組的長度。未賦值的元素為默認值。
②.數組應用舉例
求一個整型數組中的最大值和最小值。
#include <stdio.h>
int main(){
int a[10] = {2, 123, 45, 100, 575, 240, 799, 710, 10, 90};
int max=a[0], min=a[0]; //最大值和最小值
int i; //循環增量
int len = sizeof(a) / sizeof(int); //計算數組長度
for(i=1; i<len; i++){
if(a[i] > max){
max = a[i];
}
if(a[i] < min){
min = a[i];
}
}
printf("The max is %d, The min is %d\n", max, min);
return 0;
}
運行結果:
The max is 799, The min is 2
思考一下,max 和 min 的初值為什么要設為 a[0] 呢,默認不可以嗎?
③.關於排序和查找
學完了數組,有兩個重要的知識點要求大家掌握,那就是排序(Sort)和查找(Search),比如:
- 給你 10 個打亂順序的整數,要能夠按照從小到大或者從大到小的順序輸出;
- 給定一個字符串 str1,以及一個子串 str2,要能夠判斷 str2 是否在 str1 中。