第九章 数组
引言:
C语言中简单数据类型:
int float double char int * int**
构造数据类型:数组
数组定义:把类型相同的若干个变量组合到一块,它们有一个的组名,具体
示每一个变量是用组名和该变量在组中排的位置组成。
程序中代码表示:int a[5];
a[0]a[1]…..a[4]
存储图:
a[0] a[1] a[2] a[3] a[4]
1111 1115 1119 1123 1127
1.一维数组的定义和一维数组元素的引用
(1)一维数组的定义
形式:类型名 数组名[整型常量表达式]
类型名:数组元素的类型
数组名: 合法的表示法
整型常量表达式:确定组的大小
int a,i=3;
int a[3]; int a[2+1];
flaot a[3];
double a[3];
char a[3];
int *a,*b,*c;
int *a[3];
int a[i]; 非法
int a[3.0];非法
(2)一维数组的引用
下标:确定每一个具体元素在组中的位置。下标从0开始且不可越界,下标的类型(整型常量、整型常量表达式、已经定义且赋值的整型变量)
形式:组名[下标];
举例:
int a[3],i=1,j=0;
a[0] a[1] a[2]
a[1+1]
a[i] a[i+j]
a[3] 非法
a[-1] 非法
a[1.5] 非法
总结:
int a[3],i=1;
数组名:a
元素及其元素的个数:3 a[0] a[1] a[2]
下标的下界和上界 0 1 2
存储图:
一维数组名:
一个数组元素实质就是一个变量,代表内存中的一个存储单元。一个数组代表了内存中一串连续的存储单元。
C语言中一维数组名中存放的是一串连续存储单元的首地址,它是一个地址常量。一维数组名可理解为一个指针,它的基类型为数组类型。不可以对它重新赋值,也不可以用数组名a代表a[0].a[1]…这些数组元素。 理解为指针
a[0] a[1] a[2] a[3] a[4]
1111 1115 1119 1123 1127
int a[5],k,*p=&k;
a[0] a[1] a[2]…a[4]
1111 1115 …
10 20 30……
a:数组名,理解为指针
值:1111 &a[0] a 常量
a=p; a++; 非法
p=a;
基类型: int
(3)一维数组的初始化(赋值)
int a=1;
int a[3]={1,2}; 0
char a[3]={‘0’,’1’ }; ‘\0’
int a[]={0 ,0,0}; 3
(4)一维数组编程
例:求所有元素的平均值
#include "stdio.h"
main()
{int a[6]={72,98,87,64,57,77};
int sum=0,i;doble ave;
for(i=0;i<6;i++)
sum=sum+a[i]/6;
printf(“%lf”,sum);
}
2.一维数组和指针
a[0] a[1] a[2] a[3] a[4]
1111 1115 1119 1123 1127
(1)一维数组和数组元素的地址
数组名代表了一串连续存储单元的首地址(数组中第0个元素的地址)这个地址值是固定的,即数组名为一个地址常量不可以对它重新赋值。
a可理解为指针,它的基类型为数组的类型。
&a[0] 1111
一串连续存储单元的首地址 1111
a里边的内容 1111
a的基类型 int
int a[3],x=10,*p=&k,k;
a=&x; 非法
a++; 非法
a=p; 非法
p=a; p=&a[0]
语句举例:
int a[3],*p,i;
1)数组名a 的形式:
使p依次指向数组中的每一个元素
p=&a[0] a a+0 1111
p=&a[1] a+1 1111+1*4 1115
p=&a[2] a+2 1111+2*4 1119
for(i=0;i<3;i++)
p=a+i;
从键盘输入数据依次存放到a数组中
for( i=0;i<3;i++)
scanf(“%d”,&a[i]);
a+i
2)利用指针p的形式:
使p依次指向数组中的每一个元素
p=a;p=&a[0]; 1111
p=&a[0] a a+0 p p+0
p=&a[1] a+1 p+1
p=&a[2] a+2 p+2
a+i p+i p++
for(i=0;i<3;i++)
p=p+i; 或 p++;
从键盘输入数据依次存放到a数组中
for(p=a,i=0;i<3;i++)
scanf(“%d”,&a[i])
a+i
p+i
p++
for(p=a;p-a<3;p++)
scanf(“%d”,p);
(2)通过数组首地址引用一维数组元素
a是数组的首地址
1111 &a[0] a a+0
1115 &a[1] a+1
1119 &a[2] a+2
a+i
“*”间接访问运算符:访问了地址所在的存储单元的内容
10 a[0] *(&a[0]) *(a+0) *a
20 a[1] *(&a[1]) *(a+1)
30 a[2] *(&a[2]) *(a+2)
逐个输出a数组中的值
for(i=0;i<3;i++)
printf(“%d”,a[i])
*(&a[i])
*(a+i)
(3)通过指针引用一维数组元素
p=a;p=&a[0];
10 a[0] *(&a[0]) *a *(p)
20 *(a+1) *(p+1)
30 *(a+2) *(p+2)
*(a+i) *(p+i)
*p++
逐个输出a数组中的值
for(p=a,i=0;i<3;i++)
Printf(“%d”,a[i])
*(&a[i])
*(a+i)
*(p+i)
*p++
for(p=a;p-a<3;p++)
printf(“%d”,*p);
(4)用带下标的指针变量引用一维数组元素
int a[3],*p=a;
a[i]地址的表示形式:
&a[i] a+i p+i p++
a[i]表示形式:
a[i] *(a+i) *(p+i) *p++ p[i]
a[i]与p[i]的区别:
a是数组名,是一个地址常量。a++,a=p 非法
p是一个指针,是可以重新赋值的。
p++ p=a+2 p=&a[2] 合法
3.
间对一维数组和数组元素的引用
(1)数组元素作实参时,和普通变量一样,形参为与其类型相同的变量
main()
{int *a[3];
fun(&a[0],a[1],a[2]);…}
fun(int **a,int *b,int *c) a=a[0]
a=&a[0]
{………}
(2)数组名作实参,数组名是一个地址值,所以形参为基类型与数组类型一致的指针变量。
main()
{int a[3];
…..
fun(a);
….
}
fun(int *p){…..} p=a;&a[0] 1111
fun(int p[]){….} 1
fun(int p[3]){…} 1
注:在fun函数中只开辟了一个指针变量的存储单元,并不是一串连续的存储单元。把a的内容(&a[0])传给形参指针。
形参中后两种形式与数组类似,但c编译系统都将其处理为一个指针。其实质都是一个指针,只占一个存储单元。
例9.2
(3)数组元素地址作实参,形参是基类型与该地址变量类型相同的指针变量
fun(&a[1]);
例9.3
(4)函数的指针形参和函数体中数组的区别
#include
int *fun(int a[10],int n) 1
int *a
in a[]
{ int b[10]; 10
…….
return b;
}
main()
{ int w[10],*p;
……..
p=fun(w,10); &w[0] 1111
……..
}
4.一维数组应用举例
***(1)编写fun函数,将数组中数按颠倒的顺序重新存放。
操作时不可另外开辟数组,只能借助一个临时存储单元。
***(2)已知一个数组中的值在0---9范围内的整型一维数组,
统计每个整数的个数.
分别用数组元素c[0]到c[9]统计整数0----9的个数。当数组元素a[i]中的值为0时,表达式c[a[i]](即c[0])增1,实现整数0的个数增1的运算。
以后在一个循环中依次改变a数组元素的下标来逐个引用a数组中的元素。用表达式c[a[i]]++则可统计各整数的个数。
***(3) 用选择法对数组中的数进行排序(按由小到大)
5.二维数组的定义和二维数组元素的引用
(1)二维数组的定义(在逻辑结构上可理解为一个具有行和列的矩阵 )
int a[3];
类型名 数组名[常量表达式]
定义形式:
类型名 数组名[常量表达式1][常量表达式2]
类型名:数组元素的类型
数组名:合法的用户标示符
常量表达式:行
列
举例:
int a;
int a[6];
int a[2][3];
数组名: a
数组元素个数: 6
逻辑结构:
第0列 第1列 第2列
第0行 a[0][0] a[0][1] a[0][2]
a[0] a[1] a[2]
第1行 a[1][0] a[1][1] a[1][2]
a[3] a[4] a[5]
物理结构:a数组中的元素在内存中占一串连续的存储单元。
a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]
1111 1115 1119 1123 1127 1131
a
a[0] a[1]
1111
1123
a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]
1111 1115 1119 1123 1127 1131
二维数组可看成是一个一维数组,每一个数组元素又是包含了若干个元素的一维数组。
a数组可看做由a[0]和a[1]两个元素组成的一维数组。a[0]和a[1]又分别由3个元素组成的一维数组。
int a[2][3];
行:a a[0] a[1]
列 a[0] a[0][0] a[0][1] a[0][2]
a[1] a[1][0] a[1][1] a[1][2]
(2)二维数组元素的引用(必须带两个下标)
数组名[下标]
数组名[下标表达式1][下标表达式2]
int a[2][3],i=0,j=1,k=1;
a[0][0] a[1][2]
a[1][1+1]
a[i][j+k]
a[1.0][2]
a[1,2]
a[i,j+2]
(3)二维数组的初始化
int a[]={1,2};
int a[2][3]={{1,2,3},{4,5,6}};
int a[2][3]={{1,2},{3,4}};
int a[2][3]={{1,2}};
int a[2][3]={1,2,3,4};
(4)通过赋初值定义二维数组的大小(一般只省略第一维的大小)
int a[][3]={{1,2},{4},{5,6}};
int a[][3]={1,2,3,4,5,6,7};
商
商+1
举例:
a[0][0] a[0][1] a[0][2]
10 20 30
a[1][0] a[1][1] a[1][2]
40 50 60
scanf(“%d”,&a[0][0]);
0 1
0 2
1 0
1 1
1 2
&a[i][j]
i=0 j=0 1 2
i=1 j=0 1 2
main()
{int a[2][3],i,j;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
scanf(“%d”,&a[i][j]);
}
行:10 20 30
40 50 60
printf(“%d”,a[0][0]);
0 1
0 2
1 0
1 1
1 2
a[i][j]
i=0 j=0 1 2
i=1 j=0 1 2
for(i=0;i<2;i++)
for(j=0;j<3;j++)
printf(“%d”,a[i][j]);
列:10 40
20 50
30 60
printf(“%d”,a[0][0]);
1 0
2 0
0 1
1 1
2 1
a[i][j]
j=0 i=0 1 2
j=1 i=0 1 2
for(j=0;j<2;j++)
for(i=0;i<3;i++)
printf(“%d”,a[i][j]);
10 20 30 40
00 01 10 11
00 10 01 11
for(i=0;i<2;i++)
for(j=0;j<2;j++)
printf(“%d”,a[i][j])
a[j][i]
程序应用:
main()
{int a[2][3]={1,2,3,4,5,6},b[6];
fun(a,6);
for(i=0;i<6;i++)
printf(“%d”,b[i]);
}
fun(int *p,int n)
{
}
6.二维数组和指针
(1)二维数组和数组元素的地址
int a[3][3],*p=&a[0][0];
a 1111
a[0] a[1] a[2]
1111
1123
1135
a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] a[2][0] a[2][1] a[2][2]
10
90
1111 1115 1119 1123 1127 1131 1135 1139 1143
①二维数组a由若干个元素组成的一维数组。
一维:
int a[3],k,*p=&k;
a: 一维数组名,理解为指针
a的值:&a[0] 1111
a++ a=p 非法
p=a
a+1
a的基类型:int
二维:
int a[3][3],*p=&k,k;
行:a a[0] a[1] a[2]
列:a[0] a[0][0] a[0][1] a[0][2]
a[1] a[1][0] a[1][1] a[1][2]
a[2] a[2][0] a[2][1] a[2][2]
a[0],a[1],a[2]: 一维数组名
理解为指针
列指针
a[0],a[1],a[2]的值:&a[0][0] 1111
&a[1][0] 1123
&a[2][0] 1135
a[0]++
a[0]=p
p=a[0]
a[0]+1 &a[0][1]
1111+1*4
a[0],a[1],a[2]的基类型:int
②二维数组名是地址常量
int a[3][3],*p=&k,k;
a:二维数组名,理解为指针
行指针
a的值:二维数组元素的首地址
&a[0][0] a[0] 1111
a++ 非法
a+1 a[1] &a[1][0]
1111+1*3*4=1123
p=a 非法基类型
a的基类型:数组
总结:
int a[3][3],*p;
for(i=0;i<3;i++)
p=a[i];
a[0]+1 列指针
a+1 行指针
&a[0][0] a[0] a+0
&a[1][0] a[1] a+1 1111+1*3*4 1123
&a[2][0] a[2] a+2
③二维数组元素的地址
int a[3][3];
直接表示 &a[2][2]
通过每行的首地址 a[2] a[0]
a[2]+2
*(a+2)+2
a[0]+2*3+2 1111+2*3*4+2*4 1143
&a[0][0]+2*3+2
int a[3],p=a;
&a[i] a+i p+i p++
a[i] *(a+i) *(p+i) *p++ p[i]
(2)通过地址引用二维数组元素
值:第二行第一列
a[2][2]
(*(a+2))[2]
*(a[2]+2)
*(*(a+2)+2)
*(&a[0][0]+2*3+2)
(3)通过指针数组引用二维数组元素
int * a[3]; a[0] a[1] a[2]
int * p[3],a[3][2],i,j;
for(i=0;i<3;i++)
{p[i]=a[i]; p[i]++;}
1111 a[0][0]
1111
1119
1127
1115 a[0][1]
1119 a[1][0]
1123 a[1][1]
1127 a[2][0]
1131 a[2][1]
a[2][1]
*(a[2]+1) *(p[2]+1)
*(*(a+2)+1) *(*(p+2)+1)
(*(a+2))[1] (*(p+2))[1]
(4)通过行指针引用二维数组元素
int (*p)[2],a[3][2];
p=a; &a[0][0] 1111
p++ p+1 1111+1*2*4 1119
a+1
1111 a[0][0]
1111
1115 a[0][1]
p 1119 a[1][0]
1123 a[1][1]
1127 a[2][0]
1131 a[2][1]
int a[3][2],*p[3],(*q)[2],i;
for(i=0;i<3;i++)
p[i]=a[i]; p[i]++ a[i]+1
q=a; q++ a+1
7.二维数组名和指针数组作实参
(1)二维数组名作实参时,形参必须为行指针
#include
main()
{int a[3][4];
…….
fun(a);
}
fun( int (*p)[4] )
fun(int p[][4] )
fun(int p[3][4] )
形参为一个存储单元
(2)指针数组名作实参时,形参必须为一个指向指针的指针
#include
main()
{int a[3][4],*p[3];
……
for(i=0;i<3;i++) p[i]=a[i]; p[0]=&a[0][0]
fun(p);&p[0]
…….
}
fun(int **q ) q=p &p[0]
fun(int *q[] )
fun(int *q[3] )
8.二维数组程序举例
0701 数组行交换
0201 数组列交换