【C语言之数组初体验】

目录

1.介绍数组

2.一维数组

3.数组做参数

4.二维数组

5.数组的应用举例

1.介绍数组
程序内也需要容器,只不过该容器比较特殊,在程序中是连续一块的,大小固定并且里面的数据类型一致的内存空间,它还有个名字叫数组。可以将数组理解为大小固定,所放物品为同类的一个购物袋,在该购物袋中的物品是按照一定顺序放置的。

2.一维数组
(1)一维数组的定义和应用
数组是统一命名的一组变量,它们具有相同的名字和相同的数据类型,可以用数组名和每个元素在数组的位置来访问数组元素。下图显示了一个名为num的整型数组,包含五个元素,分别为num[0],num[1],num[2],num[3],num[4].通过在数组名的后面加上方括号[],括起来的位置号就可以实现对数组任意一个元素的访问。
在这里插入图片描述
用方括号括起来的位置号称作数组的下标。需要注意的是,数组的下标从0开始,首元素的下标是0,第 i 各元素的下标是 i-1 。
数组名代表的是数组在内存中的起始地址,即首元素的地址,而下标表示的是该元素相对数组起始地址的偏移量,所以首元素的下标为 0,表示相对数组起始地址偏移量为 0,第2个元素下标为 1,表示相对数组起始地址偏移量为 1。
一维数组订定义格式如下:

类型名 数组名[数组长度]

数组名是整个数组的标识符,数组名可以看作是一种特殊的变量名,需要遵循变量名的命名规则;数组长度指出数组中可以包含的数据元素的个数,数组长度通常是一个整型常量或常量表达式。例如:

int a[10]; /定义一个含有10整型元素的一维数组/
char c[200]; /定义一个含有200个字符元素的一维数组/
double arr[5]; /定义一个含有5个浮点型元素的一维数组/

数组中每个值都有一个引索值——下标来实现,下标是一个整数,放在数组名的后面的方括号中。一维数组元素引用格式如下:

数字组名 [下标]

其中,下标可以使用整型常量、变量、表达式,下标的取值范围为:[0,数组长度-1]。注意下标不能越界,即下标不能使用取值范围以外的值,否则就是非法内存访问。
例如:int a[10];
数组 a 中包含十个元素:a[0]、a[1]、……a[9]。
数组元素使用方法与同类型的变量相同。例如:
从键盘读入一个整数,存入a[i];

scanf{
    
    "%d",&a[i]);

下标为index的元素与下标为k的元素互换内容

temp=a[index];
a[index]=a[k];
a[k]=temp;

输出a[i]的值

printf("%d",a[i]);

(2)一维数组初始化
数组定义后,可以把数组元素当作一般变量来使用。与变量类似,在引用数组之前,需要为数组元素赋初值。
若数组声明为静态存储类型,则数组所有元素被初始化为0,如下:

static int a[10];

定义数组a有10个int类型的元素,每个元素的初始值为0。
但自动存储类型的数组如果没有初始化,所有元素为垃圾值。例如:

int a[10];

定义数组a有10个int类型的元素,每个元素的值是垃圾值。
为数组元素赋初值可以在定义数组是在进行,也可以在定义数组之后进行,在定义数组时对数组元素赋初值,成为数组初始化。
数组初始化格式:

类型名 数组名[数组长度] = [初值表];
例如:

int a[10] = {
    
    1,2,3,4,5,6,7,8,9,10};

初值表中包含10个整型常量,用来给数组 a 中的 10 个元素赋值。数组初始化后,数组元素的初值为:a[0]值为 1,a[1]值为2,……a[9]值为10。
也可以只对部分数组元素初始化。如果初值表中数据个数比数组长度少,那么数组中剩余的赋值为0.例如:

int a[10] = {
    
    1,2,3};

初始化后数组a的元素的初值为:a[0]值为1,a[1]值为2,a[2]值为3,其余元素值为0.
利用这一特性可以很容易地把数组所有元素初始化为0:

int a[10] = {
    
    0};

如果对全部元素赋初值,可以省略数组长度。例如:

int a[10] = {
    
    0,1,2,3,4,5,6,7,8,9};

可以写成

int a[] ={
    
    0,1,2,3,4,5,6,7,8,9};

