11.数组

//数组
/*
C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 numbers[0]、numbers[1]、...、numbers[99] 来代表一个个单独的变量。数组中的特定元素可以通过索引访问。
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
*/

//声明数组
/*
	在c中,如果声明一个数组,需要指定元素的类型和元素的数量,如下所示:
		type arrayName[arraySize];
	这叫做一维数组。arraySize 必须是一个大于零的整数常量,type 可以是任意有效的 C 数据类型。例如,要声明一个类型为 double 的包含 10 个元素的数组 balance,声明语句如下:
		double balance[10];
	现在balance是一个可用的数组,可以容纳10个类型为double的数字
*/

//初始化数组
/*
	在c中,可以逐个元素初始化数组,也可以使用初始化语句,如下所示:
		double balance[5]={100.0, 2.4, 10.55, 2.6, 4.2};
	大括号之间的元素个数,不能大于我们在声明数组时[]中指定的元素数目
	如果省略了[]中的数字,那么数组的个数则为{}元素的个数
	访问数组的元素可以通过索引来获取,balance[1],表示获取balance数组中的第二个元素
*/
#include <stdio.h>
int main01()
{
	int a[]={1,2,3,4,5};
	printf("value count of a=%d\n",sizeof(a)/sizeof(a[0])); //value count of a=5
}

int main02()
{
	//声明一个数组n,里面有是个元素。
	//注意:如果不指定数组的个数的话,那么必须要在定义的时候初始化,也就是赋值
	//这样编译器才知道到底要给数组分配多大的空间,如果定义的时候没有初始化,那么必须指定数组的个数
	//也就是不存在int array[]; 这样肯定会报错的
	int n[10];
	int i,j;
	//初始化数组元素
	for(i=0;i<10;i++)
	{
		n[i] = i+100;//给数组的元素设置为i+100
	}
	//输出数组的值
	for(j=0;j<10;j++)
	{
		printf("n[%d]=%d\n",j,n[j]);
	}
	/*
		n[0]=100
		n[1]=101
		n[2]=102
		n[3]=103
		n[4]=104
		n[5]=105
		n[6]=106
		n[7]=107
		n[8]=108
		n[9]=109
	*/
}

//多维数组
/*
	C语言支持多维数组,多维数组的声明一般如下
		type arrayName[size1][size2]····[sizen];
	如果想创建一个三维数组,可以定义如下:
		int pic[1080][1920][3];
	最常用的还是二维数组,type arrayName[size1][size2];
	比方说定义一个3行4列的数组:
		int a[3][4]={
			{0,1,2,3},  //行号为1
			{4,5,6,7},  //行号为2
			{8,9,10,11} //行号为3
		};
	内部嵌套的括号是可选的,下面与上面的初始化是相同的
		int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
	访问二维数组的元素,跟访问一维数组类似,也是通过索引,即下表。
	二维数组先指定行,再指定列,即先指定元素在哪一行,然后指定元素在哪一列,
	想象成平面直角坐标系的话,未发现类似于坐标系中的(y,x),找到行,相当于确定y的坐标,找到列,相当于确定x的坐标
	比方说我想访问6这个元素,发现第二行第三列
		int six = a[1][2]; 便可访问
*/
int main03()
{
	int a[3][4]={
			{0,1,2,3},  //行号为1
			{4,5,6,7},  //行号为2
			{8,9,10,11} //行号为3
		};
	int i,j;
	for (i=0;i<3;i++)
	{
		for(j=0;j<4;j++)
		{
			printf("a[%d][%d]=%d\n",i,j,a[i][j]);
		}
		/*
			a[0][0]=0
			a[0][1]=1
			a[0][2]=2
			a[0][3]=3
			a[1][0]=4
			a[1][1]=5
			a[1][2]=6
			a[1][3]=7
			a[2][0]=8
			a[2][1]=9
			a[2][2]=10
			a[2][3]=11
		*/
	}
}

