学C的第十二天【深入了解数组:一维和二维数组的创建和初始化;一维和二维数组的使用;一维和二维数组在内存中的存储;数组越界;数组作为函数参数;冒泡排序(对数组名的理解)】-2

简介: 5.二维数组的使用 操作符 [ ] :下标引用操作符,它其实就是数组访问的操作符,使用两个[ ],访问行和列 二维数组的行和列都是从0开始的

5.二维数组的使用

   

操作符 [ ]下标引用操作符,它其实就是数组访问的操作符,使用两个[ ],访问行和列

 

二维数组行和列都是从0开始

57766674144f497294d3c07e8bf96096.png

二维数组的使用实例

   

//二维数组的使用
#include <stdio.h>
int main()
{
  int arr[4][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7},{4,5,6,7}, };
  //      行 列
  //循环打印二维数组:
  //printf("%d\n", arr[2][3]);
  int i = 0;
  //行号
  for ( i = 0; i < 4; i++)
  {
    int j = 0;
    //列号
    for ( j = 0; j < 5; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n"); //打印完一行后换行
  }
  return 0;
}

image.png

6.二维数组在内存中的存储(32位操作系统)

 

(演示代码:)

 

//二维数组在内存中的存储(32位操作系统)
#include <stdio.h>
int main()
{
  int arr[4][5] = { 0 };
  //      行 列
  //循环打印二维数组:
  //printf("%d\n", arr[2][3]);
  int i = 0;
  //行号
  for (i = 0; i < 4; i++)
  {
    int j = 0;
    //列号
    for (j = 0; j < 5; j++)
    {
      printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
    }
  }
  return 0;
}

 

int类型的数组中,相邻的两个元素相差4个字节,因为一个整形元素占4个字节

image.png

二维数组内存中连续存放的,所以当知道了数组的起始地址,就可以知道后面的其它元素

image.png

(行可以省略,列不能省略的原因决定了一行有几个元素,一行有几个元素知道了,下一行放在哪才确定了)

 

(把“第一行”理解成一个一维数组“第二行”理解成一个一维数组……所以可以把二维数组看作是一维数组的数组一个一维数组就是一个元素

7.数组越界

  • 数组的下标是有范围限制的。

数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。

 


所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。

 


C语言本身不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。

 


 所以程序员写代码时,最好自己做越界的检查。

  • 二维数组也可能存在越界

   

数组越界实例

 

//数组越界
#include <stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  //下标:   0 1 2 3 4 5 6 7 8 9
  int i = 0;
  for ( i = 0; i <= 10; i++)
  {
    printf("%d\n", arr[i]);
    //当i等于10的时候,越界访问了
  }
  return 0;
}


33a4315b093f40ba84e9fb24349753da.png

8.数组作为函数参数

 

往往我们在写代码的时候,会将数组作为参数传给函数

例子:冒泡排序函数(算法思想)

 

未使用自定义函数:)

//输入10个整数,对这组整数进行排序:(未使用自定义函数)
//排序有很多种方法:
//1.  冒泡排序 
//2.  选择排序
//3.  插入排序
//4.  快速排序
//......
#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  int sz = sizeof(arr) / sizeof(arr[0]); //求元素个数
  //输入数组
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    scanf("%d", &arr[i]);
  }
//排序:冒泡排序,升序
  //趟数:总共要排多少次
  for (i = 0; i < sz - 1; i++) 
  //sz - 1:10个元素的话只用排9次
  { 
    //每趟排几次
    int j = 0;
    for (j = 0; j < sz - 1 - i; j++)
    //sz - 1 - i:第一次排9次,第二次只用排8次……
    {
      if (arr[j] > arr[j + 1])
      //前一个数大于后一个数,进行交换
      {
        //交换,交换两个值时,要有个中间变量
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      } 
    }
  }
  //打印数组
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

补充:对数组名的理解(重点)

 

//数组名的理解
#include <stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6 };
  //数组名通常情况下就是数组首元素的地址
  printf("%p\n", arr);
  printf("%p\n", &arr[0]); //数组首元素地址
  //有2个例外:
    //1. sizeof(数组名),数组名单独放在sizeof()内部,
      //这里的数组名表示整个数组,计算的是整个数组的大小
    printf("%d\n", sizeof(arr)); //打印结果:40
    //按道理,如果 arr 是首元素地址,那打印结果应该是 4 或 8
    //2. &数组名,这里的数组名也表示整个数组,取出的是整个数组的地址
    printf("%d\n", &arr);
    //数组的地址也是从起始位置开始的
    printf("%d\n", &arr + 1);
    // &arr + 1:会跳过整个数组,这里是40
    // 而 &arr[0] + 1 或 arr + 1 只会跳过1个元素,即4个字节
  return 0;
}



 未使用自定义函数:注意数组名传参的本质)

