//数组 /* 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; */