数组与地址,数组名到底是什么?

简介: 数组与地址,数组名到底是什么?

数组与地址,数组名到底是什么?

1.问题引出

2.分析错误代码

3.数组名是什么?

4.错误分析与修改

错误分析:

修改:

5.总结

1.问题引出


案例:设计一个函数,可以将整形数组的次序调换

例如:arr[5] = {1,2,3,4,5},输出形式为:arr[5] = {5,4,3,2,1}.


案例代码:


//能否可以正常排序?
#include <stdio.h>
void reverse(int* arr)
{
  int len = sizeof(arr) / sizeof(arr[0]);
  int top = 0;
  int tail = len - 1;
  while (top < tail)
  {
  int tmp = 0;
  tmp = arr[top];
  arr[top] = arr[tail];
  arr[tail] = tmp;
  top++;
  tail--;
  }
}
int main()
{
  int arr[] = { 1,2,3,4,5 };
  int len = sizeof(arr) / sizeof(arr[0]);
  reverse(arr);
  for (int i = 0; i < len; i++)
  {
  printf("%d ", arr[i]);
  }
  return 0;
}



我们来运行一下:

30bfda4ca7224971907f6f0ef3a5cfbd.png

我们发现结果并不是和我们想象的一样输出 5 4 3 2 1 ,而是对于原数组来说没有变化,这是为什么呢?


2.分析错误代码


首先3f19b3babf334ee6885182b08196ca95.png,按F10,点击调试——>窗口——>监视


在监视窗口中输入arr,这时是运行在主函数中,所以监控的是主函数中的arr


96213c4c085f4b358c21485d26e431cb.png



这里能够看到,arr中储存着5值


然后按F11进入函数内部,这时的监视窗口是这样:



1ce000cfa4914a27a6b878fcc84ce2ff.png


这里有个技巧:需要在监视中输入arr,5才能正常监视到:



9a905aab6fe2436c8934fe74cdbef71f.png


代码再往下走,我们发现len的值为1


98903d38100c400fb4573aefbc9fe1f4.png



但是int len = sizeof(arr) / sizeof(arr[0]),len的值应该为arr数组的长度啊,应该是5,为什么是1呢,这就需要了解数组名到底是什么


3.数组名是什么?


先探究数组名与数组元素首元素地址:


int main()
{
  int arr[10] = { 0 };
  printf("%p\n", arr);    //数组名
  printf("%p\n", &arr[0]);//数组首元素地址
  return 0;
}


结果:

9eb764848e5d4236a110de9c57633678.png


所以得出结论:数组名是数组首元素的地址

但是有两个例外:

1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节

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


验证一下sizeof(数组名):

