第八章 数组
8.1.1 初试数组
如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数?
int num[100];
scanf("%d",&number[i]);
#include<stdio.h>
int main()
{
int x;
double sum=0;
int cnt=0;
int number[100]; //定义数组
scanf("%d",&x);
while(x!=-1){
number[cnt]=x; //对数组中的元素赋值
sum+=x;
cnt++;
scanf("%d",&x);
}
if(cnt>0){
printf("%f\n",sum/cnt);
int i;
for(i=0;i<cnt;i++){ //遍历数组
if(number[i]>sum/cnt){
printf("%d\n",number[i]); //使用数组中的元素
}
}
}
return 0;
}
8.1.2 定义数组
<类型>变量名称[元素数量];
int grades[ 100];
double weight[20];
- 元素数量必须是整数
- C99之前:元素数量必须是编译时刻确定的字面量
- 数组是一种容器(放东西的东西),特点是:
- 其中所有的元素具有相同的数据类型;
- 一旦创建,不能改变大小
- *(数组中的元素在内存中是连续依次排列的)
int a[10]
-
一个int的数组
-
10个单元: a[0],a[1],…,a[9]
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] -
每个单元就是一个int类型的变量
-
可以出现在赋值的左边或右边:
- a[2]=a[1]+6;
-
在赋值左边的叫做左值
数组的单元
- 数组的每个单元就是数组类型的一个变量
- 使用数组时放在[]中的数字叫做下标或索引,下标从0开始计数:
- grades[0]
- grades[99]
- average[5]
有效的数组下标
-
编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
-
一旦程序运行, 越界的数组访问可能造成问题,导致程序崩溃
- segmentation fault
-
但是也可能运气好,没造成严重的后果
-
所以这是程序员的责任来保证程序只使用有效的下标值: [0,数组的大小- 1]
长度为0的数组
可以但没必要
8.1.3 数组的例子:统计个数
写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束
#include<stdio.h>
int main()
{
const int number=10; //数组大小
int x;
int count[number]; //定义数组
int i;
for(i=0;i<number;i++){
count[i]=0; //初始化数组
}
scanf("%d",&x); //1.读入第一个x
while(x!=-1){ //2.如果x!=-1即x是要参与运算的数
if(x>=0&&x<=9){
count[x]++; //数组运算
} //3.在有效范围内,让计数器++
scanf("%d",&x);
}
for(i=0;i<number;i++){
printf("%d:%d\n",i,count[i]); //4.遍历数组,计算I数组出现的次数
}
return 0;
}
8.2.1 数组运算
数组的集成初始化
int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
- 直接用大括号给出数组的所有元素的初始值
- 不需要给出数组的大小,编译器替你数数
int a[10]={[0]=2,[2]=3,6,}; //只有C99可以
-
用[n]在初始化数据中给出定位
-
没有定位的数据接在前面的位置后面
-
其他位置的值补零
-
也可以不给出数组大小,让编译器算
-
特别适合初始数据稀疏的数组
数组的大小
sizeof给出整个数组所占据的内容的大小,单位是字节
sizeof(a)/sizeof(a[0])
sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码
数组的赋值
int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
int b[]=a; //这样不可以
数组变量本身不能被赋值
要把一个数组的所有元素交给另一个数组,必须采用遍历
for(i=0;i<length;i++){
b[i]=a[i];
}
-
通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的i正好是数组最大的有效下标
-
常见错误是:
- 循环结束条件是<=数组长度,或;
- 离开循环后,继续用i的值来做数组元素的下标!
在一组给定的数据中,如何找出某个数据是否存在?
#include<stdio.h>
/*找出key在数组a中的位置,
@param key要寻找的数字
@param a要寻找的数组
@param length 数组a的长度
@return 如果找到,返回其在a中的位置:如果找不到则返回-1
*/
int search(int key,int a[],int length);
int search(int key,int a[],int length)
{
int ret=-1;
int i;
for(i=0;i<length;i++){
if(a[i]==key){
ret=i;
break;
}
}
return ret;
}
int main()
{
int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
int x;
int loc;
printf("请输入一个数字:");
scanf("%d",&x);
loc=search(x,a,sizeof(a)/sizeof(a[0]));
if(loc!=-1){
printf("%d在第%d个位置上\n",x,loc);
}else{
printf("%d不存在\n",x);
}
return 0;
}
数组作为函数参数时,往往必须再用另一个参数来传入数组的大小
数组作为函数的参数时:
不能在[]中给出数组的大小
不能再利用sizeof来计算数组的元素个数!
8.2.2 数组例子:素数
//去掉偶数后,从3到x-1,每次加2
int isprime(int x)
{
int ret=1;
int i;
if(x==1||(x%2==0&&x!=2))
ret=0;
for(i=3;i<x;i+=2){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}
//如果x是偶数,返回0
//否则要循环(n-3)/2+1遍,n很大时就是n/2遍
//无须到x-1,到sqrt(x)就够了
int isprime(int x)
{
int ret=1;
int i;
if(x==1||(x%2==0&&x!=2))
ret=0;
for(i=3;i<sqrt(x);i+=2){
if(x%i==0){
ret=0;
break;
}
}
return ret;
}
#include<stdio.h>
int isprime(int x,int knownprimes[],int number0fkownprimes);
int main(void)
{
const int number=100;
int prime[number]={2};
int count=1;
int i=3;
while(count<number){
if(isprime(i,prime,count)){
prime[count++]=i;
}
i++;
}
for(i=0;i<number;i++){
printf("%d",prime[i]);
if((i+1)%5)
printf("\t");
else
printf("\n");
}
return 0;
}
int isprime(int x,int knownprimes[],int number0fknownprimes)
{
int ret=1;
int i;
for(i=0;i<number0fknownprimes;i++){
if(x%knownprimes[i]==0){
ret=0;
break;
}
}
return ret;
}
构造素数表
-
欲构造n以内的素数表
- 令x为2
- 将2x、3x、4x直至ax<n的数标记为非素数
- 令x为下一个没有被标记为非素数的数,重复2;直到所有的数都已经尝试完毕
-
欲构造n以内(不含)的素数表
- 开辟prime[n],初始化其所有元素为1,prime[x]为1 表示x是素数
- 令x=2
- 如果x是素数,则对于(i=2;xi<n;i+ + )令prime[ix]=0
- 令x++,如果x<n,重复3,否则结束
int main() { const int maxNumber=25; int isPrime[maxNumber]; int i; int x; for(i=0;i<maxNumber;i++){ isPrime[i]=1; } for(x=2;x<maxNumber;x++){ if(isPrime[x]){ for(i=2;i*x<maxNumber;i++){ isPrime[i*x]=0; } } } for(i=2;i<maxNumber;i++){ if(isPrime[i]){ printf("%d\t",i); } } printf("\n"); return 0; }
8.2.3 二维数组
int a[3][5]
通常理解为a是一个3行5列的矩阵
二维数组的遍历
for(i=0;i<3;i++){
for(j=0;j<5;j++){
a[i][j]=i*j;
}
}
a [ i ] [ j ]是一个int
表示第i行第j列上的单元
二维数组的初始化
int a[][5]={{0,1,2,3,4},{2,3,4,5,6,}};
- 列数是必须给出的,行数可以由编译器来数
- 每行一个{},逗号分隔
- 最后的逗号可以存在,有古老的传统
- 如果省略,表示补零
- 也可以用定位( * C99 ONLY)
tic-tac-toe游戏
读入一个3X3的矩阵,矩阵中的数字为1表示该位置上有一个X,为0表示为O
程序判断这个矩阵中是否有获胜的一方,输出表示获胜一方的字符X或O,或输出无人获胜
//读入矩阵
const int size=3;
int board[size][size];
int i,j;
int num0fX;
int num0f0;
int result=-1; //-1:没人赢,1:X赢,0:0赢
//读入矩阵
for(i=0;i<size;i++){
for(j=0;j<size;j++){
scang("%d",&board[i][j]);
}
}
//检查行
for(i=0;i<size&&result==-1;i++){
num0f0=num0fX=0; //0和X初始化为0
for(j=0;j<size;j++){
if(board[i][j]==1)
num0fX++;
else
num0f0++;
}
if(num0f0==size){
result=0;
}else if(num0fX==size){
result=1;
}
}
//检查列
if(result==1){
for(j=0;j<size&&result==-1;j++){
num0f0=num0fX=0; //0和X初始化为0
for(i=0;i<size;i++){
if(board[i][j]==1)
num0fX++;
else
num0f0++;
}
if(num0f0==size){
result=0;
}else if(num0fX==size){
result=1;
}
}
}
//检查对角线
num0f0=num0fX=0;
for(i=0;i<sizei;i++){
if(board[i][j]==1)
num0fX++;
else
num0f0++;
}
if(num0f0==size){
result=0;
}else if(num0fX==size){
result=1;
}
num0f0=num0fX=0;
for(i=0;i<sizei;i++){
if(board[i][size-i-1]==1)
num0fX++;
else
num0f0++;
}