//传递数组给函数
/*
	传递数组给函数,可以有三种方式,结果都是一样的,都是告诉编译器要接收一个指针类型
	1.形参是一个指针
		void myFunction(int *param)
	2.形参是一个已定义大小的数组
		void myFunction(int param[10])
	3.形参是一个未定义大小的数组
		void myFunction(int param[])
*/
//举个栗子
float getAvg(int *arr, int size)
{
	//前面也说过,因为数组是一片连续的存储空间
	//如果将整个数组拷贝传递,那么会非常的耗费内存
	//因此只需要传递指针即可,接受到的便是数组的第一个元素的指针,当然也是数组的指针,因为两者是一样的
	//这样的话,如果当做指针来用便是数组的第一个元素的地址,当做数组来用,便是指向整个数组
	//还是那句话,数组是连续的,知道第一个元素的内存地址,便可以知道剩余的元素。
	//这就是为什么数组的地址是数组第一个元素的内存地址
	//int *arr=a(a是一个数组),那么arr可以当做指针来用,指向数组的a的第一个元素的内存地址
	//也可以当做数组来用,指向的便是a这个数组
	//正如数组a本身,即可以当做数组来用,通过a[index]来取值,也可以当做指针来用,通过%p打印首元素的内存地址
	int i;
	float avg;
	float sum=0;
	for (i=0;i<size;i++)
	{
		sum += arr[i];
	}
	//sizeof(arr),获取数组的总大小
	//sizeof(arr[0]),获取数组首元素的大小,获取第几个元素都无所谓,反正大小都是一样的
	//sizeof(arr) / sizeof(arr[0])  变得到了元素总个数
	avg = sum / size;
	return avg;
}
float getAvg(int *,int);
int main04()
{
	int balance[] = {100, 205, 152, 124, 452, 1224};
	float avg = getAvg(balance, sizeof(balance)/sizeof(balance[0]));
	printf("avg=%f\n",avg); //avg=376.166656
	printf("avg=%f\n",(100+205+152+124+452+1224)/6); //avg=376.166504
}

//从函数返回数组
/*
	C语言不允许返回一个完整的数组,但可以返回一个指定数组的指针。
	另外C语言不支持返回局部变量的地址,除非定义局部变量为static变量
*/
//举个栗子
#include <time.h>
int *getRandom()
{
	static int r[10];
	int i;
	//设置种子
	srand((unsigned)time(NULL));
	for (i=0;i<10;i++)
	{
		r[i] = rand();
		printf("r[%d]=%d\n",i,r[i]);
	}
	return r;  
	//这里返回的r不是一个数组吗?为什么是指针呢?
	//之前说过很多遍,返回的是什么取决于要用什么东西来接收
	//因为我们要指定一个指针变量来接收,所以这个r就是首元素的地址,也是该数组的地址
	//因为c语言不支持返回数组,所以我们也只能使用指针来接收。
	//c语言不支持返回局部变量的指针,所以我们要加上一个static
}
int *getRandom();
int main05()
{
	//一个指向整型数组的指针
	int *p;
	int i;
	p = getRandom();
	for (i=0;i<10;i++)
	{
		printf("p[%d]=%d\n",i,p[i]);
		//这里的p就是r的指针,指向r的首元素的地址
		//但是也可以当做数组来用,指向r整个数组
	}
	return 0;
}

/*
r[0]=21006
r[1]=8871
r[2]=8698
r[3]=25946
r[4]=22819
r[5]=32317
r[6]=7933
r[7]=6209
r[8]=14047
r[9]=9467
p[0]=21006
p[1]=8871
p[2]=8698
p[3]=25946
p[4]=22819
p[5]=32317
p[6]=7933
p[7]=6209
p[8]=14047
p[9]=9467
*/
//关于随机函数srand
/*
	srand((unsigned)time(NULL))是初始化随机函数种子
	1.是拿当前系统时间作为种子,由于时间是变化的,种子变化,可以产生不相同的随机数。
	计算机中的随机数实际上都不是真正的随机数,如果两次给的种子一样,是会生成同样的随机序列的。
	所以,一般都会以当前的时间作为种子来生成随机数,这样更加的随机。
	2.使用时,参数可以是unsigned型的任意数据,比如srand(10);
	3.如果不使用srand,用rand()产生的随机数,在多次运行,结果是一样的。
*/