int main()
{
  int arr[10] = { 0 };
  printf("%d", sizeof(arr));
  return 0;


结果:f90e360355d643ee85bed9eec52f839c.png


数组长度为10,int类型一个元素4个字节,所以输出40,这就说明:

sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小


验证 &数组名:


int main()
{
  int arr[10] = { 0 };
  printf("%p\n", arr);    //数组名
  printf("%p\n", &arr[0]);//数组首元素地址
  printf("%p\n", &arr);   //数组名取地址
  return 0;
}

e77ceebf6cb349c8a90a1955a4f17c42.png

结果:


发现这三个输出的地址都相同,它们有什么区别呢?我们再来探究一下


12f194a41b984226b2bbe201a87aaf23.png


arr与·&arr[0]一样,都是指向数组首元素地址,他们的类型都是int*

而&arr是整个数组的地址,&arr的类型为:int(*)[10]

由于这个数组的地址由数组首元素地址开头,所以这三个地址值从表面上看起来一样,但可以从他们的指针类型去区别他们


再对比一下:


int main()
{
  int arr[10] = { 0 };
  printf("arr:       %p\n", arr);    
  printf("arr+1:     %p\n", arr+1);
  printf("&arr[0]:   %p\n", &arr[0]);
  printf("&arr[0]+1: %p\n", &arr[0]+1);
  printf("&arr:      %p\n", &arr);  
  printf("&arr+1 :   %p\n", &arr+1);
  return 0;
}

cd75afd89302472381f60bef25e77081.png

结果:


分析:

165db43d7fca423482fe4d2de2687440.png

arr与&arr[0]都跳四个字节,也就是数组下一个元素地址

而&arr跳过了40个字节,它跳过了整个数组

从这里的计算,也可以体会出arr与&arr[0]的类型是int*,而&arr的类型是int(*)[10]


4.错误分析与修改


错误分析:

前面的内容我们知道了数组名是数组首元素地址,本质上是个指针

所以在函数中,就需要用int*去接收这个指针变量


void reverse(int* arr)
{
  int len = sizeof(arr) / sizeof(arr[0]);
  int top = 0;
  int tail = len - 1;
  while (top < tail)
  {
  int tmp = 0;
  tmp = arr[top];
  arr[top] = arr[tail];
  arr[tail] = tmp;
  top++;
  tail--;
  }
}


而在reverse内部,sizeof(arr)中的arr被判定成了指针,而不是一个数组,所以sizeof(arr)的值为4,sizeof(arr[0])也为4,所以len的值为1,而top=0,tail = len-1 = 0,top = tail,根本进不去下面的循环,所以数组才不会改变


修改:

因为数组作为参数,它的长度不可以在函数内部被计算出来,所以就要在主函数中把数组的长度计算出来,将数组长度作为参数传到函数中


#include <stdio.h>
void reverse(int* arr,int len)
{
  int top = 0;
  int tail = len - 1;
  while (top < tail)
  {
  int tmp = 0;
  tmp = arr[top];
  arr[top] = arr[tail];
  arr[tail] = tmp;
  top++;
  tail--;
  }
}
int main()
{
  int arr[] = { 1,2,3,4,5 };
  int len = sizeof(arr) / sizeof(arr[0]);
  reverse(arr,len);    //两个参数,一个是数组首地址,一个是数组长度
  for (int i = 0; i < len; i++)
  {
  printf("%d ", arr[i]);
  }
  return 0;
}


结果正确:

7ba9137966da4df59a48de25cb79f508.png


因为我们知道数组名其实就是一个指针,所以在函数的参数列表中,可以使用int arr[]去接受,也可以用int* arr去接受收


//这两种形式都可以
void reverse(int* arr,int len);
void reverse(int arr[],int len);


5.总结

1.一般情况下,数组名就是数组首元素的地址

2.有两个特殊情况:

sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。

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

3.有数组作为参数传入的函数中,想要计算这个数组的长度,在函数内部做不到,需要在主函数中将数组长度计算出来,将数组长度作为参数传入函数中,这才可以在函数中使用


目录
相关文章
|
10月前
|
人工智能 Kubernetes Cloud Native
荣获2024年AI Cloud Native典型案例,阿里云容器产品技术能力获认可
2024全球数字经济大会云·AI·计算创新发展大会,阿里云容器服务团队携手客户,荣获“2024年AI Cloud Native典型案例”。
|
芯片
IELTS学习(001) - 语法构成原理
IELTS学习(001) - 语法构成原理
120 0
|
传感器 安全 物联网
下一次大跃进?将机器人与物联网相结合
物联网是一种流行的物体视觉,通过互联网连接来回传递信息,使我们的生活更轻松,更舒适。它在我们的家庭中出现,从语音控制扬声器到智能温度传感器。为了提高我们的健康水平,智能手表告诉在线应用程序我们正在移动多少。
1235 0
|
9天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
8天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
366 130
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
|
8天前
|
人工智能 Java API
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
本文介绍AI大模型的核心概念、分类及开发者学习路径,重点讲解如何选择与接入大模型。项目基于Spring Boot,使用阿里云灵积模型(Qwen-Plus),对比SDK、HTTP、Spring AI和LangChain4j四种接入方式,助力开发者高效构建AI应用。
356 122
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
|
20天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1342 8
|
2天前
|
存储 JSON 安全
加密和解密函数的具体实现代码
加密和解密函数的具体实现代码
190 136