数组传参不理解?(数组传参的本质)

简介: 数组传参不理解?(数组传参的本质)

   在我们编写程序时,经常需要传递参数给函数,其中一种常见的参数类型就是数组。数组作为一种数据结构,可以存储多个相同类型的数据元素,并按照一定的顺序排列。在函数中传递数组参数,可以方便地对数组进行操作处理。但是,数组传参也有其特殊和需要注意的地方。在文中,我将向大家讲解数组作为参数传递时的一些问题和解决方法。学习之前我们要先了解数组的一些基础知识,也是对前期数组知识的补充。

一维数组与二维数组

本文将重点针对对一维数组和二维数组进行讲解。

前几期中我对一维数组的创建及初始化已进行了讲解,先对二维数组进行简单介绍。

二维数组

二维数组我们可以理解为一个数组里放了多个一维数组

例如:arr[4][5]:可以理解为arr[4][5]中存放了四个数组,分别名为arr[0],arr[1],arr[2],arr[3]每个数组大小都是5;

arr[0]:arr[0][0],arr[0][1],arr[0][2],arr[0][3],arr[0][4]

arr[1]:arr[1][0],arr[1][1],arr[1][2],arr[1][3],arr[1][4]

arr[2]:arr[2][0],arr[2][1],arr[2][2],arr[2][3],arr[2][4]

arr[3]:arr[3][0],arr[3][1],arr[3][2],arr[3][3],arr[3][4]

二维数组和一维数组在内存中存储都是连续存放的。

首先我们先写一个小程序对二维数组进行初始化

#include<stdio.h>
int main() {
    int arr[2][3];
    int i = 0;
    for (i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            scanf("%d", &arr[i][j]);
        }
    }
    for (i = 0; i < 2; i++) {
        int j = 0;
        for (j = 0; j < 3; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

通过调试时我们可以发现相邻的两个元素之间地址相差4(这里表示地址使用的是16进制,每个元素相差4个字节),并且是连续的。随着数组下标的增长,元素的地址也在有规律的增长(由低地址到高地址)。

二维数组的创建

二维数组的创建与一维数组很相似,当然我们也可以创建多种类型的数组

char arr[2][3];
int arr1[3][4];
double arr2[2][4];

二维数组的初始化

有以下几种初始化形式。

int arr[3][4]={1,2,3,4};
int arr1[3][4]={{1,2},{3,4}};
int arr2[][4]={{2,3},{4,5}};

二维数组的两个[],分别代表的是行和列,前两个数组创建时是一个三行四列的数组,并且在数据元素不够时,会进行补0操作。

注意:二维数组的行可以省,但列不行,计算机会根据初始化时的数据自动计算行的值,而对于列,规定列的大小是为了确定后续计算机进行补0的个数。

数组越界

数组的下标是有范围的。

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

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

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

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

如上图,编译器没有报错,最后一个数字因为数组越界访问输出结果是随机值。

当然二维数组的行和列也可能存在越界访问

数组作为函数参数

在写代码时,会将数组作为参数传给函数,比如:实现一个冒泡排序的函数

先解释一下冒泡排序的原理以升序为例:相邻两个数字进行比较,较大数与后一个数位置交换

最终直到最大数到最后的位置(此过程需要比较9次)

这个过程为一趟,而第二趟就只需要交换8次,第三趟需要交换7次,需要交换的次数依次减少,一共需要9趟(交换次数也就是9!)由于我示例数据是降序,要排列为升序,需要交换的次数是最多的,在乱序数据中,交换次数只会比示例交换次数少。

代码实现:

for (int j = 0; j < sz-1; j++) //sz是数组元素个数
  {
    for (int i = 0; j < sz - 1 - i; i++) 
    {
      if (arr[i] > arr[i + 1]) 
      {
        t = arr[i];   //t是作为交换的中间媒介
        arr[i] = arr[i + 1];
        arr[i + 1] = t;
      }
    }
  }

写一个冒泡排序函数该如何实现呢?

void bubble(int arr[10]) {
  int t;
    int sz = sizeof(arr) / sizeof(arr[0]);//计算数组元素个数
  for (int j = 0; j < sz-1; j++) 
  {
    for (int i = 0; j < sz - 1 - i; i++) 
    {
      if (arr[i] > arr[i + 1]) 
      {
        t = arr[i];
        arr[i] = arr[i + 1];
        arr[i + 1] = t;
      }
    }
  }
}
int main()
 {
  int arr[10];
  for (int i = 0; i < 10; i++) {
    scanf("%d", &arr[i]);
  }
bubble(arr);
  for (int i = 0; i < 10; i++) {
    printf("%d ", arr[i]);
  }
  return 0;
}

这段代码可以实现冒泡排序吗?当结果运行起来我们会发现,结果并没有预期那样排序,这是为什么呢?

在调用函数时bubble(arr),arr作为数组进行传参,数组传参,传递的实质是首元素的地址,而void bubble(int arr[10])这里的arr[10]本质上也是指针,在函数中我们使用sizeof计算数组大小也仅仅只是形参接收的首元素地址。

通过调试我们会发现这里的sz的值是1(在64位操作系统结果是2,64位操作系统中,指针变量占8个字节除以arr[0]整形所以结果是2)。

或许有同学会疑惑arr是数组首元素地址那sizeof(arr)为什么就可以计算数组长度。

这里额外补充一下

数组名的理解

数组名该怎么理解?

在通常情况下数组名就是首元素的地址。但是有两个意外

1.sizeof(数组名),数组名单独放在sizeof()内部,这里的数组名表示整个数组,计算的是数组大小

2.&数组名,这里的数组名也是表示整个数组,取出的是整个数组的地址

除此之外所有遇到的数组名都表示数组首元素的地址。

这里通过代码给大家更清晰的呈现:

int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9 };//%p用来打印地址
  printf("%p\n", arr);
  printf("%p\n", &arr[0]);
  printf("%p\n", &arr);
  printf("%p\n", &arr+1);
  printf("%p\n", arr+1);
  return 0;
}

