做者 谢恩铭,公众号「程序员联盟」(微信号:coderhub)。
转载请注明出处。
原文: https://www.jianshu.com/p/7fe...《C语言探索之旅》全系列程序员
结束了上一课“指针”的有点艰难的旅程(其实上一课没有讲很深),C语言探索之旅 | 第二部分第二课:进击的指针,C语言的王牌! ,这一课咱们来学习数组这个 C语言的重点。编程
咱们将继续“一路向北”,“指”哪打哪。小程序
为何这么说呢?由于这一课咱们还要涉及指针的知识。就如上一课说的,指针的使用几乎是贯穿 C语言的,并且咱们也会步步深刻指针的学习。数组
否则指针怎么能被称为 C语言的精华呢?因此“指针啊,每天见”,你觉得指针会这么“放过”你么?Too young, too naive... :P微信
想要如今逃避吗?那可不是成功者的表现哦。数据结构
不少学 C语言的朋友,都以为指针和数组貌似有点相似,又好像不一样。有点扑朔迷离的感受,“情深深,雨濛濛”,纠葛不清,难分难舍。函数
因此这一课咱们就来解惑:到底指针和数组有什么联系和区别呢?学习
学完这一课相信会有些许拨云见雾的感受。spa
在这一课中,咱们一块儿学习如何建立数组这种数据类型(或者说是数据结构)。数组在 C语言中也是极为重要的内容,因此你们不能由于过了指针那一坎,就不“正襟危坐”了。操作系统
咱们会首先解释一下数组在内存中的机制(配图),对内存的解释始终是很重要的,由于理解好了内存的机制,C语言才能学得扎实。
因此推荐你们花些时间去学习王爽老师编写的《汇编语言》第三版,对于理解 C语言和计算机原理是颇有帮助的。汇编语言可能没必要学得很深,入门就好。
能够看我之前写的文章 学习汇编对编程有什么帮助?如何学习 。
一个程序员若是能很好地知道本身的程序背后的机理,方能写出稳定、健壮的程序。
数组是在内存中具备连续地址的一系列相同类型的变量的集合。
好吧,我知道这个定义“学究气过重,腐儒味更甚”。
简单地说,数组就是“巨大的变量”(怎么听起来那么变扭,幸好我加了一个“的”字...),其中能够存储一系列相同类型的变量(long,double,int,char,等)。
数组在英语中是 array。array 这个词有这些含义:“数组,阵列;排列,列阵;大批,一系列”。
数组中变量(能够称为数组元素或成员)的数目是固定的(固然也能够构造动态数组,之后再说),它能够包含 2 个,3 个,10 个,25 个,2500 个,甚至更多变量,由你决定存放数目。
下图展现了一个由四个元素组成的数组,首元素的地址是 1700。
当你要建立包含 4 个元素的数组时,实际上是首先向操做系统这个“大管家”发出请求:“可否给我在内存中分配一块地址,以存放这四个元素”。
操做系统通常都会应声而起,随传随到,乖乖分配你要的地址。
可是对于数组来讲,这四个元素的存放地址是连续的,中间没有间隔,这也是数组的一个特色。
各个元素之间“亲密无间”。如上图所示,四个元素的地址分别是:1700,1701,1702,1703。
每个地址的区块上存放相同类型的一个数字(说到底全部数据对于计算机来讲都是数字么)。
若是数组是 int 类型的,那么每个数组元素的地址块上就存放了一个 int 类型的数。咱们不能在一个数组里既存放 int 型又存放 double 型,鱼与熊掌不可兼得也~
小结一下,对于数组:
咱们来学习如何定义一个数组。首先定义一个包含 4 个 int 类型数据的数组:
int array[4];
你会说:“原来这么简单…”
是啊,就是这么简单,只须要在中括号里写上你须要的元素个数,一个数组就建立好了。
数组成员的个数通常来讲没有限制,固然这取决于你的内存大小。
接下来,咱们如何访问每个数组成员呢?
也很简单,
array[成员编号]
注意:成员编号是从 0,1,2,这样一直到数组元素个数减一。还记得之前说过电脑数数是从 0 开始的吗?由于电脑是用二进制的。
因此数组的第一个元素就是 array[0],依此类推。因此上面的包含 4 个成员的数组,它的成员编号是没有 4 的,而是 0,1,2,3。
若是我要将数组中的成员的值赋为像上图中同样,我能够这么作:
int array[4]; array[0] = 10; array[1] = 12; array[2] = 3; array[3] = 7;
你会说:“我可没看到数组和指针有什么联系啊。”
事实上,若是你只写 array,那就是一个指针,是指向数组首元素的首地址的一个指针。
例如:
int array[4]; printf("%d", array);
结果输出:
1700
固然,这里的 1700 是照应上面图示中假设的,实际上你会获得其余的地址值。
若是你带着下标来访问数组,那会获得数组的对应那个下标的成员:
printf("%d", array[0]);
结果输出
10
对其余的下标也是相似。由于咱们知道了单独用数组名,是表示一个指针,因此咱们也能够这样来得到数组的首元素的值:
printf("%d", *array);
结果输出
10
相似地,咱们也能够获得数组的第二个元素的值,经过这样:
*(array + 1)
因此下面这两个表达式的结果是同样的,都是 12 :
array[1] *(array + 1)
C语言有好多个版本,或者称之为标准,有 C89(1989 年制定),C99(1999 年制定),C11(2011 年制定),等等。
从版本 C99 开始,容许建立大小可变的数组,也就是元素的个数是一个变量:
int variable = 5; int array[variable];
可是这个新特性可不是全部的 C 编译器都认识,因此有些版本的编译器就会在第二行出错。
咱们的课程参考和基于的 C 语言标准是 C89,因此咱们的课程里就不容许有大小可变的数组了。
咱们须要达成协议:
数组的元素个数(中括号里的数)必须是一个常量,不能是变量,连 const 变量也不行。
数组须要有一个固定的大小。
你会问:难道就真的不能建立元素个数可变的数组了吗?
答案是:能够建立元素数目一开始不肯定的数组,即便在 C89 里。
可是要达到这样的目的,咱们要使用另外一种技术:动态分配。以后的课程会讲到。
假如咱们如今要显示数组中每个成员的值。
我固然能够一个一个用 printf 输出,可是这样的话可能代码就太多了。最好仍是用一个循环来显示,好比经常使用的 for 循环:
int main(int argc, char *argv[]) { int array[4], i = 0; array[0] = 10; array[1] = 12; array[2] = 3; array[3] = 7; for (i = 0 ; i < 4 ; i++) { printf("%d\n", array[i]); } return 0; }
程序输出:
10 12 3 7
咱们的 for 循环借着一个称为 i 的变量来遍历咱们的数组,其实 i 是很经常使用的变量名,大部分程序员都喜欢将其用于遍历数组,由于 i 是 index(表示“下标”)的首字母。
你们应该发现了:咱们在定义一个数组时,在中括号 [] 里不能放一个变量(数组的成员个数须要肯定),可是在遍历数组时却能够在中括号里放置变量。
注意:不要尝试访问 array[4],由于你会获得任意数据,或者获得一个错误,由于这个地址已经产生了“数组越界”,操做系统就会停止你的程序,由于你的程序尝试访问一个没有权限访问的地址。
如今既然咱们已经知道如何遍历一个数组了,那么咱们应该也能很轻松地初始化一个数组了:咱们能够用 for 循环来将数组的各个成员都初始化为 0。
int main(int argc, char *argv[]) { int array[4], i = 0; // 数组的初始化 for (i = 0 ; i < 4 ; i++) { array[i] = 0; } // 打印数组各个成员来肯定数值 for (i = 0 ; i < 4 ; i++) { printf("%d\n", array[i]); } return 0; }
输出:
0 0 0 0
看了上面的初始化方式,以为仍是不过瘾,咱们需要知道还有另外一种初始化的方式,就是这样写:
数组名[4] = {数值1, 数值2, 数值3,数值4};
简单说来,就是把各个成员的数值写在大括号里,用逗号隔开,以下:
int main(int argc, char *argv[]) { int array[4] = {0, 0, 0, 0}, i = 0; for (i = 0 ; i < 4 ; i++) { printf("%d\n", array[i]); } return 0; }
输出也是:
0 0 0 0
实际上,也能够更简便。就是写上前几个成员的初始值。后面的成员的值,假如你没给出初值,是会自动初始化为 0 的:
int array[4] = {10, 23}; // 初始化的值 : 10, 23, 0, 0
第一个成员取到的值是 10,第二个是 23,第三和第四都初始化为 0 了。
那么如何简便地把数组的全部成员都初始化为 0 呢,只须要这样写:
int array[4] = {0};
这样,全部的成员都初始化为 0 了。
在咱们写程序的时候可能会须要把一个数组的全部成员的值显示出来,那为何不把这个功能写成一个函数呢?
借着这个小程序,咱们也能够学习如何将一个数组做为参数传递给函数。
咱们须要传递两个参数给函数:数组(实际是数组的地址)和数组的大小。
咱们以前说过,数组名直接用的话实际上是一个指针,指向数组的首元素的首地址!
因此咱们能够这样来写咱们的程序:
void display(int *array, int arraySize); int main(int argc, char *argv[]) { int array[4] = {10, 15, 3}; // 显示数组内容 display(array, 4); return 0; } void display(int *array, int arraySize) { int i; for (i = 0 ; i < arraySize ; i++) { printf("%d\n", array[i]); } }
程序输出:
10 15 3 0
上面的函数 display 看上去好像和咱们以前在指针那一课的函数没什么区别,这个函数的第一个参数是一个指向 int 型的指针(咱们的数组名 array)。
第二个参数是数组的大小(成员个数),为了知道咱们的 for 循环何时停止。
也有另外一种方式来代表一个函数接受一个数组做为参数,这样写:
void display(int array[], int arraySize);
此次我用了中括号,来代表参数接受一个数组,但其实传递给函数的仍是数组的首元素,没有传递整个数组过去(由于拷贝整个数组是很大的开销)。
这样写的好处是不会让读者误觉得接受的参数是一个普通的指针,固然这一次就不用在中括号里面写数组的大小了。
我写程序时两种方式都用,但通常为了避免混淆,仍是用中括号的方式多一些。
学了今天的课,想让你们本身实现一些和数组有关的函数。
这里只给出练习题的描述,你们须要本身思考如何实现这些函数。以后能够在程序员联盟的 QQ 群和微信群里讨论。
求数组的平均值。函数模板:
double arrayAverage(int array[], int arraySize);
写一个拷贝数组的函数,这个函数有三个参数,第一和第二个参数是数组名,第三个参数是数组大小。将第一个参数(数组)的内容拷贝到第二个参数(数组)里。函数模板:
void copyArray(int originalArray[], int copyArray[], int arraySize);
写一个函数,有三个参数,第一个是一个数组,第二个是数组大小,第三个是最大值。若是这个数组里有成员的值大于最大值,则将此成员的值变为 0。函数模板:
void arrayMax(int array[], int arraySize, int valueMax);
这道练习题是最难的。写一个函数,来给数组按成员数值从小到大排序,好比,数组原先是 [15, 81, 22, 13],排序后变为 [13, 15, 22, 81] 。函数模板:
void orderArray(int array[], int arraySize);
加油吧,欢迎交流讨论。
今天的课就到这里,一块儿加油吧!
我是 谢恩铭,公众号「程序员联盟」(微信号:coderhub)运营者,慕课网精英讲师 Oscar 老师,终生学习者。 热爱生活,喜欢游泳,略懂烹饪。 人生格言:「向着标杆直跑」