//指向数组的指针
//这个内容其实在前面已经反复出现了,尽管没有真正拿出来单讲,但是已经出现很多次了
/*
	double mmp[50];
	那么mmp便是一个指向&mmp[0]的指针
	因此 int *p=mmp;这句代码把p赋值为mmp的第一个元素的地址
	使用数组名作为常量指针是合法的,反之亦然
	因此,*(mmp+4)是访问mmp[4]的一种合法方式,之前也说过,mmp既可以当数组,也可以当指针。
	如果当指针,那么就是数组首元素的地址。所以*(mmp+4)这里的mmp显然是被当成了指针
	那么mmp被当成指针是首元素的地址,那么mmp+4便是第五个元素(索引为4)的地址。
	所以*(mmp+4)=mmp[4],这便是c中指针的强大之处,指针还可以进行运算,你mmp当成指针,
	不是首元素的地址吗?我给你加上4,那么不就变成了第五个元素的地址吗?所以mmp+4,
	要好好理解,mmp+4指的是从mmp(首元素地址)指的元素再移动4个元素,然后所在的元素的地址。
	所以当我们指针打印mmp+1,mmp+2····mmp+n,会发现它们之间的值相差4,因为整型数组里面每个元素(int)占4个字节,
	所以跨一个元素,等于跨了4个字节。
	mmp[2] = *(mmp+2) = *(&mmp[0]+2) = (&mmp[0])[2]
	mmp可以当指针和数组使用,但是mmp[0]只能当值使用,以指针形式打印要加上&
	(&mmp[0])[2]是什么鬼?因为&mmp[0]在指针上和mmp相同啊,但是要加上括号,不然会引起歧义。
	所以说指针是C语言的灵魂,不是没有道理的,合理的利用的指针,都完成很多意想不到的操作。
	插一句:
		表示搞python的,发现C的指针真的是发现了新大陆,在python中虽然也有指针的概念,但是写代码基本上不会用到,
		指针在python中被封装了起来,程序猿基本上不会用到指针来进行操作。
		之前使用go的时候,也用过指针,但是C中指针强大的是,指针居然特么可以进行运算
*/
//栗子
int main06()
{
	//带有5个整型元素的数组
	int mmp[] = {11,22,33,44,55};
	int *p;
	int i;
	p = mmp;
	printf("使用指针打印值\n");
	for(i=0;i<5;i++)
	{
		//*(p+0) *(p+1) 
		printf("*(p+%d)=%d\n",i,*(p+i));
	}
	
	printf("使用mmp打印\n");
	for (i=0;i<5;i++)
	{
		printf("*(mmp+%d)=%d\n",i,*(mmp+i));
	}
}
/*
	使用指针打印值
	*(p+0)=11
	*(p+1)=22
	*(p+2)=33
	*(p+3)=44
	*(p+4)=55
	使用mmp打印
	*(mmp+0)=11
	*(mmp+1)=22
	*(mmp+2)=33
	*(mmp+3)=44
	*(mmp+4)=55
*/


//笔记
/*
	指针和数组的区别
	char *str="matsuri";
	char str[]="matsuri";
	字符数组提前说一下
	char *str,显然是一个指针,让他等于一个字符数组,那么str便指向了字符首元素的地址
	但是依然可以当成数组来使用,str[0]获取的便是m
	char str[],显然是一个数组,通过str[0]获取m是没问题的,但是也可以当做指针来用,
	printf("%p-%p-%p",str,&str,&str[0]);打印的都是首元素m的地址
	但是对于char *str是不一样的,因为str本身就是指针,所以str和&str不一样
	
	得出结论:
		字符数组的首地址,str和&str是一样的
		但对于指针来说,str和&str不一样,因此处理时要注意方式
		是使用char str[]还是char *str
*/

/*
	int a[] = {11,22,33,44······,nn};
	a[0] 、a[1]...a[i] 代表的都是值,a、(a+0)、(a+1)、(a+i) 代表的是地址
	另外这里的 a 代表整个数组的首地址,相当于 a[0] 的地址,而这里 (a+1) 就代表的是 a[0+1] 的地址。
	正如文章中提到的:所有的数组都是由连续的内存位置组成。
	最低的地址对应第一个元素,最高的地址对应最后一个元素,即是说 (a+i) 就代表的是 a[0+i] 的地址。
*/

/*
	关于数组的初始化
	1.可以只给部分元素赋值,当 { } 中值的个数少于元素个数时,只给前面部分元素赋值。例如:
	int a[10]={12, 19, 22 , 993, 344};
	表示只给 a[0]~a[4] 5 个元素赋值,而后面 5 个元素自动初始化为 0。
	当赋值的元素少于数组总体元素的时候,不同类型剩余的元素自动初始化值。说明如下:
	对于 short、int、long,就是整数 0;
	对于 char,就是字符 '\0';
	对于 float、double,就是小数 0.0。
	由于剩余的元素会自动初始化为0,我们可以通过下面的形式将数组的所有元素初始化为 0:
	int nums[10] = {0};
	char str[10] = {0};
	float scores[10] = {0.0};
	2.只能给元素逐个赋值,不能给数组整体赋值。例如给 10 个元素全部赋值为 1,只能写作:
	int a[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
	而不能写作:
	int a[10] = 1;
*/

  

猜你喜欢

转载自www.cnblogs.com/traditional/p/9471719.html