运行结果如下:

我们发现前3个结果相同,这是因为整个数组的地址也是从首元素地址开始的,将&arr+1,和arr+1我们就会发现结果不同。&arr+1结果与首元素地址相差28(这里28是16进制转化为10进制就是40。)正好相差一个数组的大小,arr+1与首元素地址相差4。

区别在于:arr+1跳过的是一个数组元素,而&arr+1跳过的是整个数组。

那如何完成冒泡排序函数呢?

也很简单,只需在主函数里求好数组元素个数,传到函数中(上述情况说明:数组元素是数组时没法在函数中计算大小,这里也仅限数组元素是数字,字符串数组可以),就可以解决这个问题。

代码修改后:

void bubble(int arr[10],int sz) {
  int t;
  for (int j = 0; j < sz-1; j++) 
  {
    for (int i = 0; j < sz - 1 - i; i++) 
    {
      if (arr[i] > arr[i + 1]) 
      {
        t = arr[i];
        arr[i] = arr[i + 1];
        arr[i + 1] = t;
      }
    }
  }
}
int main() {
  int arr[10];
  for (int i = 0; i < 10; i++) {
    scanf("%d", &arr[i]);
  }
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble(arr, sz);
  for (int i = 0; i < 10; i++) {
    printf("%d ", arr[i]);
  }
  return 0;
}

这里在给大家补充一点:计算数组元素个数有sizeof(数组名)/sizeof(arr[0]),和strlen(数组名)两种,

char str[]="hello world"
printf("%d %d",sizeof(str),strlen(str));//输出结果是:12 11

sizeof(str):获取数组的总大小,12个元素,每个元素占1个字节,因此总共是12个字节

strlen(str): 获取字符串中有效字符的个数,不算'\0',因此总共11个有效字符。此外strlen是只能计算字符串长度。在函数中可以使用strlen计算字符串数组的大小(当然字符串数组传进去的也是数组首元素地址)。

