2.3:二维数组的使用
二维数组中的每一个元素都有其对应的行号和列号,行号和列号都是从0开始的,故可以通过下标来使用二维数组
//通过下标访问数组的每一个元素并将其打印出来 int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
2.4:二维数组在内存中的存储
//打印二维数组中每一个元素的地址 int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 4; j++) { printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]); } } return 0; } //打印结果如下: &arr[0][0]=00AFF860 &arr[0][1]=00AFF864 &arr[0][2]=00AFF868 &arr[0][3]=00AFF86C &arr[1][0]=00AFF870 &arr[1][1]=00AFF874 &arr[1][2]=00AFF878 &arr[1][3]=00AFF87C &arr[2][0]=00AFF880 &arr[2][1]=00AFF884 &arr[2][2]=00AFF888 &arr[2][3]=00AFF88C
可见二维数组中每相邻两个元素的地址是连续的
//因为二维数组中元素的存储是连续的,因此我们也可通过下面这种方式来打印数组中的每一位元素 int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int* p = &arr[0][0]; int i = 0; for (i = 0; i < 12; i++) { printf("%d ", *(p + i)); } printf("\n"); return 0; }
如果把二维数组的每一行都看成是一个以为数组,那么:
arr[0]可以看作是第一行元素的数组名
arr[1]可以看作是第二行元素的数组名
arr[2]可以看作是第三行元素的数组名
2.4.1:二维数组行数和列数的计算
//计算二维数组由多少行 sizeof(arr)/sizeof(arr[0])//二维数组总的字节数除以每一行的字节数就是行数 //计算二维数组的列数 sizeof(arr[0])/sizeof(arr[0][0])//二维数组一行的字节数除以每一个元素的字节数就是列数
三:数组越界
int arr[10];//下标的取值范围是0~9,超出这个范围就是越界
数组的下标是有范围限制的。
数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。
int main() { int arr[10] = { 0 }; //下标的取值范围是0~9,超出这个范围就是越界 int i = 0; for (i = 0; i <= 10; i++) { printf("%d ", arr[i]); } } //结果: 0 0 0 0 0 0 0 0 0 0 -858993460 //通过最后一个打印结果(随机值)可以看出,发生了数组越界
int main() { int arr[10] = { 0 }; //下标的取值范围是0~9,超出这个范围就是越界 arr[10] = 11;//数组已越界 }
执行上面这段代码,会报出如下的数组越界错误
/
四:数组作为函数的参数
4.1:冒泡排序函数
冒泡排序的思想是:从左到右,相邻的两个元素进行比较,并且进行适当的交换。每次比较一轮,就会找到序列中最大的或者最小的数,这个数就会来到序列的最右边。
以从小到大排序为例:从左到右,让相邻的两个元素进行比较,如果左边的元素大于右边的元素就将这两个元素进行交换,从左到右,两两相邻的元素比较结束,最大的那个数就会来到序列的最右边,第一轮比较就结束了。接着进行第二轮,从左边第一个数一直比较到倒数第二个数(最后一个数就是序列里面最大的,无需再进行比较),如下图的序列:9 8 7 6 5 4 3 2 1.第一轮冒泡共进行了9次两两相互比较,第一轮冒泡排序结束后序列里面最大的数字9的位置就确定了下来,即在序列的最后面(因为是从小到大排序)。接着进行第二轮冒泡排序,找出9前面的序列:8 7 6 5 4 3 2 1 0里面的最大值让他来到9的前面,第二轮冒泡排序共进行了8次两两比较,最终序列:8 7 6 5 4 3 2 1 0中的最大值8来到了他应该出现的位置,即在9的前面。第三轮冒泡会让7去到他应该去到的位置,第四轮冒泡会让6去到他应该去到的位置。大家猜猜这十个数字要进行几轮冒泡呢?既然一轮搞定一个数字,那十个数字不就需要十轮嘛?答案是需要九轮,因为十个数经过九轮冒泡,那一定有九个数都去到了他们应该去的位置,既然九个数的位置都确定下来了,那最后剩的那个数肯定就在他应该出现的位置上,就不用再进行第十轮冒泡了。因此,如果有n个数就要进行n-1轮冒泡才能得到一个有顺序的序列。
void Sort(int arr[], int sz) { int i = 0; for (i = 0; i < sz - 1; i++) { //冒泡轮数 int j = 0; for (j = 0; j < sz - 1 - i; j++)//第一轮要进行九次两两比较,第二轮要进行八次两两比较,所以这里的判断条件是sz-1-i { //每一轮冒泡要进行多少次两两比较 if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } int main() { int arr[] = { 3,1,5,4,7,6,9,8,0,2 };//一共十个数 //写一个函数对数组排序 int sz = sizeof(arr) / sizeof(arr[0]);//计算数组中元素的个数,数组总的字节数除以每一个元素的字节数就是数组中元素的个数 Sort(arr, sz); int i = 0; //将排完序的数组打印出来 for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
注意:计算数组元素个数的这段代码“int sz = sizeof(arr) / sizeof(arr[0]);”一定得放在主函数里面,将计算出的个数sz传到Sort函数里面,切不可在Sort函数里面进行计算。为什么只能这样呢?这就是我们接下来要讨论的问题-数组名是什么了?
4.2:数组名是什么?
数组名是首元素的地址(有两个例外)
例外1:sizeof(数组名),这里的数组名是表示整个数组,计算的是整个数组的大小,单位是字节
例外2:&数组名,这里的数组名也表示整个数组,&数组名取出的是数组的地址
//通过这段代码可证明:数组名就是首元素的地址 int main() { int arr[] = { 1,2,3,4,5,6,7,8,9 }; printf("%p\n", arr);//打印数组名 printf("%p\n", &arr[0]);//打印首元素地址 return 0; } //执行结果: 004FFEAC 004FFEAC
int main() { int arr[] = { 1,2,3,4,5,6,7,8,9 }; printf("%p\n", arr);//打印数组名 printf("%p\n", &arr[0]);//打印首元素地址 printf("%p\n", &arr);//这里&arr取的是数组的地址 return 0; } //执行结果: 00EFFBF8 00EFFBF8 00EFFBF8
虽然上面的这段代码打印出来的结果相同,但是意义却不相同,前两个仅仅代表首元素的地址,而第三个代表的是数组的地址
通过下面的代码便可得以验证
int main() { int arr[] = { 1,2,3,4,5,6,7,8,9 }; printf("%p\n", arr); printf("%p\n", arr+1); printf("%p\n", &arr[0]); printf("%p\n", &arr[0]+1); printf("%p\n", &arr); printf("%p\n", &arr+1); return 0; } //执行结果: 006FFB5C 006FFB60 006FFB5C 006FFB60 006FFB5C 006FFB80
可见arr+1和&arr[0]+1的结果一样,都是跳过了四个字节,而&arr+1则是跳过了四十个字节
这下我们就知道了:数组作为函数的参数,传递的是首元素的地址,既然是地址那形参就应该用一个指针来接收。此时我们再看这段代码“int sz = sizeof(arr) / sizeof(arr[0]);”如果放在函数内部,arr此时仅仅是一个整型指针,而一个指针的字节数在×86的环境下就是四个字节,所以sizeof(arr)的结果就是4而不是整个数组所占的字节数,而sizeof(arr[0])也是4,最终相除的结果就是1了,并不是我们想得到的数组元素个数,因此求数组的元素个数应该在主函数中求
//数组作为函数的参数 void Sort(int* arr)//形参是对应的指针类型 { } int main() { int arr[] = { 1,2,3 }; Sort(arr);//传的是首元素的地址 return 0; }
形参得到了数组的首元素地址,接着顺藤摸瓜就能找到数组中的其他元素
到这里,数组的有关分享就结束啦,喜欢的话可以点赞、评论和收藏哟!