织梦php网站模板修改,网站策划建设,新手怎么做电商卖农产品,育贤网站建设数组 1. 前言2. 一维数组的创建和初始化3. 一维数组的使用4. 一维数组在内存中的存储5. 二维数组的创建和初始化6. 二维数组的使用7. 二维数组在内存中的存储8. 数组越界9. 数组作为函数参数10. 综合练习10.1 用函数初始化#xff0c;逆置#xff0c;打印整型数组10.2 交换两… 数组 1. 前言2. 一维数组的创建和初始化3. 一维数组的使用4. 一维数组在内存中的存储5. 二维数组的创建和初始化6. 二维数组的使用7. 二维数组在内存中的存储8. 数组越界9. 数组作为函数参数10. 综合练习10.1 用函数初始化逆置打印整型数组10.2 交换两个整型数组10.3 三子棋10.4 扫雷 1. 前言
大家好我是努力学习游泳的鱼。今天我们来学习数组。 数组分为一维数组和二维数组我们需要学习它们如何创建和初始化如何使用如何在内存中存储。话不多说让我们开始对数组的学习吧。
2. 一维数组的创建和初始化 数组是一组相同类型元素的集合。 一维数组的创建格式如下 数组的元素类型 数组名[常量表达式]; int arr[100]; // 表示数组能存放100个int类型的数据 我们使用大括号来初始化数组。 完全初始化对数组的所有元素都进行初始化。大括号里初始化的元素个数和数组最多存放的元素个数相同。 int arr[10] {1,2,3,4,5,6,7,8,9,10}; // 完全初始化数组最多存放10个元素大括号内初始化了10个元素 不完全初始化只初始化数组的部分元素剩下的被初始化成0。 int arr[100] {0}; // 不完全初始化第一个元素被手动初始化成0剩下的元素默认被初始化为0 如果对数组初始化数组大小可以省略默认为初始化元素的个数。如下面两种写法效果是相同的。
int arr[5] {1,2,3,4,5};
int arr[] {1,2,3,4,5}; // 省略数组大小默认是5如果创建数组时不初始化则数组大小不能省略此时分为两种情况
局部数组里存放的都是随机值。全局数组会被默认初始化为0。
int arr1[10]; // 默认初始化为0int main()
{int arr2[10]; // 存储的是随机值return 0;
}对于一个变量这两种情况也成立。
当一个局部变量不初始化时存储的是随机值。当一个全局变量或静态变量不手动初始化时会被默认初始化成0。
int a; // 默认初始化为0int main()
{static int b; // 默认初始化为0int c; // 存储的是随机值return 0;
}本质上这是存储位置的差异导致的。局部变量是存储在栈区的栈区上的数据如果不初始化存储的是随机值。全局变量和静态变量是存储在静态区的静态区上的数据如果不手动初始化会被默认初始化为0。 C99中引入了变长数组的概念允许数组的大小用变量来指定如果编译器不支持C99中的变长数组那就不能使用。 int n 10;
int arr[n]; // 变长数组变长数组不能初始化。
int n 10;
int arr[n] {0}; // 不能像这样初始化3. 一维数组的使用 数组通过下标来访问数组的下标是从0开始的。 []是下标引用操作符。 比如int arr[] {10,20,30,40,50};,10的下标是020的下标是130的下标是240的下标是350的下标是4。 那么arr[3]对应的就是40如果我们想把40改成400就这么写arr[3] 400; 我们如何计算数组的元素个数呢很简单用数组的总大小除以数组一个元素的大小就行了。比如对于arr数组数组元素个数sz就可以这么算int sz sizeof(arr) / sizeof(arr[0]); 假设我们想存储1~100的整数并打印出来就可以这么写
#include stdio.hint main()
{int arr[100] { 0 };int sz sizeof(arr) / sizeof(arr[0]);int i 0;for (; i sz; i){arr[i] i 1;}for (i 0; i sz; i){printf(%d , arr[i]);}return 0;
}4. 一维数组在内存中的存储 一维数组在内存中是连续存放的。 随着数组下标的增长地址是由低到高变化的。 我们可以写个程序来验证。
#include stdio.hint main()
{int arr[10] { 0 };int sz sizeof(arr) / sizeof(arr[0]);int i 0;for (; i sz; i){printf(arr[%d] %p\n, i, arr[i]);}return 0;
}我们把数组元素的地址按照下标从低到高打印出来。
我们发现相邻两个元素之间的地址都差4这是因为数组在内存中是连续存放的相邻元素的地址就差一个int即4个字节。 并且随着下标的增长地址是由低到高变化的。
5. 二维数组的创建和初始化
如我们想要创建一个三行五列的二维整型数组就可以这么写int arr[3][5];这个数组有三行五列共15个元素每个元素是int类型的。 int int int int int int int int int int int int int int int 对二维数组进行初始化要使用大括号初始化时会一行一行放一行放满后才会放下一行。若是不完全初始化剩余元素会被默认初始化成0。 如int arr[3][5] { 1,2,3,4,5,6 };的效果是 1 2 3 4 5 6 0 0 0 0 0 0 0 0 0 由于二维数组的每一行都可以看作一个一维数组如果要对每一行分别初始化可以在大括号里使用大括号。 这么说有点抽象举个例子int arr[3][5] { {1,2}, {3,4}, {5,6} };的效果是 1 2 0 0 0 3 4 0 0 0 5 6 0 0 0 如果对二维数组初始化二维数组的行可以省略但是列不能省略。如果省略二维数组的行编译器会根据初始化的内容来确定有几行。 如int arr[][5] {1,2,3,4,5,6};的效果是 1 2 3 4 5 6 0 0 0 0 由于两行就够放了编译器会默认行数为2。 再比如int arr[][5] { {1,2}, {3,4}, {5,6} };的效果是 1 2 0 0 0 3 4 0 0 0 5 6 0 0 0 由于3行就够放了编译器会默认行数为3。 如果我们想给数组的所有元素都初始化成0就可以这么写 int arr[3][5] {0};数组的第一个元素被初始化成0剩下的元素会被默认初始化成0效果是数组的全部元素都被初始化成0。 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
6. 二维数组的使用
二维数组也是用下标来访问的。和一维数组的区别是二维数组有两个下标行标和列标。行标和列标都是从0开始的。比如说一个3行5列的数组arr每个位置的元素的访问方式如下 arr[0][0] arr[0][1] arr[0][2] arr[0][3] arr[0][4] arr[1][0] arr[1][1] arr[1][2] arr[1][3] arr[1][4] arr[2][0] arr[2][1] arr[2][2] arr[2][3] arr[2][4] 如果我们想把这些元素打印出来可以用一层循环产生行标另一层循环产生列标。每行打印完后记得换个行。
#include stdio.hint 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 3; i){int j 0;for (; j 5; j){printf(%d , arr[i][j]);}printf(\n);}return 0;
}当然二维数组有几行几列也是可以通过代码计算出来的。 计算行数的方法sizeof(arr) / sizeof(arr[0]);即数组的总大小除以数组第一行元素的大小。 计算列数的方法sizeof(arr[0]) / sizeof(arr[0][0]);即数组第一行元素的大小除以数组第一行第一列的元素的大小。 上面的代码就可这样改进
#include stdio.hint 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 sizeof(arr) / sizeof(arr[0]); i){int j 0;for (; j sizeof(arr[0]) / sizeof(arr[0][0]); j){printf(%d , arr[i][j]);}printf(\n);}return 0;
}7. 二维数组在内存中的存储
我们用下面的程序把二维数组每个元素的地址打印出来。
#include stdio.hint main()
{int arr[3][5] { 0 };int i 0;for (; i sizeof(arr) / sizeof(arr[0]); i){int j 0;for (; j sizeof(arr[0]) / sizeof(arr[0][0]); j){printf(arr[%d][%d] %p\n, i, j, arr[i][j]);}}return 0;
}我们发现每两个元素之间都差4个字节每当发生换行如第一行最后一个元素arr[0][4]到第二行第一个元素arr[1][0]也差4个字节。arr是一个整型数组每两个元素之间差1个int类型的大小。这说明 二维数组在内存中也是连续存放的。 随着下标的增长地址也是由低到高变化的。 除此之外我们还可以把二维数组的每个元素理解成一维数组。如对于三行五列的二维数组arrarr[0]就是这个二维数组的第一个元素同时arr[0]也是一个有五个元素的一维数组这五个元素分别是arr[0][0]arr[0][1]arr[0][2]arr[0][3]arr[0][4]。
8. 数组越界
数组的下标是有范围限制的。 数组的下标规定是从0开始的如果数组有n个元素最后一个元素的下标就是n-1。 所以数组的下标如果小于0或者大于n-1就是数组越界访问了超出了数组合法空间的访问。 C语言本身是不做数组下标的越界检查编译器也不一定报错但是编译器不报错并不意味着程序就是正确的所以程序员写代码时最好自己做越界的检查。 如下面的程序当i为10时就越界了。
int main()
{int arr[10] {0};int i 0;for (; i10; i){arr[i] i;}return 0;
}二维数组的行和列也可能存在越界。
9. 数组作为函数参数
数组名表示什么看看下面的代码
#include stdio.hint main()
{int arr[10] { 0 };printf(arr %p\n, arr);printf(arr 1 %p\n, arr 1);printf(arr[0] %p\n, arr[0]);printf(arr[0] 1 %p\n, arr[0] 1);printf(arr %p\n, arr);printf(arr 1 %p\n, arr 1);printf(sizeof(arr) %d\n, sizeof(arr));return 0;
}运行结果 由运行结果可知数组名(arr)和数组首元素地址(arr[0])是完全相同的且1之后也是完全相同的都跳过了4个字节(1个int)。数组的地址(arr)和前两者值是相同的但1之后跳过了40个字节(即跳过了整个数组)这是因为如果写arr或arr[0]都表示首元素地址即第一个整型元素的地址1会跳过一个整型如果写arr取出的是整个数组的地址1自然也跳过了整个数组。注意以上所有计算都是十六进制的计算如所谓的跳过40个字节其实是0x28再转换成十进制后得到的40。 并且sizeof(arr)计算的是整个数组的大小即40。 数组名一般表示数组首元素的地址。但是有两个例外 sizeof(数组名)数组名不是数组首元素的地址数组名表示整个数组计算的是整个数组的大小。数组名数组名不是数组首元素的地址数组名表示整个数组取出的是整个数组的地址。 题目写一个函数对一个整型数组进行冒泡排序。 冒泡排序对数组中2个相邻的元素进行比较如果不满足就交换。 比如一个数组里存放的是 9 8 7 6 5 4 3 2 1 0 我想把它排成升序也就是左边的数要比右边的数小。 首先比较9和8因为9比8大不满足升序就交换。 8 9 7 6 5 4 3 2 1 0 接着比较9和7因为9比7大不满足升序就交换。 8 7 9 6 5 4 3 2 1 0 接着比较9和6因为9比6大不满足升序就交换。 8 7 6 9 5 4 3 2 1 0 …… 这一趟冒泡排序下来数组就排序成 8 7 6 5 4 3 2 1 0 9 9的位置就正确了。接下来进行下一趟冒泡排序。由于9的位置处在正确的位置上下一趟冒泡排序就不需要考虑9了只需排序除了9之外的数字。 首先比较8和7因为8比7大不满足升序就交换。 7 8 6 5 4 3 2 1 0 9 接着比较8和6因为8比6大不满足升序就交换。 7 6 8 5 4 3 2 1 0 9 …… 这一趟冒泡排序下来数组就排序成 7 6 5 4 3 2 1 0 8 9 8的位置就正确了。 有没有发现一趟冒泡排序可以使一个数字的位置正确。那么假设有10个数字总共需要几趟冒泡排序呢答案10个数字需要9趟冒泡排序。因为一趟冒泡排序搞定一个数字9趟冒泡排序就搞定9个数字最后一个数字的位置自然也是正确的。 那么一趟内部又需要多少次比较呢假设有10个数字第一趟就需要比较9次第一趟结束后就搞定了其中一个数字只需排序剩下9个数字所以第二趟需要比较8次。由于每趟排序都能够搞定一个数字每趟排序都会比上一趟少一次比较这样就知道每一趟需要几次比较了。 有了以上的分析我们需要写两层循环。外层循环负责控制冒泡排序的趟数假设数组有sz个元素就需要排序sz-1趟内层循环控制一趟内部冒泡排序的比较第一趟需要sz-1次比较第二趟需要sz-2次比较后面每趟都比前一趟少一次比较如果外层循环的循环变量假设是i是从0开始的那第i趟就需要sz-1-i次比较。 如果一趟比较完后没有发生交换则数组就已经有序了就不需要继续排序了。我们可以定义一个flag并初始化为1假设数组已经有序。如果发生交换则把flag改成0。如果一趟冒泡排序中没有发生交换则flag仍然是1就已经有序了不需要继续排序了。 数组元素个数怎么知道呢一定要在数组创建的局部范围内计算数组的元素个数int sz sizeof(arr) / sizeof(arr[0]);因为如果传参给别的函数后我们使用数组名传参由前面的知识可知数组名表示数组首元素的地址传递过去的是一个指针所以sizeof(arr)就是4或者8sizeof(arr) / sizeof(arr[0])这个表达式是无法计算出数组元素个数的。
#include stdio.hvoid bubble_sort(int arr[], int sz)
{int i 0;for (; i sz - 1; i){// 一趟冒泡排序int flag 1; // 假设已经有序int j 0;for (; j sz - 1 - i; j){if (arr[j] arr[j 1]){flag 0;// 交换int tmp arr[j];arr[j] arr[j 1];arr[j 1] tmp;}}if (1 flag)return;}
}int main()
{int arr[] { 9,8,7,6,5,4,3,2,1,0 };int sz sizeof(arr) / sizeof(arr[0]);// 冒泡排序bubble_sort(arr, sz);int i 0;for (; i sz; i){printf(%d , arr[i]);}return 0;
}当然就一种情况是无法代表所有场景的。我写了个测试代码在不同场景下测试这个冒泡排序是否正确供大家参考。
#include stdio.h
#include stdlib.h
#include time.hint test_bubble_sort()
{srand((unsigned int)time(NULL));const int sz 100; // 数组元素个数int* arr (int*)malloc(sz * sizeof(int));if (NULL arr){perror(test_bubblesort::malloc);return 0;}int i 0;for (; i 1000; i){// 生成随机数组int j 0;for (; j sz; j){arr[j] rand();}// 冒泡排序bubble_sort(arr, sz);// 检验排序是否成功for (j 0; j sz - 1; j){if (arr[j] arr[j 1]){free(arr);return 0; // 排序失败}}}free(arr);return 1; // 检验成功
}int main()
{int ret test_bubble_sort();if (1 ret)printf(检验成功\n);elseprintf(检验失败\n);return 0;
}10. 综合练习
10.1 用函数初始化逆置打印整型数组
初始化和打印应该相当简单了吧只需要产生所有的下标遍历数组就能够访问整个数组并初始化或打印了。唯一需要注意的是整型数组无法在函数内部使用sizeof(arr) / sizeof(arr[0])计算元素个数因为数组作为函数参数传递时传递的是首元素的地址。 初始化函数假设初始化为全0
void init(int arr[], int sz)
{int i 0;for (; i sz; i){arr[i] 0;}
}打印函数
#include stdio.hvoid print(int arr[], int sz)
{int i 0;for (; i sz; i){printf(%d , arr[i]);}printf(\n);
}接下来讲讲如何逆置。类似字符串的逆置整型数组的逆置只需交换第一个元素和最后一个元素再交换第二个元素和倒数第二个元素接着交换第三个元素和倒数第三个元素…… 如果用下标来访问数组我们需要一个左下标left指向第一个元素一个右下标right指向最后一个元素交换后left向后走right向前走当left还在right左边时说明还有元素可以交换否则就跳出循环。
void reverse(int arr[], int sz)
{int left 0;int right sz - 1;while (left right){int tmp arr[left];arr[left] arr[right];arr[right] tmp;left;--right;}
}10.2 交换两个整型数组
如何交换两个整型数组arr1和arr2假设两个数组一样大呢 很简单只需要遍历两个数组把对应位置的元素交换就行了
#include stdio.hint main()
{int arr1[] { 1,3,5,7,9 };int arr2[] { 2,4,6,8,0 };int i 0;int sz sizeof(arr1) / sizeof(arr1[0]);printf(交换前:\n);printf(arr1: );for (i 0; i sz; i){printf(%d , arr1[i]);}printf(\narr2: );for (i 0; i sz; i){printf(%d , arr2[i]);}printf(\n);// 交换arr1和arr2for (i 0; i sz; i){int tmp arr1[i];arr1[i] arr2[i];arr2[i] tmp;}printf(交换后:\n);printf(arr1: );for (i 0; i sz; i){printf(%d , arr1[i]);}printf(\narr2: );for (i 0; i sz; i){printf(%d , arr2[i]);}printf(\n);return 0;
}10.3 三子棋
我们可以用已有的知识实现一个三子棋小游戏。详细讲解戳这里
10.4 扫雷
再来实现一个扫雷小游戏。详细讲解戳这里 文章转载自: http://www.morning.nkqnn.cn.gov.cn.nkqnn.cn http://www.morning.kyjpg.cn.gov.cn.kyjpg.cn http://www.morning.ndmbd.cn.gov.cn.ndmbd.cn http://www.morning.srtw.cn.gov.cn.srtw.cn http://www.morning.wtsr.cn.gov.cn.wtsr.cn http://www.morning.lkkkf.cn.gov.cn.lkkkf.cn http://www.morning.nrchx.cn.gov.cn.nrchx.cn http://www.morning.tktyh.cn.gov.cn.tktyh.cn http://www.morning.jcffp.cn.gov.cn.jcffp.cn http://www.morning.bnpcq.cn.gov.cn.bnpcq.cn http://www.morning.qdsmile.cn.gov.cn.qdsmile.cn http://www.morning.gpxbc.cn.gov.cn.gpxbc.cn http://www.morning.lkmks.cn.gov.cn.lkmks.cn http://www.morning.yrngx.cn.gov.cn.yrngx.cn http://www.morning.qcdtzk.cn.gov.cn.qcdtzk.cn http://www.morning.tslfz.cn.gov.cn.tslfz.cn http://www.morning.rngyq.cn.gov.cn.rngyq.cn http://www.morning.bpmnl.cn.gov.cn.bpmnl.cn http://www.morning.nlgmr.cn.gov.cn.nlgmr.cn http://www.morning.ymsdr.cn.gov.cn.ymsdr.cn http://www.morning.zgdnd.cn.gov.cn.zgdnd.cn http://www.morning.trtxt.cn.gov.cn.trtxt.cn http://www.morning.xkwyk.cn.gov.cn.xkwyk.cn http://www.morning.fcftj.cn.gov.cn.fcftj.cn http://www.morning.tgydf.cn.gov.cn.tgydf.cn http://www.morning.kbdrq.cn.gov.cn.kbdrq.cn http://www.morning.ldnrf.cn.gov.cn.ldnrf.cn http://www.morning.bxyzr.cn.gov.cn.bxyzr.cn http://www.morning.yghlr.cn.gov.cn.yghlr.cn http://www.morning.zpfqh.cn.gov.cn.zpfqh.cn http://www.morning.zrpbf.cn.gov.cn.zrpbf.cn http://www.morning.zdxinxi.com.gov.cn.zdxinxi.com http://www.morning.pwdmz.cn.gov.cn.pwdmz.cn http://www.morning.zrkws.cn.gov.cn.zrkws.cn http://www.morning.tqpr.cn.gov.cn.tqpr.cn http://www.morning.okiner.com.gov.cn.okiner.com http://www.morning.fylsz.cn.gov.cn.fylsz.cn http://www.morning.cnfjs.cn.gov.cn.cnfjs.cn http://www.morning.zcqgf.cn.gov.cn.zcqgf.cn http://www.morning.coatingonline.com.cn.gov.cn.coatingonline.com.cn http://www.morning.cyhlq.cn.gov.cn.cyhlq.cn http://www.morning.lwsct.cn.gov.cn.lwsct.cn http://www.morning.nyqxy.cn.gov.cn.nyqxy.cn http://www.morning.trnl.cn.gov.cn.trnl.cn http://www.morning.coatingonline.com.cn.gov.cn.coatingonline.com.cn http://www.morning.jlxld.cn.gov.cn.jlxld.cn http://www.morning.jwsrp.cn.gov.cn.jwsrp.cn http://www.morning.krhkn.cn.gov.cn.krhkn.cn http://www.morning.bnkcl.cn.gov.cn.bnkcl.cn http://www.morning.yuminfo.com.gov.cn.yuminfo.com http://www.morning.gwjnm.cn.gov.cn.gwjnm.cn http://www.morning.hxbjt.cn.gov.cn.hxbjt.cn http://www.morning.tfpqd.cn.gov.cn.tfpqd.cn http://www.morning.jwtwf.cn.gov.cn.jwtwf.cn http://www.morning.symgk.cn.gov.cn.symgk.cn http://www.morning.ljjmr.cn.gov.cn.ljjmr.cn http://www.morning.kgxrq.cn.gov.cn.kgxrq.cn http://www.morning.kpzrf.cn.gov.cn.kpzrf.cn http://www.morning.xrhst.cn.gov.cn.xrhst.cn http://www.morning.ftdlg.cn.gov.cn.ftdlg.cn http://www.morning.gwjqq.cn.gov.cn.gwjqq.cn http://www.morning.kdrjd.cn.gov.cn.kdrjd.cn http://www.morning.bnzjx.cn.gov.cn.bnzjx.cn http://www.morning.plhhd.cn.gov.cn.plhhd.cn http://www.morning.pxwzk.cn.gov.cn.pxwzk.cn http://www.morning.dpdns.cn.gov.cn.dpdns.cn http://www.morning.syfty.cn.gov.cn.syfty.cn http://www.morning.xjnjb.cn.gov.cn.xjnjb.cn http://www.morning.xdxpq.cn.gov.cn.xdxpq.cn http://www.morning.spsqr.cn.gov.cn.spsqr.cn http://www.morning.lwbhw.cn.gov.cn.lwbhw.cn http://www.morning.sjzsjsm.com.gov.cn.sjzsjsm.com http://www.morning.twgzq.cn.gov.cn.twgzq.cn http://www.morning.fwkpp.cn.gov.cn.fwkpp.cn http://www.morning.rccbt.cn.gov.cn.rccbt.cn http://www.morning.wtsr.cn.gov.cn.wtsr.cn http://www.morning.gbybx.cn.gov.cn.gbybx.cn http://www.morning.pqwrg.cn.gov.cn.pqwrg.cn http://www.morning.bfhrj.cn.gov.cn.bfhrj.cn http://www.morning.yxlpj.cn.gov.cn.yxlpj.cn