C语言之详解数组【附三子棋和扫雷游戏实战】(二)

简介: C语言之详解数组【附三子棋和扫雷游戏实战】(二)

C语言之详解数组【附三子棋和扫雷游戏实战】(一):https://developer.aliyun.com/article/1426998

四、数组作为函数参数

1、冒泡排序函数的错误设计

  • 然后让我们来看看错误的冒泡排序
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;
}

  • 为什么会发生这样的情况呢?我们通过DeBug来调试看看
  • 这里n应该为10而不是1

接下来就来介绍一下为什么是1而不是10

2、数组名意味着什么?

  • 对于数组名而言,当我们将一个数组作为函数的参数进行传递的时候,传入的仅仅这个数组的首元素地址,而并不是把整个数组作为参数传递过去
情况1:sizeof(数组名)

sizeof(数组名)求解的是整个数组的字节大小

int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sizeof(arr));

  • 可以看到输出的结果为【40】,我们刚才说到数组名指的是首元素地址,刚才在【冒泡排序】中计算sizeof(a)得出的结果为4,但是这个为什么是40呢
  • sizeof(数组名)计算的就是整个数组的大小,因为arr数组中有十个元素,一个整型元素占4个字节,所以整个数组的大小即为40
情况2:&数组名

&数组名为整个数组的地址

printf("%p\n", &arr[0]);
printf("%p\n", arr);
printf("%p\n", &arr);

  • 三个打印出来的结果都是一样的,对于第一个arr[0]指的是首元素,&arr[0]指的便是首元素的地址;对于arr来说也是一样为首元素地址
  • 也是一样为首元素地址
  • 而对于&arr来说,指的则是整个数组的地址,它和数组首元素地址是一样的,所以三者地址相同

小结一下

  • &数组名:数组名表示整个数组。取出的是整个数组的地址
  • sizeof(数组名):数组名表示整个数组。求解的是整个数组的大小,单位是字节
  • 除此之外见到数组名全部都为该数组的首元素地址

3、冒泡排序函数的改进

  • 通过上面的分析可以知晓出错的地方是在数组的个数,所以我们在排序外面计算完再把这个数组的大小传进去就行
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);

4、数组地址与指针

数组地址偏移量与指针偏移量
  • 在C语言中,我们可以通过指针来访问数组的元素,利用指针进行数组元素的遍历和访问。首先,我们将数组的首元素地址赋给一个指针变量,然后通过逐步向后移动指针来访问数组的各个元素。
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赋值给了p,所以我们在使用arr的时候可以换成p,使用p的时候可以换成arr

  • 在C语言中,数组名(如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;
}
  • arr[i] == *(arr + i) == *(p + i) == p[i]

五、数组的应用实例1:三子棋

由于篇幅较长,这里我另外写一篇文章来详解三子棋小游戏

六、数组的应用实例2:扫雷游戏

由于篇幅较长,这里我另外写一篇文章来详解扫雷小游戏

相关文章
|
4天前
|
存储 编译器 C语言
C语言之数组
C语言之数组
14 0
|
5天前
|
C语言
C语言:数组和指针笔试题解析(包括一些容易混淆的指针题目)
C语言:数组和指针笔试题解析(包括一些容易混淆的指针题目)
|
5天前
|
存储 算法 程序员
C语言:深入探索与实战应用
C语言:深入探索与实战应用
11 0
|
5天前
|
C语言
数组深入剖析(C语言基础入门)
数组深入剖析(C语言基础入门)
|
7天前
|
存储 C语言
C语言中字符串的引用与数组元素操作
C语言中字符串的引用与数组元素操作
12 0
【C语言】函数递归实现——扫雷小游戏(详细步骤)
【C语言】函数递归实现——扫雷小游戏(详细步骤)
【C语言】函数递归实现——扫雷小游戏(详细步骤)
|
5天前
|
C语言
C语言:内存函数(memcpy memmove memset memcmp使用)
C语言:内存函数(memcpy memmove memset memcmp使用)
|
2天前
|
C语言
pta浙大版《C语言程序设计(第3版)》 习题6-4 使用函数输出指定范围内的Fibonacci数 (20分)
pta浙大版《C语言程序设计(第3版)》 习题6-4 使用函数输出指定范围内的Fibonacci数 (20分)
|
2天前
|
C语言
pta 浙大版《C语言程序设计(第3版)》题目集 习题6-6 使用函数输出一个整数的逆序数 (20分)
pta 浙大版《C语言程序设计(第3版)》题目集 习题6-6 使用函数输出一个整数的逆序数 (20分)
|
2天前
|
C语言
(浙大版《C语言程序设计(第3版)》 习题6-5 使用函数验证哥德巴赫猜想 (20分)
(浙大版《C语言程序设计(第3版)》 习题6-5 使用函数验证哥德巴赫猜想 (20分)