系列文章目录
第一章“C“浒传——初识C语言(更适合初学者体质哦!)
第二章详细认识分支语句和循环语句以及他们的易错点
第三章初阶C语言——特别详细地介绍函数
第四章初始C语言——详细地讲解数组的内容以及易错点
目录
系列文章目录
前言
一、一维数组的创建与初始化
1.1 数组的创建
1.2 数组的初始化
1.3 一维数组的使用
1.4 一维数组在内存中的存储
二、二维数组的创建与初始化
2.1 二维数组的创建
2.2 二维数组的初始化
2.3 二维数组的使用
2.4 二维数组在内存中的存储
三、数组越界
四、数组作为函数参数
4.1 冒泡排序函数的错误设计
4.2 数组名是什么?
4.3 冒泡排序函数的正确设计
总结
前言
在上一章中,我们已经详细地介绍了有关函数的相关内容,学习了函数是什么、C语言中函数的分类、函数的参数、调用、函数的嵌套调用和链式访问、函数的声明和定义、函数递归。
而在这一章,小编将带领大家进行数组的学习,虽然数组的知识点比较小,但是我们还是要进行好好学习,从标题中,我们能看出要详细地学习数组的知识,希望大家看的开心!
一、一维数组的创建与初始化
小编在之前就比较迷数组,第一就是数组的下标是从0开始的,但是不要害怕,紧跟小编的步伐,来进行学习吧!
1.1 数组的创建
数组是一组相同类型元素的集合,一定要记住,因为后面我们要学习结构体,结构体可以存放不同类型元素的集合。
数组的创建方式:
type_t arr_name [const_n]
type_t是指数组的元素类型
const_n 是一个常量表达式,用来指定数组的大小
在了解完数组的创建方式后,下面我们来进行数组创建的实例:
//代码1int arrq[10];//代码2int count = 10;int arr2[count];//数组这样可以正常创建吗?//代码3char arr3[10];float arr4[10];double arr5[10];
代码2这种情况在C语言(C99之前)中是不能使用的,因为数组创建在C99标准之前,[ ]中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念,数组的大小是可以使用变量指定的,但一定要切记变长数组是不能初始化的。
1.2 数组的初始化
为啥要讲数组的初始化呢?
因为局部变量不初始化的话,局部变量里放的是随机值;全局变量不初始化的话,全局变量里放的是0。所以说初始化很重要。
数组初始化是指:在创建数组的同时给数组的内容一些合理初始值(初始化)。
数组的初始化分为:完全初始化和非完全初始化。
看代码:
int arr1[10] = { 1,2,3 };//不完全初始化int arr2[] = { 1,2,3,4 };//不完全初始化int arr3[5] = { 1,2,3,4,5 }; //完全初始化char arr4[3] = { 'a', 98, 'c' }; //完全初始化char arr5[] = { 'a', 'b', 'c' }; //不完全初始化char arr6[] = "abcdef";//不完全初始化
数组的完全初始化是指:数组创建时在给定数字时将数组填满;而数组的非完全初始化是指:数组创建时在给定数字时填不满数组,则剩下的元素默认初始化为0;下面,我们来进行调试验证。
数组在创建的时候如果想不指定数组的确定大小就得初始化。数组的元素个数根据初始化的内容来确定。
1.3 一维数组的使用
对于数组的使用,我们之前介绍了一个操作符:[ ],下标引用操作符。他其实就是数组访问的操作符。下面,我们来看一下代码吧!
#include int main(){int arr[10] = { 0 };//数组的不完全初始化//计算数组的元素个数int sz = sizeof(arr) / sizeof(arr[0]);//对数组内容赋值,数组是使用下标来访问的,下标从0开始,所以:int i = 0; //做下标for (i = 0; i < sz; i++){arr[i] = i;}//输出内容数据for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");return 0;}
在这里,小编我要提醒大家:要记住数组的长度怎样计算?但有一种情况大家不要使用这种方法进行计算(代码在下面):这种方式会在sizeof(arr)算的是整个数组的长度,而不是光输入的数组长度,其余情况倒是可以随便用。
const int N = 10;int arr[100];for(int i = 0; i < N; i++){...}int sz = sizeof(arr)/sizeof(arr[0]);
总结:
1)数组是使用下标来访问的,下标是从0开始的,依次增加1;
2)数组的大小可以通过计算得到的。
1.4 一维数组在内存中的存储
当讨论完一维数组的使用后,接下来我们来讨论数组在内存中的存储。下面,我们来看一下代码吧!(在这里提一嘴,打印地址的占位符是:%p;为了便于观察,我们将环境改为X86。)
#include int main(){int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;for (i = 0; i < sz; i++){arr[i] = i;}for (i = 0; i < sz; i++){printf("&arr[%d] = %p\n", i, &arr[i]);}printf("\n");return 0;}
输出的结果如下:
仔细观察输出的结果,我们知道,随着数组下标的增长,元素的地址也是有规律的递增。由此可以得出结论:数组在内存中是连续存放的。(方便内存区使用数组)
二、二维数组的创建与初始化
2.1 二维数组的创建
在小编看来,二维数组与一维数组的不同在于:二维数组增加了行和列的关系。接下来,看二维数组的创建。
//数组创建int arr[3][4];char arr[3][5];double arr[2][4];
2.2 二维数组的初始化
小编记得二维数组的初始化要比一维数组的初始化要难,下面也有好几种情况:
int arr1[3][4] = { 1,2,3,4 };int arr2[3][4] = { {1,2},{3,4} };//如果二维数组没有放满,则补0int arr3[][4] = { {1,2},{5,4} };//二维数组如果有初始化,行可以省略,列不能省略
下面在监视窗口中可以验证上述所说:
为什么在二维数组初始化中可以有大括号呢?
我们可以将二维数组的每一行想像为一个一维数组。
2.3 二维数组的使用
二维数组的使用也是通过下标的方式,在假想中,二维数组是一个矩阵,所以二维数组有自己的行号和列号,我们可以通过行号和列号进行使用一个元素,就如同坐标系一样去访问。下面举个例子:
int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
下面看代码:
int main(){int arr[3][5] = { 0 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){arr[i][j] = i * 4 + 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;}
2.4 二维数组在内存中的存储
在经历了上面对二维数组的学习后,大家可能会发现其实二维数组与一维数组基本类似。像一维数组一样,这里我们尝试打印二维数组的每一个元素地址。下面,我们来看一下代码吧!(在这里再提一嘴,打印地址的占位符是:%p;为了便于观察,我们将环境改为X86。)
int main(){int arr[3][5] = { 0 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){arr[i][j] = i * 4 + j;}}for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);}printf("\n");}return 0;}
输出的结果如下:
通过结果我们可以分析得到,其实二维数组在内存中也是连续存储的,这里能看出二维数组是一维数组的数组,证实了这一点。
三、数组越界
数组的下标是有范围限制的;数组的下标规定是从0开始的,如果数组有N个元素,最后一个元素的下标就是N-1。所以数组的下标如果小于0,或者大于N-1,就是数组越界访问了,超出了数组合法空间的访问。二维数组的行和列也可能存在越界。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。
但如果有赋值操作的话,程序就会崩溃。
所以程序员写代码时,最好自己做好越界的检查。
四、数组作为函数参数
往往我们写代码的时候,会将数组作为参数传入函数,接下来我们借着举例子的时候,为大家介绍一下排序算法中最简单的排序——冒泡排序。
冒泡排序的算法思想:两两相邻的元素进行比较,将较大(较小)的沉入最下面。
4.1 冒泡排序函数的错误设计
我们接下来看代码:
void bubble_sort(int arr[]){int sz = sizeof(arr) / sizeof(arr[0]);for (int i = sz - 1; i >= 0; i++){for (int j = 0; j arr[j + 1]){int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}}int main(){int arr[] = { 3,5,7,8,0,2,1,4,6,9 };bubble_sort(arr);for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}return 0;}
这个代码出现问题,那我们来找一下问题:经过调试之后,可以看到bubble_sort函数内部的sz为2,而不是我们想要的值。难道数组作为函数参数的时候,不是把整个数组传递过去吗?
4.2 数组名是什么?
为了解决上述问题,我们进行探究数组名的内容:看下面的代码:
结论:数组名就是地址。通常来说,数组名是数组首元素的地址。
如果数组名是首元素地址的话,那么我们之前学过计算数组的长度和这个情况不同。看代码:
所以数组名表示的并不一定都是首元素地址,有两个例外:
- sizeof(数组名),计算整个数组的大小,sizeof内部单独放入一个数组名,数组名表示整个数组,单位是字节。
- &数组名,取出的是数组的地址。&数组名。数组名表示整个数组。
4.3 冒泡排序函数的正确设计
当数组传参的时候,实际上只是把数组的首元素的地址传递过去了。所以即使在函数参数部分写成数组的形式:int arr[ ] 表示的依然是一个指针: int* arr。那么这也回答了4.1中的问题:sizeof(arr)的结果是8。
那我们该怎么进行更改函数设计呢?下面看代码:
void bubble_sort(int arr[], int sz)//参数接受元素个数{//代码同上面函数}int main(){int arr[] = { 3,5,7,8,0,2,1,4,6,9 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;}
总结
在这一部分,小编详细地编写了有关数组的一篇博客。希望大家看完以后,进行点评,谢谢大家!