尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

第5讲:数组

第5讲:数组 目录1.数组的概念2.一维数组的创建和初始化2.1 数组创建2.2 数组的初始化2.3 数组的类型3.一维数组的使用3.1 数组下标3.2 数组元素的打印3.3 数组的输入4.一维数组在内存中的存储5.sizeof计算数组元素个数6.二维数组的创建6.1 二维数组的概念6.2 二维数组的创建7.二维数组的初始化7.1 不完全初始化7.2 完全初始化7.3 按照行初始化7.4 初始化时可以省略行但是不能省略列8.二维数组的使用8.1 二维数组的下标8.2 二维数组的输入和输出9.二维数组在内存中的存储10.C99中的变长数组11.数组练习1.数组的概念数组是一组相同类型元素的集合从这个概念中我们可以发现2个有价值的信息数组中存放的是1个或者多个数据但是数组元素个数不能为0数组中存放的多个数据其类型是相同的。数组分为一维数组和多维数组多维数组一般比较多见的是二维数组。2.一维数组的创建和初始化2.1 数组创建一维数组创建的基本语法如下type arr_name[常量值];存放在数组的值被称为数组的元素数组在创建的时候可以指定数组的大小和数组的元素类型。type指定的是数组中存放数据的类型可以是char、short、int、float等也可以是自定义的类型。arr_name指的是数组的名字这个名字根据实际情况见名知义即可。[ ]中的常数值是用来指定数组的大小的这个数组的大小根据实际的需求指定就行。比如我们现在想存储某个班级的20人的数学成绩那我们就可以创建一个数组如下int math[20];当然我们也可以根据需要创建其他类型和大小的数组char ch[8]; double score[10];2.2 数组的初始化有时候数组在创建的时候我们需要给定一些初始值这种就称为初始化。那数组如何初始化呢数组的初始化一般使用大括号将数据放在大括号中。数组如果进行了初始化数组的大小是可以省略掉的。// 完全初始化 int arr[5] {1,2,3,4,5}; // 不完全初始化 int arr[6] {1}; // 第一个元素初始化为1剩余的元素默认初始化为0 // 错误的初始化 - 初始化太多 int arr[3] {1,2,3,4};2.3 数组的类型数组也是有类型的数组算是一种自定义类型去掉数组名留下的就是数组的类型。如下int arr1[10]; int arr2[15]; char ch[6]arr1数组的类型是int[10]arr2数组的类型是int[15]ch数组的类型是char[6]这里注意和数组元素的类型做区分别混淆了两个概念。3.一维数组的使用学习了一维数组的基本语法一维数组可以存放数据存放数据的目的是对数据的操作那我们如何使用一维数组呢3.1 数组下标C语言规定数组是有下标的下标是从0开始的假设数组有n个元素最后一个元素的下标是n-1下标就相当于数组元素的编号如下int arr[10] {1,2,3,4,5,6,7,8,9,10};数组元素和下标在C语言中数组的访问提供了一个操作符[ ]这个操作符叫下标引用操作符。有了下标访问操作符我们就可以轻松地访问到数组的元素了。比如我们访问下标为7的元素我们就可以使用arr[7]想要访问下标是3的元素就可以使用arr[3]如下代码#include stdio.h int main() { int arr[0] {1,2,3,4,5,6,7,8,9,10}; printf(%d\n, arr[7]);//8 printf(%d\n, arr[3]);//4 return 0; }输出结果3.2 数组元素的打印接下来如果想要访问整个数组的内容那怎么办呢只要我们产生数组所有元素的下标就可以了那我们使用for循环产生0~9的下标接下来使用下标访问就行了。如下代码#include stdio.h int main() { int arr[10] {1,2,3,4,5,6,7,8,9,10}; int i 0; for(i 0; i 10; i) { printf(%d , arr[i]); } printf(\n); return 0; }输出结果3.3 数组的输入明白了数组的访问当然我们也可以根据需求自己给数组输入想要的数据如下#include stdio.h int main() { int arr[10] {1,2,3,4,5,6,7,8,9,10}; int i 0; // 输入 for(i 0; i 10; i) { scanf(%d, arr[i]); } //输出 for(i 0; i 10; i) { printf(%d , arr[i]); } printf(\n); return 0; }输入并输出结果4.一维数组在内存中的存储有了前面的知识我们其实使用数组基本没什么障碍了如果我们要深入了解数组我们最好能了解一下数组在内存中的存储。依次打印数组元素的地址#include stdio.h int main() { int arr[10] {1,2,3,4,5,6,7,8,9,10}; int i 0; for(i 0; i 10; i) { printf(arr[%d] %p\n, i, arr[i]); } return 0; }输出结果并分析如下从输出的结果我们分析数组随着下标的增长地址是由小到大变化的并且我们发现每两个相邻的元素之间相差4因为一个整型是4个字节。所以我们得出结论数组在内存中是连续存放的。这就为后期我们使用指针访问数组奠定了基础在讲指针的时候我们再讲这里暂且记住就行。数组元素在内存中是连续存放的5.sizeof计算数组元素个数在遍历数组的时候我们经常想知道数组的元素个数那C语言中有办法使用程序计算数组元素个数吗答案是有的可以使用sizeof。sizeof在C语言中是一个关键字是可以计算类型或者变量大小的其实sizeof也可以计算数组的大小。比如#include stdio.h int main() { int arr[10] {0}; printf(%zu\n, sizeof(arr)); //10 * 4 40 return 0; }这里输出的结果是40计算的是数组所占内存空间的总大小单位是字节。我们又知道数组中所有元素的类型都是相同的那只要计算出一个元素所占字节的个数数组的元素个数就能算出来。这里我们选择第一个元素算大小就可以。#include stdio.h int main() { int arr[10] {0}; printf(%zu\n, sizeof(arr[0]));//计算一个元素的大小单位是字节 return 0; }接下来就能计算出数组的元素个数#include stdio.h int main() { int arr[10] {0}; int sz sizeof(arr) / sizeof(arr[0]); printf(%d\n, sz); return 0; }这里的结果是10表示数组有10个元素。以后再代码中需要数组元素个数的地方就不用固定写死了使用上面的计算不管数组怎么变化计算出的大小也会随之变化了。6.二维数组的创建6.1 二维数组的概念前面学习的数组称为一维数组数组的元素都是内置类型的。如果我们把一维数组作为数组的元素这时候就是二维数组二维数组作为数组元素的数组被称为三维数组二维数组以上的数组统称多维数组。整型、整型一维数组、整型二维数组6.2 二维数组的创建那我们如何定义二维数组呢语法如下type arr_name[常量值1][常量值2]; 例如 int arr[3][5]; double data[2][8];解释上述代码中出现的信息3表示数组有3行5表示每行有5个元素int表示数组的每个元素是整型类型arr是数组名可以根据自己的需要指定名字data数组意思基本一致7.二维数组的初始化在创建变量或者数组的时候给定一些初始值被称为初始化。那二维数组如何初始化呢像一维数组一样也是使用大括号初始化的。7.1 不完全初始化int arr1[3][5] {1,2}; int arr2[3][5] {0};7.2 完全初始化int arr[3][5] {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};7.3 按照行初始化int arr4[3][5] {{1,2}, {3,4}, {5,6}};7.4 初始化时可以省略行但是不能省略列int arr5[][5] {1,2,3}; int arr6[][5] {1,2,3,4,5,6,7}; int arr7[][5] {{1,2}, {3,4}, {5,6}};8.二维数组的使用8.1 二维数组的下标当我们掌握了二维数组的创建和初始化那我们怎么使用二维数组呢其实二维数组访问也是使用下标的形式的二维数组是有行和列的只要锁定了行和列就能唯一锁定数组中的一个元素。C语言规定二维数组的行是从0开始的列也是从0开始的如下所示int arr[3][5] {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};图中最右侧绿色的数字表示行号第一行蓝色的数字表示列号都是从0开始的。比如我们说第2行第4列快速就能定位出7。#include stdio.h int main() { int arr[3][5] {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7}; printf(%d\n, arr[2][4]); return 0; }输出的结果如下8.2 二维数组的输入和输出访问二维数组的单个元素我们知道了那如何访问整个二维数组呢其实我们只要能够按照一定的规律产生所有的行和列的数字就行以上一段代码中的arr数组为例行的选择范围是0~2列的取值范围是0~4所以我们可以借助训话实现生成所有的下标。#include stdio.h int main() { int arr[3][5] {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7}; int i 0;//遍历行 //输入 for(i 0; i 3; i)//产生行号 { int j 0; for(j 0; j 5; j)//产生列号 { scanf(%d, arr[i][j]);//输入数据 } } //输出 for(i 0; i 3; i)//产生行号 { int j 0; for(j 0; j 5; j)//产生列号 { printf(d% , arr[i][j]);//输出数据 } printf(\n); } return 0; }输入和输出的结果9.二维数组在内存中的存储像一维数组一样我们如果想研究二维数组在内存中的存储方式我们也是可以打印出数组所有元素的地址的。代码如下#include stdio.h int main() { int arr[3][5] {0}; int j 0; int i 0; for(i 0; i 3; i) { for(j 0; j 5; j) { printf(arr[%d][%d] %p\n, i, j, arr[i][j]); } } return 0; }输出的结果从输出的结果来看每一行内部的每个元素都是相邻的地址之间相差4个字节跨行位置处的两个元素如arr[0][4]和arr[1][0]之间也是差4个字节所以二维数组中的每个元素都是连续存放的。如下图所示二维数组的每一行在内存中连续存放了解清楚二维数组在内存中的布局有利于我们后期使用指针来访问数组的学习。10.C99中的变长数组在C99标准之前C语言在创建数组的时候数组大小的指定只能使用常量、常量表达式或者如果我们初始化数据的话可以省略数组大小。如int arr1[10]; int arr2[35]; int arr3[] {1,2,3};这样的语法限制让我们创建数组就不够灵活有时候数组大了浪费空间有时候数组小了不够用。C99中给一个变长数组variable-length array简称VLA的新特性允许我们可以使用变量指定数组大小。请看下面的代码int n a b; int arr[n];上面示例中数组arr就是变长数组因为它的长度取决于变量n的值编译器没法事先确定只有运行时才能知道n是多少。变长数组的根本特征就是数组长度只有运行时才能确定所以变长数组不能初始化。它的好处是程序员不必再开发时随意为数组指定一个估计的长度程序可以在运行时为数组分配精确的长度。有一个比较迷惑的点变长数组的意思是数组的大小是可以使用变量来指定的在程序运行的时候根据变量的大小来指定数组的元素个数而不是说数组的大小是可变的。数组的大小一旦确定就不能再变化了。VS默认使用的msvc这个编译器不支持C99的变长数组但可以在VS中下载clang这个编译器组件来测试下载流程如下1.找到Visual Studio Installer点击2.点击修改3.勾选使用C的桌面开发然后勾选适用于Windows的CClang工具并点击修改4.等待下载完成5.选中对应项目右击鼠标并点击属性6.选择平台工具集并选择LLVMclang-cl最后点击确定即可7.完成上述操作此时的VS便支持变长数组的使用了#include stdio.h int main() { int n 0; scanf(%d, n);//根据输入数值确定数组的大小 int arr[n]; int i 0; for(i 0; i n; i) { scanf(%d, arr[i]); } for(i 0; i n; i) { printf(%d , arr[i]); } printf(\n); return 0; }第一次测试我给n中输入5然后输入5个数字在数组中并正常输出第二次测试我给n中输入10然后输入10个数字在数组中并正常输出11.数组练习练习1多个字符从两端移动向中间汇聚编写代码演示多个字符从两端移动向中间汇聚思路需要两个字符数组分别存入对应字符串每次循环分别将两端的字符互换打印在屏幕上一秒之后刷新屏幕并从两端分别往中间移动一步实现上述方法可用双指针法此处可把指针看成数组下标双指针法图解效果如下图所示练习2二分查找题目给定一个升序的整型数组在这个数组中查找到指定的值n找到了就打印n的下标找不到就打印“找不到”。在一个升序的数组中查找指定的数字n很容易想到的方法就是遍历数组但是这种方法效率比较低。例如#include stdio.h #include string.hs int main() { int arr[] { 1,2,3,4,5,6,7,8,9,10 }; int k 17; // 要找的数 int sz sizeof(arr) / sizeof(arr[0]); int i 0; for (; i sz; i) // 省略循环变量的初始化 { if (arr[i] k) { printf(找到了,下标为%d\n, i); break; } } // 1.找到后通过break跳转到这里 // 2.没找到后结束循环来到这里 if (i sz) { printf(找不到了\n); } return 0; }这就好比我买了一双鞋你好奇问我多少钱我说不超过500元。你还是好奇你想知道到底多少我就让你猜你会怎么猜你会1,2,3,4...这样猜吗显然很慢。一般你都会猜中间数字比如150然后看大了还是小了这就是二分查找也叫折半查找。注意二分查找的前提是升序接下来我们就假定有一个整型数组数组内有10个整数从1到10升序排列。现在我们要用二分查找的方式判断要找的某个数字是否在该数组中思路设数组arr两端的下标分别为left和right计算它们的中间值 mid (left right) / 2用mid对应的数组元素与想要 查找的数字key比较如果arr[mid] key说明mid到right之间的数字里没有key也就是mid的右边不用找了此时我们就可以将右端范围缩小即 right mid -1;如果arr[mid] key说明mid到left之间的数字里没有key也就是mid的左边不用找了此时我们就可以将左端范围缩小即 left mid 1;除去上述两种情况外那就是 arr[mid] key此时我们就找到了数字key而它对应的下标就是mid上述2中的过程需要重复因此需要写成一个循环且循环结束后我们需要知道这个过程中是否有找到数字key。因此我们需要一个变量find来记录结果当 find 0时表示没有找到key当 find 1时表示找到key二分查找图解#include stdio.h int main() { int arr[] {1,2,3,4,5,6,7,8,9,10}; int left 0; int right sizeof(arr)/sizeof(arr[0]) - 1; int key 7; // 要找的数字 int mid 0; // 记录中间元素的下标 int find 0; // 假设法假设没找到若找到则find 1 while(left right) { mid (left right) / 2; if(arr[mid] key) { right mid - 1; } else if(arr[mid] key) { left mid 1; } else { find 1; break; } } if(1 find) { printf(找到了下标是%d\n, mid); } else { printf(找不到\n); } return 0; }我们再来讨论其中的一个问题求中间元素的下标使用 mid (left right) / 2如果left和right比较大的时候可能存在问题比如#includestdio.h #includelimits.h int main() { // INT_MAX2147483647 int left 2147483646; int right 2147483646; printf(%d\n, (left right) / 2); return 0; }输出结果在这个情况下left right的值已经超过了int的取值范围所以出现了问题。那可不可以先除以2再相加呢?#includestdio.h #includelimits.h int main() { // INT_MAX2147483647 int left 2147483646; int right 2147483646; printf(%d\n, left / 2 right / 2); return 0; }输出结果看起来好像没什么问题但如果left和right是奇数呢比如int main() { int left 3; int right 5; printf(%d\n, left / 2 right / 2); // 1 2 3 return 0; }输出结果你会发现3和5的平均值算出来居然还是3说明这种求平均值的方法依旧存在问题那我们怎么办呢可以使用下面的方式mid left (right - left) / 2;对应的图解如下事实上无论是left大还是right大 left (right - left) / 2算出来的结果都是对的假设right大即 right - left 0相对于在left上加上一个差值的一半假设left大即 right - left 0相对于在 left 上减去一个差值的一半。
返回列表