//输入10个整数,对这组整数进行排序:(未使用自定义函数)
//排序有很多种方法:
//1.  冒泡排序 
//2.  选择排序
//3.  插入排序
//4.  快速排序
//......
#include <stdio.h>
//使用自定义函数
//void bubble_sort(int arr[10]) 
//虽然写成int arr[10],但这里arr的本质是指针,是个指针变量
void bubble_sort(int* arr, int sz)
{
  //直接把数组元素个数sz传进来
  //sizeof(arr):所以这里算的是指针变量的大小即4,不是想要的整个数组大小
  //所以  sz   =    4 / 4 =1
  //int sz = sizeof(arr) / sizeof(arr[0]); //sz=1
  int i = 0;
  for (i = 0; i < sz - 1; i++)
    //sz - 1:10个元素的话只用排9次
  {
    //每趟排几次
    int j = 0;
    for (j = 0; j < sz - 1 - i; j++)
      //sz - 1 - i:第一次排9次,第二次只用排8次……
    {
      if (arr[j] > arr[j + 1])
        //前一个数大于后一个数,进行交换
      {
        //交换,交换两个值时,要有个中间变量
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
  }
}
int main()
{
  int arr[10] = { 0 };
  int sz = sizeof(arr) / sizeof(arr[0]); //求元素个数
  //输入数组
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    scanf("%d", &arr[i]);
  }
  //排序:冒泡排序,升序
  bubble_sort(arr, sz); 
  //让这个函数来完成数组arr中数据的排序
  //整形数组传参只写地址名就可以了
  //arr作为数组进行了传参,传递的是地址
  //数组传参,传递的是地址,传递的是首元素的地址
  //打印数组
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

5578ed39ab99432988c67ba78f427c51.png

(优化:发现有序之后就不一一排序了,在一趟冒泡排序发现两两排序后都没有对调时,说明已经可以停止排序了)

//输入10个整数,对这组整数进行排序:(未使用自定义函数)
//排序有很多种方法:
//1.  冒泡排序 
//2.  选择排序
//3.  插入排序
//4.  快速排序
//......
#include <stdio.h>
//使用自定义函数
//void bubble_sort(int arr[10]) 
//虽然写成int arr[10],但这里arr的本质是指针,是个指针变量
void bubble_sort(int* arr, int sz)
{
  //直接把数组元素个数sz传进来
  //sizeof(arr):所以这里算的是指针变量的大小即4,不是想要的整个数组大小
  //所以  sz   =    4 / 4 =1
  //int sz = sizeof(arr) / sizeof(arr[0]); //sz=1
  int i = 0;
  for (i = 0; i < sz - 1; i++)
    //sz - 1:10个元素的话只用排9次
  {
    //每趟排几次
    int j = 0; 
    // 每一趟开始前就假设已经有序了
    int flag = 1;
    for (j = 0; j < sz - 1 - i; j++)
      //sz - 1 - i:第一次排9次,第二次只用排8次……
    {
      if (arr[j] > arr[j + 1])
        //前一个数大于后一个数,进行交换
      {
        //交换,交换两个值时,要有个中间变量
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
        flag = 0; // 交换了就说明还是无序的
      }
    }
    //如果没有进入for循环进行交换,说明是有序的,就break停止循环
    if (flag == 1)
    {
      break;
    }
    //这样就不用数组有序了还进行排序
  }
}
int main()
{
  int arr[10] = { 0 };
  int sz = sizeof(arr) / sizeof(arr[0]); //求元素个数
  //输入数组
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    scanf("%d", &arr[i]);
  }
  //排序:冒泡排序,升序
  bubble_sort(arr, sz); 
  //让这个函数来完成数组arr中数据的排序
  //整形数组传参只写地址名就可以了
  //arr作为数组进行了传参,传递的是地址
  //数组传参,传递的是地址,传递的是首元素的地址
  //打印数组
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

image.png

9.数组的应用实例1:三子棋

10.数组的应用实例2:扫雷游戏

相关文章
|
21天前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
2月前
|
存储 安全 程序员
内存越界写入
【10月更文挑战第13天】
51 4
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
91 1
|
2月前
|
Rust 安全 Java
内存数组越界
【10月更文挑战第14天】
33 1
|
2月前
|
Java 编译器 C++
内存越界读取
【10月更文挑战第13天】
51 2
|
2月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
2月前
|
存储 容器
内存越界访问(Out-of-Bounds Access)
【10月更文挑战第12天】
236 2
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
23天前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
186 1
|
12天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。