下面介绍经典的fibonacci数列。
某人有一对刚出生的兔子饲养在围墙中,如果它们在两个月后每个月生一对兔子,且新生的兔子在两个月后也是每个月生一对兔子,问一年后围墙中共有多少个兔子,假定没有兔子死亡。
问题分析:第1个月和第2个月都只有最初的这一对兔子,到第3个月,这一对兔子生下一对兔子,围墙内有两对兔子。到第4个月仍是最初的一对兔子生下一对兔子,共有3对兔子,到第5个月除最初最初的兔子新生一对兔子外,第3个月出生的兔子也开始生兔子,因此共有5对兔子。到第 i 个月,凡是第 i-2 个月存在的兔子都会生一对兔子,所以每个月的兔子总数 f(i) 可由前两个月的兔子数相加而得,因为上一个月存在的兔子数是 f(i-1),而当月新出生的兔子数为 f(i-2)。
由此得出fibonacci数列,又称黄金分割数列,指这样一个数列:1、1、2、3、5、8、13、21、……。在数学上,fibonacci数列以如下递归的方法定义:f(0)=1,f(1)=1,f(n)=f(n-1)+f(n-2) (n>=2)。
解决问题:fibonaaci数列。
输入一个正整数 n (1<n<=46),输出fibonacci数列的前 n 项。程序运行效果:

请输入整数 n (1<n<=46) :10
fibonacci 数列
1 1 2 3 5 8 13 21 34 55

解题思路:
(1)定义数组 f 存放 fibonacci 数列,数列的前两项 f[0] 和 f[1] 赋值为1:

f[0] = 1;
f[1] = 1;

或者在定义数组 f 时初始化:

int f[N] = {1,1};

(2)从第三项开始 f[2] 开始,以后的每个元素都是由其前两项相加得到的,用循环变量控制下标变化,i 从 2 开始递增到 n,实现代码如下:

for(i = 2;i<=n;i++)
	f[1] = f[i-1] + f[i-2];

完整代码:

#include<stdio.h>
int man(void)
{
    
    
	int f[N];
	int n,i;
	printf("请输入整数n(1<n<=46):");
	scanf("%d",&n);
	f[0]=1;
	f[1]=1;
	for(i=2;i<n;i++)
	{
    
    
		f[i]=f[i-1]+f[i-2];
	}
	for(i=0;i<n;i++)
	{
    
    
		printf("%d",f[i]);
		printf("\n");
	}
	return 0;
}

3.数组做参数
数组作为函数参数
数组经常被用作实际参数。若将一个数组作为实际参数传递给函数,只需不带方括号的数组名即可。若数组 a 定义为:

int a[10];

若将数组 a 作为实参传递给被调用函数SumArr(),则调用语句可写为:

SumArr(a,10);

因为数组名代表数组首元素的地址,因此数组名做参数就可以将数组的起始地址传递给形参。另外需要将数组元素的大小也传递给被调用函数。
而在定义函数SumArr()期望用形参array来接收一个整型数组,用形参n来接受数组元素个数。注意在声明形参数组时,数组元素的个数不需要写在方括号中,即便方括号中出现数字,编译器也将忽略该数字。在编译器看来,形参array不是一个真正的数组,只是一个可以存放地址的指针变量。(下一篇文章我们将会讲到数组与指针的紧密联系)
一般变量做参数时,是把实参的值传递给形参,形参的改变不会影响实参,但是数组做参数时则完全不同,因为数组名做实参把首元素的地址传给了被调函数,这样被调函数就能准确知道数组存储在哪里,所以被调函数访问的是主调函数中的原数组。

4.二维数组
如果说把一维数组看作是一行多列的话,那么二维数组就是采用多行多列的方式组织数据。这种数据组织方式在日常生活中很常见,如矩阵、表格等。
二维数组
二维数组定义格式如下:

类型名  数组名[行数][列数]

例如:

int a[3][2];//定义二维数组a,3行2列,6个元素
int b[5][10];//定义二维数组b,5行10列,50个元素

二维数组的元素数量等于行数和列数的成绩。与一维数组类似,二维数组的类似,二维数组的引用也必须遵循”先定义,后引用“的原则。
二维数组的引用格式如下:

数组名[行下标][列下标]

其中,行下表和列下标可以使用整型常量、变量或表达式。行下标的取值范围是[0,行数-1],列下标的取值范围是[0,列数-1]。注意下标不要越界,即二维数组的行下标和列下标都不能超出其取值范围。例如:

int a[3][2];//3行2列的二维数组,6个元素

二维数组 a 的元素列表如下:

a[0][0]  a[0][1]
a[1][0]  a[1][1]
a[2][0]  a[2][1]

二维数组元素在内存中按照先行后列的顺序连续储存。
二维数组的初始化
与一维数组类似,二维数组可以在数组定义时给数组元素赋初值,即初始化。
1.分行初始化
例如:

int a[3][3] = {
    
    {
    
    1,2,3},{
    
    4,5,6},{
    
    7,8,9}};

初始化3行3列的数组a,将每一行的3个整型数值用逗号分开,放入一对花括号中,3行一共3组花括号,中间用逗号分隔开,外边再加上一对花括号。按照C语言规范,把数组看作一个行列结构的表,从上往下,第一行的行列号 0,第二行行号是 1,从左往右,第一行的列号是 0,第二行的列号是 1,以此类推。因此,a[0][0]对应数值 1,a[2][2]对应数值9。

数组 a
1 2 3
4 5 6
7 8 9

数组 b
1 2 3
4 0 0
5 6 0
0 0 0

也可以只给数组部分元素赋值。例如:

int b[4][3] = {
    
    {
    
    1,2,3},{
    
    4},{
    
    5,6}};

上述代码的作用是:在对4行3列的数组b进行初始化过程中只给出了3行数据,没有第四行,除了第1行之外,第2、3行的数据也未完全给出,没有给出初始值的元素被赋值为0。

数据初始化
与一维数组初始化一样,使用一组花括号,将所有数组元素的初始值按数组元素的存储顺序放入花括号,数值之间用逗号分隔开。顺序初始化时,可以给数组所有元素赋值,也可以只给数组部分元素赋值。

int a[3][3] = {
    
    1,2,3,4,5,6,7,8,9};
int b[4][3] = {
    
    1,2,3,4,0,0,5,6};

得到的数组a,b与前面分行初始化的数据a,b完全一样。二维数组初始化时可以不给出行数,编译器会根据初值的个数和数组的列数计算出数组的行数,但不推荐这样做。例如:

int a[][3] = {
    
    1,2,3,4,5,6,7,8,9};

使用二维数组编程时,通常通过二重循环遍历二维数组,即将行下标作为外循环的循环变量,将列下标作为内循环的循环变量。

for(i=0;i<m;i++)/*i 从变到m-1,处理m行*/
{
    
    
	for(j=0;j<n;j++)/*处理第 i 行的 n 列*/
	{
    
    
		/*对数组元素的具体操作*/
	}
}

二维数组做函数参数
可以用二维数组名作为实参或者形参。若将一个二维数组作为实际参数传递给函数,只需不带方括号的数组名即可。若数组 a 定义为:

int a[5][10];

若要将数组 a 作为实参传递给被调用函数SumArr(),则调用语句可以写为:

SumArr(a);

因为数组名代表首元素的地址,因此数组名做参数就可以将数组的其实地址传递给形参。
被调用函数中对形参数组定义时,可以指定所有维数的大小,也可以省略第一维的大小说明,如:

int SumArr( int a[5][10] );
int SumArr( int a[][10] );

二者都是合法的。但是不能把第二维或者更高维的大小省略,如下面的定义就是不合法的:

int SumArr( int a[][] );
int SumArr( int a[5][]);

不能省略第一维之外的原因和多维数组在C语言的存储方式有关,前面讲过C语言是按照“先行后列”的顺寻来储存数组的,即先储存第0行的元素,然后是第1行的元素,依此类推。所以编译器必须知道每行元素的个数,才能正确计算arr[i][j]的地址。所以,如果形式参数是多维数组,在声明参数时只能省略第一维的长度。
一般情况下,数组实际元素的行数和列数,常常小于数组定义时的行数和列数,故传递数组时常常需要传递实际使用的行数和列数。
若数组 a 定义为:

int a[5][10];

而实际存储的是一个3行4列的矩阵:

1 3 5 7
9 8 7 6
2 4 6 8

则调用函数SumArr()计算该矩阵所有元素和时,调用函数可写为:

SumArr(a,3,4);

在该调用函数中对形参数组声明形式如下:
int SumArr( int a[5][10],int m,int n); /计算a中前m行n列元素的和/
多维数组初始化需要注意以下事项:
1.采用第一种初始化数组声明必须指定列的维数。因为系统会根据数组中元素的总个数来分配空间,当知道元素总个数以及列的维数后,会直接计算出行的维数,
2.采用第二种初始化时数组声明必须同时指定行和列的维数。
多维数组的遍历
多维数组也是存在遍历的,和一维数组遍历一样,也是需要用到循环。不一样的就是多维数组需要采用嵌套循环。

注意:多维数组的每一维下标均不能越界。

猜你喜欢

转载自blog.csdn.net/z2004cx/article/details/128407017