系列文章目录
第一章 如何学习好C语言
第二章 C语言的编译工具的相关问题
第三章 对C语言的初步认识
第四章 C语言的初阶学习
第五章 C语言的高阶学习
第六章 C语言有关的小项目
第七章 C语言的思维导图及重要知识思维导图
第四章 C语言的初阶学习–数组
一、一维数组的创建,初始化,使用
1.1一维数组的创建形式
数组:一组相同类型的元素的集合
格式:type_t arr_name [const_n];
type_t 指数组的元素类型,const_n是一个常量表达式,用来指定数组的大小,arr_name 数组名
在C99的标准之前,数组的大小必须是常量或者常量表达式
在C99之后,数组的大小可以是变量,为了支持变长数组(但对编译器有所要求)
1.2一维数组的初始化
int a[10]={1,2,3}; //不完全初始化。
int a[]={1,2,3,4,5}; //数组的大小未说明时,由元素的个数决定。
注意区别:
char ch1[10]={‘a’,’b’,’c’};初始化放了三个元素,其余七个补0
char ch2[10]=”abc”; 初始化放了a,b,c,\0.补了6个0,字符串里面是含’\0’的
1.3一维数组的使用
操作符:[],下标引用操作符,它是用于数组访问的操作符。
例1:如何将数组的所有元素进行打印
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
//一维数组的使用,[]下标引用符
int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //未指定数组的大小
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组的长度
//打印数组的每个元素,正序
/*for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}*/
//倒序,关键就在于使用下标
for (i = sz-1; i >=0; i--)
{
printf("%d ", arr[i]);
}
return 0;
}
小结:
1.数组是使用下标来访问的,下标是从0开始的。
2.没有给定数组的长度时候:借助int sz=sizeof(arr)/sizeof(arr[0]); 进行求解
这个求解方法也可以用于尽可能防止数组越界。
1.4一维数组在内存中的布局情况
#include <stdio.h>
int main()
{
//一维数组的使用,[]下标引用符
int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //未指定数组的大小
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组的长度
for (i = 0; i < sz; i++)
{
printf("&arr[%d]= %p\n",i,&arr[i]);
}
return 0;
}
不难发现,一维数组在内存中存储是连续的。
二、二维数组的创建,初始化,使用
2.1二维数组的创建
int arr[3][4]
2.2二维数组的初始化
int arr[3][4]={1,2,3,4};依次往进放入,不够的时候补0,不完全初始化
Int arr[][4]={{2,3},{4,5}}; 二维数组可以省略行的大小,但列不可以,会报错。
为什么不可以省略列呢?
比如:int arr[][4]={{1,2},{3,4}} 调试监视可以看到第一行1 2 0 0,第二行是3 4 0 0
如果是int arr1[][]={{1,2},{4,5}} 都不清楚一行有几个元素,第一行 1 2之后究竟有没有别的元素不清楚。
依次类推,n维数组的初始化,第n个[]不可以省略。
2.3二维数组的使用
同样利用下标进行使用
把二维数组理解成一维数组的数组(进一步加深对二维数组的理解)
比如访问第一行的元素arr[0][j],arr[0]当作一维数组的数组名
2.4二维数组在内存中的存储(也是连续存储的)
int main()
{
int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };
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;
}
不难发现,二维数组的元素在内存中也是连续进行存储的。
三、数组越界
数组的下标是有范围限制的,数组的下标是从0开始,最后一个元素的下标是n-1;如果数组的下标小于0或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。C语言本身是不做数组下标的越界检查,编译器不报错,不代表程序是正确的。因此,自己最好做一下越界的检查。
比如:
a数组只有5个元素,却进行打印第10个元素,显然超出了数组的范围,便出现了一些奇怪的数值。
那么如何进行预防数组越界的情况呢?
借用sizeof函数来计算数组的长度,便不会出现索引数组长度之外的元素。
四、数组作为函数参数(以冒泡排序为例)
冒泡排序的核心思想
两个相邻的元素进行比较,一趟冒泡排序结束之后,一个数据会来到它最终应该出现的位置上!
对于冒泡排序,每一趟只能将一个数据排在它该排的位置,因此,对于n个元素的冒泡排序,便需要n-1趟来完成排序,并且在每一趟结束之后,需要两两相邻比较的对数也在减少。
经典错误!!!
void bubble_sort(int arr[])//要有[]
{
//趟数
int sz = sizeof(arr) / sizeof(arr[0]); //容易犯错的地方
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序
int j = 0;
for (j = 0; j < sz - 1 - i; j++) //每结束一趟的比较,所需要的比较的相邻对数减1
{
if (arr[j] > arr[j + 1])
{
//利用中间变量进行交换
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
bubble_sort(arr); //数组传参,给数组名称即可
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
为什么结果会是这样呢?
通过调试,不难发现,sz的值为1,那么在子函数便没有进行循环的做法。
原因在于数组名本质上(除了两种特殊的情况)是数组首元素的地址。
因此sz的计算就会出现问题,为了解决这一问题,将sz的计算放在主函数中,并将sz作为参数传入到冒泡排序的函数中。
修改后的代码`
对于数组传参的时候,形参有两种形式:数组和指针的形式
//冒泡排序的函数,并且形参是指针的形式
void bubble_sort(int* arr, int sz)//趟数
//形参是数组的形式
//void bubble_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++) //每结束一趟的比较,所需要的比较的相邻对数减1
{
if (arr[j] > arr[j + 1])
{
//利用中间变量进行交换
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr,sz); //数组传参,给数组名称即可
for (i = 0; i < sz; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
但在下面的两种情况下,数组名不表示数组的首元素的地址
1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2.&数组名,这里的数组名表示整个数组,取的是整个数组的地址
这个说明数组名是作为数组的首元素的地址的
&arr是作为数组的地址,而不是首元素的地址的。数组+1地址,表示跳过整个数组
第5个和第6个差值为十六进制的0x28(换算为10进制就是2*16+8=40),即就是整个数组。
arr表示的是二维数组的第一行的地址(和第一行第一列的地址虽然一样)但是arr+1的地址,就是二维数组的第二行的地址,而不是第一行第二列的元素的地址。
计算二维数组的行数和列数
int main()
{
int arr[3][4] = { 0 };
//sizeof(arr)计算整个二维数组的大小,sizeof(arr[0])计算二维数组第一行的大小
printf("%d\n", sizeof(arr) / sizeof(arr[0]));
//sizeof(arr[0])计算二维数组第一行的大小,sizeof(arr[0][0])计算二维数组第一行第一列元素的大小
printf("%d\n", sizeof(arr[0]) / sizeof(arr[0][0]));
return 0;
}
总结
以上就是关于一维,二维数组的创建,初始化,使用,以及简单的冒泡排序的介绍。