数组是一组相同类型元素的集合。
数组的创建方式:
type_t arr_name [const_n];
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
首先我们就来看看数组如何创建~~
int a1[5];
char a2[6];
float a3[7];
double a4[4 + 4]; //也可以是一个表达式
对于整型、字符型、浮点型的数据可以创建
[]
内的数字便是这个数组的大小,表示这个数组中可以存放多少元素。
除了数字也可以是一个表达式放里面
虽然指定数组大小可以是一个常量,但VS不支持是一个变量
int n = 0;
scanf("%d", &n);
int arr[n];
变长数组
的概念int n = 0; //初始化
int m;
m = 0; //赋值
接下去就来看看数组的初始化
首先是整型数组
//1.不完全初始化,数组个数10个。第一个元素为1,其余9个位0
int arr1[10] = { 1 };
//2.完全初始化,数组个数10个
int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
//3.若没有指定数组的个数,则初始化了几个这个数组的大小就为几
int arr3[] = { 1,2,3,4,5 };
//1.数组大小为4,初始化四位,abc + '\0'
char ch1[] = "abc";
//2.数组大小为3,初始化前三位,abc
char ch2[] = { 'a', 'b', 'c' };
\0
;0
,在ASCLL码中0就是\0
全局范围
默认初始化为0,当变量在局部范围
内默认没有初始化,放的是随机值对于数组的使用,我们有一个操作符 []
,下标引用操作符,它其实就数组访问的操作符
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
sizeof()
去首先计算出数组的大小~~int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
注意: 在数组创建的时候不能使用变量,而在使用的时候就可以~~
小结:
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("&arr[%d] = %p \n", i, &arr[i]);
}
return 0;
}
小结:
int main()
{
int arr1[3][4]; //整型二维数组
double arr2[3][5]; //字符型二维数组
float arr3[4][5]; //浮点型二维数组
return 0;
}
int main()
{
int arr1[3][4] = { 1,2,3,4 };
int arr2[3][4] = { {1,2},{4,5} };
int arr3[][4] = { {2,3},{4,5} };
return 0;
}
对于二维数组在初始化的时候可以省略行,但是不可以省略列
在C语言中,数组的大小在声明时需要指定,包括行和列。这是因为C语言中的数组是一块连续的内存空间,编译器在编译时需要知道数组的大小以便正确分配内存。
如果省略列,编译器将无法确定每个元素在内存中的偏移量,因此无法正确计算数组的地址。指定列数有助于编译器进行正确的地址计算和内存分配
初始化好了,我们可以将一个二维数组打印在屏幕上
int arr[][4] = { {1,2},{3,4},{5} };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
那么数组再内存中是怎么存储的呢?
int arr[][4] = { {1,2},{3,4},{5} };
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 4; ++j)
{
printf("&arr[%d][%d] = %p\n", i, j, &arr1[i][j]);
}
printf("\n");
}
数组首元素地址:
对于一个数组的数组名来说就是这个数组的首元素地址,我们可以根据一个数组的首元素地址来访问到这个数组,然后就可以访问到这个数组中的所有内容
对于这个二维数组来说,因为它一行就是一个一维数组,因此我们就可以说
arr[1]
是第一行的首元素地址arr[2]
是第二行的首元素地址arr[3]
是第三行的首元素地址然后便可以根据每一行的首元素地址的偏移量访问到这行的所有内容
int main()
{
int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 5; ++j)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
0~4
会依次访问五个元素,但是每一行只有四个元素char arr[] = "";
scanf("%s", arr);
printf("%s\n", arr);
arr[]
并没有指定数组的大小,因此数组大小由初始化的字符个数决定。\0
,那么这个数组的大小即为1。所以当我scanf
输入一个长度大于1的字符串时,其实就会造成数组越界的问题【arr数组周围的堆栈被破坏即为数组越界】int arr1[] = { 0 };
for (int i = 0; i < 10; ++i)
{
arr1[i] = i;
}
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
}
void BubbleSort(int a[10])
{
int n = sizeof(a) / sizeof(a[0]);
for (int i = 0; i < n - 1; ++i)
{
for (int j = 0; j < n - 1 - i; ++j)
{
if (a[j] > a[j + 1])
{
int t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
PrintArray(arr, sz);
BubbleSort(arr);
PrintArray(arr, sz);
return 0;
}
接下来就来介绍一下为什么是1而不是10
首元素地址
,而并不是把整个数组作为参数传递过去sizeof(数组名)求解的是整个数组的字节大小
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sizeof(arr));
sizeof(a)
得出的结果为4,但是这个为什么是40呢sizeof(数组名)
计算的就是整个数组的大小,因为arr数组中有十个元素,一个整型元素占4个字节,所以整个数组的大小即为40&数组名为整个数组的地址
printf("%p\n", &arr[0]);
printf("%p\n", arr);
printf("%p\n", &arr);
arr[0]
指的是首元素,&arr[0]
指的便是首元素的地址;对于arr
来说也是一样为首元素地址
void bubble_sort(int arr[], int sz)
{
int i = 0;
//确定冒泡排序的趟数
for (i = 0; i < sz - 1; i++)
{
//假设数组是有序的
int flag = 1;
//一趟冒泡进行多少对比较
int j = 0;
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;
}
}
}
void print_arr(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
print_arr(arr, sz);
printf("\n");
bubble_sort(arr, sz);
print_arr(arr, sz);
}
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
p
中存放着数组 arr
的首元素地址。要通过这个指针变量访问后面的所有元素,我们可以使用循环,通过 p + i
的方式来获取元素的地址,然后通过解引用操作 *(p + i)
来访问元素的值。for (int i = 0; i < 10; ++i)
{
printf("%d ", *(p + i));
}
printf("\n");
*(p + i)
表示访问数组中第 i
个元素的值。这种方式可以适用于任何一维数组,因为一维数组在内存中是一块连续的存储空间,通过指针的偏移可以依次访问数组的所有元素。p
,然后逐个递增指针,每次递增一个元素的大小(在这里是4个字节,假设是int类型数组),第 i
个元素的地址即为 p + i
。当我们需要访问这个地址的内容时,通过对指针进行解引用 *(p + i)
,就能够获取数组中第 i
个元素的值。这种方式可以灵活地遍历数组中的所有元素,而不需要直接使用数组下标。在循环中,这个过程被用来打印数组中的十个元素。int* p = &arr[0]
可以写成int* p = arr
arr
)表示该数组的首元素地址。i
个位置时,就到达了下标为 i
的元素所在的位置。通过对其进行解引用,就可以获取下标为 i
的元素。这可以表示为 *(arr + i)
。[]
,它有交换律。将 arr[i]
转换为 *(arr + i)
时,括号中的操作数可以进行交换,变成 *(i + arr)
。*(i + arr)
也可以写成 i[arr]
。*(arr + i)
可以等价于 arr[i]
,同时也可以写成 i[arr]
。*(i + arr)
是否可以写成i[arr]
呢此刻我们再进行代码演示一下~~
p[i]
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
for (int i = 0; i < 10; ++i)
{
printf("%d ", arr[i]);
}
printf("\n\n\n");
for (int i = 0; i < 10; ++i)
{
printf("%d ", *(arr + i));
}
printf("\n\n\n");
for (int i = 0; i < 10; ++i)
{
printf("%d ", *(p + i));
}
printf("\n\n\n");
for (int i = 0; i < 10; ++i)
{
printf("%d ", p[i]);
}
printf("\n\n\n");
return 0;
}
由于篇幅较长,这里我另外写一篇文章来详解三子棋小游戏【制作中】
由于篇幅较长,这里我另外写一篇文章来详解扫雷小游戏【制作中】
好了,本文到这里就结束了,感谢大家的收看,希望大家学有所获🌹🌹🌹