第 9 章数 组
9.1.1一维数组的定义定义方式为,
类型说明符 数组名 [ 常量表达式 ] ;
9.1 一维数组的定义和一维数组元素的引用例如,int a[8];
1)定义 a[8]后,a数组有 8个元素,分别是,a[0],
a[1],a[2],a[3],…,a[7]
2) 类型名 int规定了数组中每个元素都是整型,
只能存放整型数据
3)下标从 0开始,所有最后一个元素的下标应为 7
4) c编译程序将为 a数组在内存中开辟如图的 8
个连续存储单元,可以用这样的名字直接来引用各存储单元
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7]
说明,数组同变量一样,也必须先定义,后使用 。
1维数组是只有 1个下标的数组,同时定义多个数组的形式如下:
如,double w[22],v[100],u[5]
( 1),数据类型,是指数组元素的数据类型 。
( 2) 数组名,与变量名一样,必须遵循标识符命名规则,。
( 3),常量表达式,必须用方括号括起来,指的是数组的元素个数(又称数组长度),它是一个整型值,其中可以包含常数和符号常量,
但不能包含变量。
注意,C语言中不允许动态定义数组。
( 4) 数组元素的下标,是元素相对于数组起始地址的偏移量,所以从 0开始顺序编号 。
( 5) 数组名中存放的是一个地址常量,它代表整个数组的首地址 。 同一数组中的所有元素,按其下标的顺序占用一段连续的存储单元 。
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7]
数组必须先定义,后使用。
只能逐个引用数组元素,不能一次引用整个数组。
由于是一维数组,因此引用数组元素时只带一个下标数组元素的表示形式:数组名 [下标 ]
如,a[2],a[4],a[0]
9.1.2一维数组元素的引用
下标可以是整型常量,也可以是整型表达式。 但是注意下标从 0开始,小于等于 7
在 c语言中,数组不能整体引用,数组名存放的是一个地址常量,它代表是整个数组的首地址。
9.1.3 一维数组的初始化
( 1)在定义数组时对数组元素赋初值。 例如:
int a[6] = {1,3,5,7,9,11};
( 2) 可以只给一部分元素赋初值。 例如:
int a[6]={1,3,5};
a[0],a[1],a[2] 的值分别是 1,3,5,其他元素值是 0。
(3) 对全部数组元素赋初值时,可以不指定数组长度,通过赋初值来定义数组的大小。
int b[3]={4,6,8}; 可以写成
int b[ ]={4,6,8};
注:当所赋值初值少于定义数组元素的个数时,将自动给后面的元素补以初值 0,
对于字符类型的补上,\0”
如,char c[5]={?@?}; 相当于
char c[5]={?@?,?\0?,?\0?,?\0?,?\0?}
9.1.5 一维数组的定义和数组元素引用程序举例例 9.1 编写程序,定义一个含有 30个元素的 int类型数组,依次给数组元素赋奇数 1,3,5,7,
9,….,;然后按每行十个数顺序输出,最后再按每行十个数逆序输出 。
#define SIZE 30
main()
{
int s[SIZE],i,k=1;
for (i = 0; i < SIZE ; k+=2,i++) s[i]=k;
printf(“\n顺序输出 \n”);
for(i = 0 ; i < SIZE; i++)
{
printf(“%4d”,s[i]);
if((i+1)%10==0) printf(“\n”);
}
printf(“\n逆序输出 \n”);
for(i = SIZE - 1; i >= 0 ; i--)
printf(“%3d%c”,s[i],(i%10 == 0)? ’\n’,’ ’);
printf(“\n”);
}
9.2 一维数组和指针
9.2.1一维数组和数组元素的地址
C语言中,在函数体中或在函数外部定义的 数组名 可以认为是一个存放地址值的指针常量,其值(地址)是数组第一个元素的地址,也就是数组所占一串连续存储单元的起始地址。
语句,a=&a[4],a++都是非法的,因为不能给 a重新赋地址值
a[0]
a[9]
aa = &a[0]?
一维数组的内存模型例如有如下定义:
int a[10];
其内存模型如下:
a[9]a[8]a[7]a[6]a[5]a[4]a[3]a[2]a[1]a[0]
a &a[0]
9.2.2 通过指针引用数组元素可以通过指针给数组元素赋值。
int a[10];
int *p;
p=&a[3];
*p=10; /*相当于 a[3]=1;*/
C语言 规定:若 p指向数组中的一个元素,
则 p +1 指向数组的下一个元素。
例如:若数组元素是实型,每个元素占 4个字节,则 p+1使 p的原值(地址)加 4个字节。
p+1所代表的地址,实际上是 p的原地址加 d,d
是一个数组元素所占的字节数
(整型 d=2,实型 d=4,字符型 d=1)。
若有如下定义:
int a[10],*p = a;
或 int a[10],*p = &a[0];
则:
p+i和 a+i就是 a[i]的地址,p+i和 a+i指向 a
数组的第 i个元素。 p+i和 a+i的实际地址是,a+i?d。
*(p+i)或 *(a+i)是指针 p+i或 a+i所指向的数组元素,即 a[i]。
实际编译时,将数组元素 a[i]处理成 *(a+i),找出该单元中的内容。
例如,a数组的首地址为 1000,则 a[3]的地址是
1000+3?2=1006,从 1006地址所标志的整型单元中取出的值,就是 a[3]的值。
指向数组的指针变量 p也可以带下标,如 p[i]与
*(p+i)等价。
a[9]a[i]a[3]a[1]a[0]
1000 1006
a
p p+i
a+i
引用一个数组 a的 元素,可以用 3种方法,
( 1)下标法,如形式 a[i]。
(2) 首地址加偏移量,如 *(a+i),其中 a为数组名,是数组的首地址。
( 3)指针法,*(p+i)。 p是指针变量,p=a。
例 整型数组 a有 10个元素,可用 3种方法输出各元素值。
1、下标法
main( )
{
int i,a[10];
for(i = 0; i < 10; i++) a[i] = i * i;
printf(“\n”);
for (i = 0; i < 10; i++) printf(“%d”,a[i]);
}
2、首地址加偏移量
main( )
{
int a[10],j;
for(j = 0; j < 10; j++)
a[j] = j * j;
printf(“\n”);
for(j = 0; j < 10; j++)
printf(“%d”,*(a+i));
}
3、指针法:
main( )
{
int a[10],*p,i;
for(i = 0; i < 10; i++)
a[i] = i * i;
printf(“\n”);
for(p = a; p < (a+10); p++)
printf(“%d”,*p);
}
使用指针变量要注意以下 4个问题:
( 1)指针变量可以使自身的值发生改变。
如 p++使 p的值不断改变。
如果将第 3种方法的后两行改为下面形式是错误的:
for(p = a; a < (p+10); a++)
printf(“%d”,*a);
因为 a是数组名,它是数组首地址,它的值在程序运行期间是固定不变的。
a++是无法实现的。
( 2)要注意指针变量的当前值。
例 输出 a数组的 10个元素。
main( )
{
int *p,i,a[10];
p=a;
for(i=0;i<10;i++)
{
p[i] = 2*i + 1;
p++;
}
printf(“\n”);
for(i = 0; i < 10; i++,p++)
printf(“%d”,*p);
}
运行程序,输出结果不是 a数组的元素值。
原因是:第 1个 for循环后,p已指向 a数组的末尾,
因此执行第 2个 for循环时,
p的起始值不是 &a[0]了。
解决的办法是:在第 2
个 for循环前,加一个语句:
,p=a;”,使 p的初始值为
&a[0]。
( 3)从上可以看到,虽然定义数组包含 10个元素,用 p指向数组元素,但指针变量可以指到数组以后的内存单元。
如果有 a[10],C不认为非法,把它按
*(a+10)处理。
在定义数组时,可以不定义数组的长度。
如将实例中的定义改为,int *p,i,a[ ];,,不指定 a数组长度,用 p来指向数组各个元素,程序运行结果相同。
( 4)注意指针变量的运算。
如果先使 p指向数组 a(即 p=a),则:
1,*(p++)与 *(++p)作用不同。 前者是先取 *p的值,再使
p 加 1。后者是先使 p加 1,再取 *p。
2,(*p)++表示 p所指向的元素值加 1。 即 (a[0])++,若
a[0]=3,则 (a[0] )++的值为 4。
3、如果 p当前指向 a数组中第 i 个元素,则:
*( p - -)相当于 a[i- -],先取 p值作,*”运算,再使 p自减。
*(++p)相当于 a[++i],先使 p自加,再作 *运算。
*( - -p )相当于 a[- -i],先使 p自减,再作 *运算。
将 ++和 - - 运算符用于指针变量,可使指针变量自动向前或向后移动,指向上一个或下一个数组元素。 例:
p=a;
while(p < a+100)
printf(“%d”,*p++);
9.3 函数之间对一维数组和数组元素的引用
9.3.1 数组元素作实参当调用函数时,数组元素可以作为实参送给形参,
方式和普通变量一样,对应的形参也必须是相同类型的变量,如 max(a[0],a[1]);
9.3.2 数组名作实参数组名也可以作为实参传送,但数组名本身是一个地址值,因此,对应的形参就应该是个指针,并且指针的基类型必须与数组类型对应一致,
例 将 a数组中 n个整数按相反顺序存放。
void inv(int x[ ],int n)
{
int tmp,i,j,m = (n-1)/2;
for(i = 0; i <= m; i++)
{
j = n-1-i;
tmp = x[i]; x[i] = x[j]; x[j] = tmp;
}
}
3 7 9 11 0 6 7 5 4 2
i m j
main( )
{
int i,a[10] = {3,7,9,11,0,6,7,5,4,2};
printf(“The original array:\n”);
for(i = 0; i < 10; i++)
printf(“%d,”,a[i]);
printf(“\n”);
inv(a,10);
printf(“The array has been invertde:\n”);
for (i = 0; i < 10; i++)
printf(“%d,”,a[i]);
printf(“\n”);
}
数组名代表数组的首地址,数组名作实参,
实际是把数组的首地址传给形参。这样,实参数组和形参数组共占同一段内存。形参数组的元素值发生变化时,实参数组的元素值相应变化。 数组名作为形参时,C编译系统是将行参数组名作为指针进行处理的。故在函数中定义:
void inv(int x[ ],int n) 与 void inv(int *x,int n)
是等价的 。
例 9.3 解法 2:将函数 inv中的形参 x改成指针变量。
void inv(int *x,int n)
{
int *mid,tmp,*first,*last,m,
m = (n-1)/2;
first = x; last = x + (n-1); mid = x + m;
for(; first <= mid; first++,last--)
{
tmp = *first; *first = *last; *last = tmp;
}
}
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
first=x?
mid=x+m?
last=x?
main( )
{
int i,a[10]={3,7,9,11,0,6,7,5,4,2}
printf(“The original array:\n”);
for (i=0;i<10;i++) printf(“%d,“,a[i]);
printf(“\n”);
inv(a,10);
printf(“The array has been inverted:\n”);
for (i=0;i<10;i++) printf(“%d,“,a[i]);
printf(“\n”);
}
9.3.3 数组元素地址作为实参
#define M 10
#define B 4
void setstar(char *,int);
void arrout(char *,int);
main()
{
char c[m]={?A?,?B?,?C?,?D?,?E?,?F?,?G?,?H?,?I?,?J?};
setstar(&c[4],M-B);
arrout(c,M);
}
当用数组元素地址作为实参时,由于是地址值,所以对应的形参也应当是基类型相同的指针变量,
例 9.3:编写函数,对具有 10个元素的 char类型数组,从下标为 4的元素开始,
全部设置为,*”,保持前四个元素内容不变
void setstar(char *a,int n)
{
int i;
for(i = 0; i < n; i++) *(a+i)=?*?;
}
void arrout(char *a,int n)
{
int i;
for(i = 0; i < n; i++) printf(“%c”,a[i]);
printf(“\n”);
}
9.3.4 函数的指针形参和函数体中数组的区别若有以下程序,程序 fun函数,形参 a指向 w数组,函数体内定义了一个 b数组,函数把 b数组的起始地址值作为函数值返回,企图使指针 p指向函数体内 b数组的开头,是否可以呢
#define N 10
int *fun(int a[],int n)
{
int b[n]; 2.函数体中数组
return b;
}
main()
{ int w[n],*p;
p = fun(w,n);
}
1.函数的指针形参
1.形参 a和数组 b具有完全不同的含义 ;
形参 a在编译时将其作为一个指针变量处理,可以进行指针变量的运算,如 a++
而数组 b是系统开辟的一串连续单元,不能重新赋值
当函数 fun执行完之后,系统将释放 a和 b所占的存储单元,所以,
不能通过 return b来返回地址
9.4 一维数组应用举例例 9.4 编写程序,定义一个含有 15个元素的数组,并编写函数分别完成以下操作,
1)调用 random随机函数给所以元素赋 0—
50之间的随机数
2)输出数组元素
3)按顺序对每隔三个数求一个和数,并传回主函数
4)最后输出所有求出的和值,
#include,stdlib.h”
#define SIZE 15
#define N 3
void getrand(int*,int);
void priarr(int*,int);
void getsum(int*,int*,int);
main()
{
int x[SIZE],w[SIZE/N] = {0};
getrand(x,SIZE);
printf(“输出 %d个随机数,\n”,SIZE);
priarr(x,SIZE);
getsum(x,w,SIZE);
printf(“输出 5个和数 \n”);
priarr(w,SIZE/N);
}
void getrand(int *a,int n)
{ int i ;
for(i = 0 ;i < n ; i++)
a[i] = random(50);
}
void priarr(int *a,int n)
{ int i;
for(i = 0 ; i < n ; i++)
{ printf(“%5d”,a[i];)
if((i + 1)%5==0)
printf(“\n”);
}
}
void getsum(int *a,int *b,int n)
{ int i,j,sum;
for(sum = 0,i = 0,i = 0; i < n; i++)
{
sum+= a[i];
if((i + 1)%3) == 0
{
b[j] = sum;
j++;
sum = 0;
}
}
}
例 9.7:已知存放在 a数组中的数不相重,
在 a数组中查找和 x值相同的元素的位置。
若找到输出该值和该值在 a数组中的位置,若没找到,输出相应的信息。
#include <stdio.h>
#define NUM 30
int arrin(int*);
int search(int*,int,int);
main()
{ int a[NUM],x,n,p;
n = arrin(a);
printf(“Enterthe number to search:x=”);
scanf(“%d”,&x);
p = search(a,x,n);
if(p != -1)
printf(“%dindex is:%d\n”,x,p);
else
printf(“%d don?t be found!\n”,x);
}
int arrin(int *a)
{
int i,n;
do {
printf(“Enter number of elements,0 < n < %d:”,NUM);
scanf(“%d”,&n);
} while((n < 1) || (n >= Num));
printf(“Enter %d integer number,\n”,n);
for(i = 0; i < n; i++)
scanf(“%d”,a + i);
return n;
}
int seach(int *a,int x,int n)
{
int i,p;
i = 0;
a[n] = x;
while(x !=a[i]) i++;
if(i == n)
p = -1;
else
p = i;
return p;
}
例 9.8 w数组中存放 n个数据,
编写函数删除下标为 k的元素中的值。
程序中用到两个函数 arrdel和 arrout,前者用于删除数组中指定下标的元素,后者用于输出数组的元素。
删除数组中指定下标的元素实质上是将该下标以后的所有元素向前移动。
#include <stdio.h>
#define NUM 10
int arrdel(int*,int,int);
void arrout(int*,int);
void main()
{
int a[NUM] = {21,22,23,24,25,26,27,28,29,30};
int d,n = NUM;
printf("Output array data:\n";
arrout(a,n);
printf("Input delete index(0--NUM-1):\n");
scanf(%d",&d);
n = arrdel(a,n,d);
printf("Output array data after delete:\n");
arrout(a,n);
}
void arrout(int* a,int n)
{
int k;
for(k = 0; k < n; k++)
printf("%d",a[k]);
printf("\n");
}
int arrdel(int* w,int n,int k)
{
int i;
for(i = k; i < n - 1; i++)
w[i] = w[i + 1];
n--;
return n;
}
例 9.9 用选择法对数组中的元素排序
(由小到大)
算法:
用变量 p记下数组中最小元素的位置,
用就是 m存放该最小数,依次进行比较。
#include <stdio.h>
#define NUM 10
void arrsort(int*,int);
void arrout(int*,int);
void main()
{
int a[NUM] = {5,7,4,2,8,6,12,3,1,9};
arrsort(a,NUM);
arrout(a,NUM);
}
void arrsort(int* a,int n)
{
int i,j,p,t;
for(j = 0; i < n - 1; j++)
{
p = j;
for(i = j + 1; i < n; i++)
if(a[p] > a[i]) p = i;
if(p != j)
{
t = a[j]; a[j] = a[p]; a[p] = t;
}
}
}
9.5 二维数组的定义和二维数组元素的引用
9.5.1 二维数组的定义。如:
int a[3][4]; //二维整型数组,3行 4列共 12个元素
double x[5][10]; //5行 10列共 50个元素
char ch[5][20];
二维数组可看成矩阵或表格例如,int a[3][4];
表示有 3x4=12个元素的二维数组,每个元素都是 int变量。 12个元素分别是:
第 0列 第 1列 第 2列 第 3列第 0行 a[0][0] a[0][1] a[0][2] a[0][3]
第 1行 a[1][0] a[1][1] a[1][2] a[1][3]
第 2行 a[2][0] a[2][1] a[2][2] a[2][3]
C语言的数组 行为先 。
二维数组的存储方式
二维数组虽然可看成是表格(或矩阵),
但它是静态的,在编译时就分配存储空间,并且必须是连续的内存单元。
例如,int a[3][4]; 共 12个 int元素变量,占用内存中连续的 24个字节的存储空间。
因为内存不可能像表格一样是二维的,
所以,C编译程序将二维数组也象一维数组一样按行存储。
按行存储的含义:
对于二维数组,C编译程序是在内存中先存储第 0行,然后紧接着存储第 1行,第 2
行,以此类推。 int a[3][4]的内存模型如下所示:
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]
第 0行 第 1行 第 2行既然二维数组在内存中是按行存储,所以可以通过指针依次访问它的每一个元素,
即像访问一维数组一样访问它的每一个元素。如:
main()
{
int a[3][4];
int i,*pa;
pa = &a[0][0]; /*不可写成 pa = a*/
for(i = 0; i < 12; i++)
*(pa + i) = 2*i + 1;
}
main()
{
int a[3][4];
int i,*pa;
for(pa = &a[0][0],i = 0; i < 12; pa++,i++)
*pa = 2*i + 1;
for(pa = &a[0][0],i = 0; i < 12; pa++,i++)
printf("%4d",*pa);
}
main()
{
int a[3][4];
int i,*pa;
pa = &a[0][0],;
for(i = 0; i < 12; i++)
pa[i] = 2*i + 1;
}
main()
{
int a[3][4];
int i,j,*pa;
pa = &a[0][0],;
for(i = 0; i < 12; i++)
pa[i] = 2*i + 1;
for(i = 0; i < 3; i++)
for(j = 0; j < 4; j++)
printf("%4d",a[i][j]);
}
二维数组和一维数组的关系
在 C语言中,可以把一个二维数组看成一个一维数组,每个数组元素又是包含有若干个元素的一维数组。
如,int a[3][4]; 可看成一个一维数组,这个一维数组有三个元素,分别是,a[0],a[1],a[3]。
其中每个元素又是由 4个元素组成的一维数组。
如元素 a[0]本身又是一个一维数组,它由 4个元素组成,分别是,a[0][0],a[0][1],a[0][2],
a[0][3]。
由此可得如下结论:
对于二维数组 int a[3][4];
(1)把数组 a看成一个一维数组,它由 3个元素组成,分别是,a[0],a[1],a[3]。 所以
a就是这三个元素的首地址。即:
a 和 &a[0]是等价的,都表示在内存中的首地址。即 *a 或 *(a + 0) 和 a[0]是等价的 。
(2) *(a+i) 和 a[i] 是等价的。
(3)由于把数组 a看成一个一维数组,它由
3个元素组成,分别是,a[0],a[1],a[3]。
又由于每一个元素又是一个一维数组。
如:元素 a[1]是由 a[1][0],a[1][2],a[1][3],
a[1][4]这四个元素组成的一维数组,所以
a[1]是这个一维数组的首地址,即:
a[1]和 &a[1][0]是等价的同理
a[0]和 &a[0][0]等价
a[2]和 &a[2][0]等价
(4)既然
a[1]和 &a[1][0]等价
a[0]和 &a[0][0]等价
a[2]和 &a[2][0]等价,所以:
*(a[0] + 0) 就表示 a[0][0]
*(a[0] + 1) 就表示 a[0][1]
*(a[0] + 2) 就表示 a[0][2]
*(a[2] + 2) 就表示 a[2][2]
结论:
*(a[i] + j) 就表示 a[i][j]
(5) 由于 *(a[i] + j) 表示 a[i][j] (结论 4)
又由于 *(a+i) 和 a[i] 是等价的 (结论 2)。
所以:
*(*(a + i) + j) 就表示 a[i][j]
如果有一个指针变量 pa可以等价于 a(a是常量 ),则上述式子可以写成:
*(*(pa + i) + j) 就表示 a[i][j]
9.5.3 二维数组的初始化
初始化时初值个数与数组元素个数相同
int a[4][3] = { {1,2,3},
{4,5,6},
{7,8,9},
{10,11,12} };
初始化时每行初值个数少于列数
int a[3][4] = { {1,2},
{3,4,5},
{6,7,8,9} };
1 2 0 0 3 4 5 0 6 7 8 9
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]
初值行数少于数组行数
int a[3][4] = { {1,2},
{4,5,6} };
1 2 0 0 4 5 6 0 0 0 0 0
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]
初始化时省略行花括号
int a[3][4] = {1,2,3,4,5,6,7};
1 2 3 4 5 6 7 0 0 0 0 0
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]
9.5.4 通过赋初值定义二维数组的大小
对于二维数组,如果定义的时候初始化,可以省略第一维的大小,此时 C编译系统可以根据初值的个数自动确定第一维的大小。
int a[ ][4] = { {1,2,3},
{4,5},
{6},
{8} };
数组 a的第一维是 4
int b[ ][3] = {1,2,3,4,5};
数组 b的第一维为 2,其中 b[1][2] = 0
9.5.5 二维数组的定义和元素引用举例
例 9.10 通过键盘给 2X3的二维数组输入数据,第一行赋 1,2,3,第 2行赋 10,20,30,
然后按行输出此二维数组。
void main()
{
int a[2][3];
int i,j;
printf("Input 6 integers:\n");
for(i = 0; i < 2; i++)
for(j = 0; j < 3; j++)
scanf("%d",&a[i][j]);
printf("Output these 6 integers:\n");
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
printf("%d",a[i][j]);
printf("\n");
}
}
9.6 二维数组和指针
9.6.1 二维数组和数组元素的地址
对于定义 int a[3][4],*p;
二维数组 a由 3个一维数组组成
每个一维数组又是由 4个元素组成
二维数组名是地址常量,是行指针,其基类型是 4X2个 int(8byte),所以 p = a是不合法的,因为其基类型不同。
a + 1 指向 a[1],1表示其基类型是 8个字节
9.6.4 通过建立一个行指针来引用二维数组元素对于定义 int a[3][4];
若要定义一个行指针来指向二维数组 a,必须这样定义指针:
int (*pa)[4]; 才能有如下赋值
pa = a;
在这里,pa先与 *结合,表示 pa是一个指针变量,然后再与 [4]结合,表示 pa的基类型是一个包含有 4个 int元素 (8byte)的数组
这样一来,所以出现 a的表达式都可以用
pa来代替。如:
a[i][j]pa[i][j]
*(a[i] + j)*(pa[i] + j)
*(*(a+i) + j)*(*(pa+i) + j)
(*(a+i))[j](*(pa+i))[j]
9.6.3 通过建立一个指针数组来引用二维数组元素
定义 int a[3][4],*p[3];
在这里,p先与 [3]结合,表示 p是一个数组名,系统将分配 3个连续的存储单元,
然后再与 *结合,表示数组 p是指针数组,
即数组 p的 3个元素都是 int *。 所以如下赋值合法:
p[i] = a[i]; /*0≤i <3*/
for(i = 0; i < 3; i++) p[i] = a[i];
执行上述语句后,指针变量 p[i]的指向如下图:
a[i][j]*(a[i] + j)*(p[i] + j)
a[i][j]*(*(a +i) + j)*(*(p+i)+j)
a[i][j](*(p+i))[j](*(a+i))[j]
a[i][j]p[i][j]
9.7 二维数组名和指针数组作为函数的参数
9.7.1 二维数组名作为函数的参数
形参必须是一个行指针,即基类型要一致。如:
int a[3][4];
fun(a);
则函数原型中形参应该为下面三种情况之一:
(1) fun( int m[3][4] )
(2) fun( int m[ ][4] )
(3) fun( int (*p)[4] )
9.7.2 指针数组作为函数参数
指针数组名是一个指向指针的指针常量。如:
int a[3][4],*p[3];
for(i = 0; i < 3; i++) p[i] = a[i];
fun(p); //调用函数,p作为实参则函数原型中形参应说明为以下形式之一:
(1) fun( int *pt[3] )
(2) fun( int *pt[ ] )
(3) fun( int **pt )
9.8 二维数组程序举例
例 9.11 调用系统随机函数 rand()给 5X6
的二维数组赋 10- 40的整数,然后求出数组每行的平均值并输出。
解:定义三个函数,GetData()用于赋值,
average()用于求每行平均值,output()用于输出平均值。
#include <stdio.h>
#include <stdlib.h>
#define ROW 5
#define COL 6
void GetData(int (*)[COL]);
void average(int [][COL],float *);
void output(int [ROW][COL],float *);
void main()
{
int a[ROW][COL];
float aver[ROW];
GetData(a);
average(a,aver);
output(a,aver);
}
void GetData(int (*p)[COL])
{
int i,j;
for(i = 0; i < ROW; i++)
for(j = 0; j < COL; j++)
p[i][j] = 10 + random(31);
}
void average(int p[][COL],float *b)
{
int i,j;
float ave;
for(i = 0; i < ROW; i++)
{
ave = 0.0;
for(j = 0; j < COL; j++)
ave += p[i][j];
b[i] = ave / COL;
}
}
void output(int p[ROW][COL],float *b)
{
int i,j;
for(i = 0; i < ROW; i++)
{
for(j = 0; j < COL; j++)
printf("%4d",p[i][j]);
printf("average = %6.2f\n",b[i]);
}
}
例 9.12 编程打印 7行杨辉三角
定义 7X7的二维数组来存储数据
杨辉三角的第 0列和对角线上的元素值都为 0;
除第 0列和对角线上的元素外,其他元素值为前一行同列元素与前列元素值之和
函数 SetData()用于设置杨辉三角元素值,
函数 outputData()用于输出
#include <stdio.h>
#define N 7
void SetData(int (*)[N]);
void outputData(int (*)[N]);
void main()
{
int yanghui[N][N];
SetData(yanghui,N);
outputData(yanghui,N);
}
void SetData(int (*s)[N])
{
int i,j;
for(i = 0; i < N; i++)
{
s[i][0] = 1;
s[i][i] = 1;
}
for(i = 2; i < N; i++)
for(j = 1; j < i; j++)
s[i][j] = s[i -1][j -1] + s[i - 1][j];
}
void outputData(int (*p)[N])
{
int i,j;
for(i = 0; i < N; i++)
{
for(j = 0; j <= i; j++)
printf("%6d",p[i][j]);
printf("\n");
}
}
例 9.13 找出方阵(二维数组的行列数相等)
中每列的最小元素及其行号
#include <stdio.h>
#include <stdlib.h>
#define M 5
void SetData(int (*)[M]);
void FindMin(int (*)[M],int *);
void output(int (*)[M],int *);
void main()
{
int a[M][M];
int min[M];
SetData(a);
FindMin(a,min);
output(a,min);
}
void SetData(int (*p)[M])
{
int i,j;
for(i = 0; i < M; i++)
for(j = 0; j < M; j++)
*(*(p + i) + j) = random(101);
}
void FindMin(int (*p)[M],int *b)
{
int i,j,ColofMindata;
for(i = 0; i < M; i++)
{
ColofMindata = 0;
for(j = 1; j < M; j++)
if(p[j][i] < p[ColofMindata][i])
ColofMindata = j;
b[i] = ColofMindata;
}
}
void output(int (*p)[M],int *b)
{
int i,j;
for(i = 0; i < M; i++)
{
for(j = 0; j < M; j++)
printf("%6d",p[i][j]);
printf("\n");
}
printf("-----------------------------------------\n");
for(i = 0; i < M; i++)
printf("%6d",b[i]);
printf("\n");
}