数组
一维数组的创建和初始化
数组是一组相同类型元素的集合
数组的创建
数组的创建方式:
type_t arr_name [const_n];
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
注:
- C99语法支持变长数组(数组的大小是变量),VS2019不支持变长数组,GCC编译器对C99支持的比较好
- 数组创建, [] 中要给一个常量才可以,不能使用变量(不考虑C99语法)。
数组的创建:
char arr1[10];
float arr2[1];
double arr3[20];
数组的初始化
数组的初始化是指在创建数组的同时给数组的内容一些合理初始值(初始化)。
数组的初始化:
//整形数组的初始化
int arr1[10] = {1,2,3,4,5,6,7,8,9,10};//完全初始化
int arr2[10] = { 1,2,3,4,5 };//不完全初始化
int arr3[] = { 1,2,3,4,5 }; //相当于int arr3[5] = { 1,2,3,4,5 };
int arr[2]={0}; //不完全初始化 数组中第一个元素放0其余元素默认初始化成0
int arr[2]={0,0} //完全初始化 数组中第一、二个元素都放0
//字符数组的初始化
char ch1[5] = {'b', 'i', 't'};
char ch2[] = { 'b', 'i', 't' };
char ch3[5] = "bit";//b i t \0 0
char ch4[] = "bit";//b i t \0
数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。
注:当数组定义时未明确数组元素个数,它会根据初始化内容从而确定数组元素的个数
字符数组中用字符初始化以及字符串初始化的区别:
注:ch5的字符数组创建,ch5要内存中分配4个字节大小的内存;ch6的字符数组创建,ch6要内存中分配3个字节大小的内存(需要注意的是内存分配给字符数组的空间,数组本身是知道里面放的是什么值的,而其他内存空间中放的是什么值是不知道的)。printf函数打印字符串的内容和strlen函数计算字符串的长度所得到的结果都是不一样的,这是因为这两个函数都是遇到‘\0’结束,而字符数组ch6中没有‘\0’,所以一直找直到找到‘\0’,因此ch6打印的是随机值
一维数组的使用
对于数组的使用需要介绍一个操作符: [] (下标引用操作符)它其实就数组访问的操作符
一维数组的使用:
int main()
{
int arr[10] = { 0 };
arr[4] = 5;//[] - 下标引用操作符
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
总结:
- 数组是使用下标来访问的,下标是从0开始。
- 数组的大小可以通过计算得到。
int arr[10];
int sz = sizeof(arr)/sizeof(arr[0]);
一维数组在内存中的存储
注:
-
%p 是按地址的格式打印(地址是有固定长度的) - 十六进制的打印(不够补0) 。%x是按十六进制的格式打印
-
一维数组在内存中是连续存放的
-
随着数组下标的增长,地址是由低到高变化的
二维数组的创建和初始化
二维数组本质上是以数组作为数组元素的数组,即“数组的数组”,类型说明符 数组名[常量表达式][常量表达式]。
二维数组的创建
//数组创建
int arr[3][4];
char arr[3][5];
double arr[2][4];
二维数组的初始化
//初始化 - 创建的同时给赋值
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int arr[3][4] = { 1,2,3,4,5,6,7};//不完全初始化 - 后面补0
int arr[3][4] = { {1,2}, {3,4} ,{4,5} };
int arr[][4] = { {1,2}, {3,4} ,{4,5} };
注:二维数组初始化时的行数是可以省略的,它会根据初始化情况来确定行数的
二维数组的使用
int main()
{
int arr[][4] = { {1,2}, {3,4} ,{4,5} };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
注:二维数组的行和列下标都是从0开始的
二维数组在内存中的存储
总结:
- 二维数组在内存中是连续存放的
- 二维数组一行内部连续,跨行也是连续的
二维数组int arr[3][4]的数组名是arr,arr二维数组的第一行数组名是arr[0],第二行是arr[1],第三行是arr[2]
用途:
- 二维数组在内存中的存储决定了二维数组初始化时行是可以省略的列不行(因为二维数组是连续的只有确定一行有多少元素才能决定下一行,否则就不知道下一行从哪开始)
- 当拿到二维数组的第一个元素的地址时就可以遍历出所有的元素(二维数组在内存中的存储是连续的)
数组作为函数参数
数组名是什么
数组名就是首元素的地址
总结:数组名是数组首元素的地址。但是有2个例外:sizeof(数组名) - 数组名表示整个数组 - 计算的是整个数组的大小,单位是字节。 &数组名 - 数组名表示整个数组 - 取出的是整个数组的地址。除此之外所有的数组名都表示数组首元素的地址。
补充:
- 数组作为函数传参的时候,形参可以写成两种形式:数组形式、指针形式。
- 二维数组作为实参传参时,函数中的形参是二维数组时数组大小必须设置成与实参大小一致
数组传参(不论是一维数组还是二维数组)在参数部分、调用部分传参的时候,实参传的时候一定是数组名(数组传参),而传过去形参部分展示的时候可以写数组也可以写成指针,但本质上都是指针(因为数组传参的时候传过去的其实是数组首元素的地址)。
冒泡排序
冒泡排序的思想:两两相邻的元素进行比较,并且可能的话需要交换
void bubble_sort(int arr[], int sz)//形参arr本质是指针 参数接收数组元素个数
{
//确定趟数
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序的过程
int j = 0;
int flag = 1;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
//交换
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
int main()
{
//int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
//排序为升序 - 冒泡排序
//计算数组元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);//数组传参的时候,传递的其实是数组首元素的地址
return 0;
}
注:
- 一趟冒泡排序解决一个数字(有一个数字一定来到最终出现的位置)
- 当数组传参的时候,实际上只是把数组的首元素的地址传递过去了。所以即使在函数参数部分写成数组的形式: int arr[] 表示的依然是一个指针: int *arr 。