好了本期内容到这里就结束了,希望对你有所帮助,感谢观看!

相关文章
|
8月前
|
弹性计算 负载均衡 安全
【上云基础系列03】基于标准架构的安全升级
本文回顾了业务上云从基础到进阶的理念,介绍了企业在不同发展阶段所需的架构选择。在“入门级:上云标准弹性架构基础版”的基础上,本文针对安全升级,重点介绍了:(1)公网入口基于应用型负载均衡ALB集成WAF防护,提升Web应用的安全性;(2)公网出口则通过NAT网关升级为CFW防火墙,保障出站流量的安全。 此外,还提供了详细的架构演进说明,涵盖从入门级标准弹性架构到高级安全能力和数据库升级的全过程。
|
存储 SQL 监控
一文解读如何落地企业级云上日志审计方案
近年来,随着云计算的广泛应用和企业上云的深度普及,许多企业或个人用户将各种日志托管在云上进行留存,从而进行查询、审计等操作。什么是日志审计?为什么要做日志审计?企业如何落地云上日志审计方案?带着这些问题出发,本文将以阿里云日志服务(SLS)旗下的日志审计服务为视角切入,带领读者从当今网络安全形势和法律法规要求出发,解读云计算背景下,如何构建、运行、实践一个标准的企业级云上审计方案,从而更好地保障企业或组织的云上资产、云上数据的安全,确保企业业务持续安全稳定地运行。
|
SQL 安全 中间件
AppScan安全扫描工具之安装及配置GlassBox
IBM AppScan是一个自动化Web应用安全漏洞评估工具,通过安装和配置GlassBox可以检测更多Web程序的安全漏洞。
173 0
AppScan安全扫描工具之安装及配置GlassBox
|
运维 Devops 应用服务中间件
阿里云云效操作报错合集之从企业仓库里拉取依赖报错403,该如何解决
本合集将整理呈现用户在使用过程中遇到的报错及其对应的解决办法,包括但不限于账户权限设置错误、项目配置不正确、代码提交冲突、构建任务执行失败、测试环境异常、需求流转阻塞等问题。阿里云云效是一站式企业级研发协同和DevOps平台,为企业提供从需求规划、开发、测试、发布到运维、运营的全流程端到端服务和工具支撑,致力于提升企业的研发效能和创新能力。
阿里云云效操作报错合集之从企业仓库里拉取依赖报错403,该如何解决
|
关系型数据库 MySQL 大数据
C#使用SqlSugar操作MySQL数据库实现简单的增删改查
C#使用SqlSugar操作MySQL数据库实现简单的增删改查
699 2
|
算法 C语言 C++
万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(一)
三子棋游戏设计的核心是对二维数组的把握和运用。
240 1
|
XML Java Android开发
Myeclipse 为项目设置UTF-8格式:
在Eclipse中设置UTF-8编码:1) Window -&gt; Preferences,选择General -&gt; Workspace,设置Text file encoding为UTF-8。2) 同样路径,进入Content Types,选中Text,设Java Source File默认编码为UTF-8。其他文件类型如properties和XML默认已设定。
300 2
|
存储 网络安全 开发工具
Git 协同开发详解:从基础命令到多人协作
Git 协同开发详解:从基础命令到多人协作
302 0
|
存储 PHP 数据安全/隐私保护
攻防世界 Web_php_unserialize
攻防世界 Web_php_unserialize
308 0
|
JavaScript API
「宜搭」如何在选择部门组件后,自动带出该部门下的成员,并查询该成员当月考勤天数
本文档只做参考使用,请结合具体需求修改,如有问题,概不负责!!!! 因本章涉及页面代码,请确保宜搭使用版本可使用页面JS 该篇只会获取当前部门下的成员,不包含下级部门; 注意:涉及到钉钉接口,请严格按照钉钉开放接口文档要求操作; 若此文章对您有帮助,记得点下赞同哦~
904 4
「宜搭」如何在选择部门组件后,自动带出该部门下的成员,并查询该成员当月考勤天数