前言
C 语言支持数组数据结构,数组是用来存储一系列数据,而且是一系列相同类型的变量。
数组
- 1. 一维数组
- 1.1 数组的创建
- 1.2 数组的初始化
- 1.3 数组的使用
- 1.4 数组在内存中的存储
- 2. 二维数组
- 1.1 数组的创建
- 1.2 数组的初始化
- 1.3 数组的使用
- 1.4 数组在内存中的存储
- 3. 数组越界
- 4. 数组作为函数参数
- 4.1 数组名你用对了吗?
- 结束语
1. 一维数组
数组是一组相同类型元素的集合。(存放一组数)
1.1 数组的创建
数组创建方式:
type_t arr_name [const_n];
//type_t 指数组的元素类型
//const_n 常量表达式,指定数组的大小
🍤数组创建实例:
int arr[9];
//不同数据类型的数组
char arr2[10];
float arr3[9];
double arr4[5];
//下面是一个错误例子:
int n=9;
int arr1[n];//[]中必须是常量表达式
🍩注:
数组创建,在C99标准之前,[ ]中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念,数组的大小可以使用变量指定,但是数组不能初始化。
1.2 数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值。
比如:
int arr1[10] = {1,2,3,4,5,6,7,8,9,10};//完全初始化
int arr2[10] = { 1,2,3 };//不完全初始化,剩余的元素默认都是0
int arr3[10] = { 0 };//不完全初始化,剩余的元素默认都是0
int arr4[] = { 0 };//省略数组的大小,数组必须初始化,数组的大小是根据初始化的内容来确定
char arr5[]={'a','c',9};
char arr6[]="abc";
char arr7[3]={'a','b','c'};
int arr8[];//错误
🍩数组在创建的时候如果想不指定数组的确定的大小就得初始化。
🍩数组的元素个数根据初始化的内容来确定。
1.3 数组的使用
对于数组的使用我们之前介绍了一个操作符:[]
,下标引用操作符。它就是数组访问的操作符。
下图是一个长度为 10 的数组,第一个元素的索引值为 0,第九个元素的索引值为 8:
对数组元素进行索取:
🍤实例1:计算数组元素个数
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};//数组的不完全初始化
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组的元素个数
printf("%d\n",sz);
return 0;
}
🍩sizeof:返回一个对象或者类型所占的内存字节数
🍤实例2:输出数组元素
#include <stdio.h>
int main()
{
int arr[10] = { 0 };//数组的不完全初始化
//对数组内容赋值,数组是使用下标来访问的,下标从0开始。
int i;//做下标
for (i = 0; i < 10; i++)//定义的数组有10个元素,所以下标是从0-9
{
arr[i] = i;
}
//输出数组的内容
for (i = 0; i < 10; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
在不知道数组长度的情况下,该如何控制for语句的循环条件?
🍤实例3:
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//将i<10改为i<sz
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
🍩数组是使用下标来访问的,下标是从0开始。
🍩数组的大小可以通过计算得到。
//计算数组大小
int arr[10];
int sz = sizeof (arr)/sizeof(arr [0]);
1.4 数组在内存中的存储
我们先看一段代码:
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5 };
int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小
int i = 0;
for (i = 0; i < sz; i++)
{
printf("&arr[%d] = %p\n", i, &arr[i]);//%p -- 用来打印地址
}
return 0;
}
运行结果:
在内存中的存储地址:
仔细观察,可以发现后一个元素的地址比前一个元素的地址增4
说明了:数组在内存中是连续存放的,而且由高到低。
2. 二维数组
1.1 数组的创建
int arr[3][4];//3行4列
char arr[3][5];
float arr[1][3];
1.2 数组的初始化
//数组初始化
int arr[3][4] = {1,2,3,4};
int arr[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
int arr[][4] = {{2,3},{4,5}};//二维数组如果有初始化,行可以省略,列不能省略
🍩二维数组的行可以省略,但列不能省
比如:有二维数组
int arr[3][3]={1,2,3,4,5,6,7,8,9};
如果只知道行,无法确定(推出)数组的列数
如果知道了列,就知道了下一行从哪里开始
1.3 数组的使用
二维数组的使用也是通过下标的方式:
#include<stdio.h>
int main()
{
int arr[4][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7},{5,6,7,8,9} };
printf("%d\n", arr[2][3]);//6
return 0;
}
🍤实例:
#include<stdio.h>
int main()
{
int arr[4][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7},{5,6,7,8,9} };
int i ;//用i控制行,下标是0-3
for (i = 0; i < 4; i++)//外循环控制行
{
int j ;//j控制列,下标是0-4
for (j = 0; j < 5; j++)//内循环控制列
{
printf("%d ", arr[i][j]);//输出整个数组
}
printf("\n");//每行打印完后,换行
}
return 0;
}
1.4 数组在内存中的存储
像一维数组一样,这里我们尝试打印二维数组的每个元素:
#include<stdio.h>
int main()
{
int arr[3][4] = { 0 };
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;
}
运行结果:
可以发现:二维数组在内存中也是连续存储的
3. 数组越界
数组的下标是有范围限制的。
数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
数组的下标如果小于0,或者大于n-1,就是数组越界访问了。
比如这段代码:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
for(i=0; i<=10; i++)
{
printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
}
return 0;
}
🍩二维数组的行和列也可能存在越界。
4. 数组作为函数参数
在写代码的时候,需要将数组作为参数传给函数。
比如:实现一个冒泡排序函数将一个整形数组排序。
冒泡排序:依次比较两个相邻的元素的大小,按照升序或降序排列,重复的比较,直到所有元素排列完成。
代码实现:
//升序排列
void bubble_sort(int* arr, int sz)//这里的arr的本质是指针
{
int i = 0;
for (i = 0; i < sz - 1; i++)//数组下标范围:0-sz-1,
{
int j = 0;
int flag = 1;//假设已经是有序数组
//进行比较
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
//交换
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = 0;
}
}
if (flag == 1)
break;
}
}
int main()
{
int arr[10] = { 0 };
int i ;
int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小
for (i = 0; i < sz; i++)
{
scanf("%d", &arr[i]);//输入
}
//arr作为数组进行了传参
bubble_sort(arr, sz);//arr 是数组首元素的地址,传递的是首元素的地址
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);//输出排好序的数组元素
}
return 0;
}
当数组传参的时候,实际上只是把数组的首元素的地址传递过去了。
所以即使在函数参数部分写成数组的形式: int arr[]
,其表示的依然是一个指针: int *arr
。
4.1 数组名你用对了吗?
先看一段代码:
//%p--打印地址
int main()
{
int arr[10] = { 1,2,3};
printf("%p\n", arr);//首元素地址--arr[0]
printf("%p\n", arr+1);//arr[1]
printf("%p\n", &arr[0]);//首元素取地址
printf("%p\n", &arr[0]+1);//arr[1]
printf("%p\n", &arr);//数组的地址,是从首元素地址开始的
printf("%p\n", &arr+1);//数组+1,跳过整个数组
printf("%d\n", sizeof(arr));//整个数组所占内存空间大小
return 0;
}
运行结果:
🍩数组名通常情况下就是数组首元素的地址。
但是有2个例外:
🍥 sizeof(数组名),数组名单独放在sizeof()内部,这里的数组名表示整个数组
,计算的是整个数组的大小
🍥 &数组名,这里的数组名也表示整个数组
,这里取出的是整个数组的地址
除此之外所有遇到的数组名都表示数组首元素的地址
结束语
合理安排时间,就等于节约时间。
我们下一篇文章再见。