对(一维)数组与指针的深入理解(1)

目录

  • 1.数组名的理解
  • 2.使用指针访问(一维)数组
  • 3.(一维)数组传参的本质

1.数组名的理解

以前我们在使用指针访问数组内容时,有这样的代码:

#include <stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
}

这里我们使用 &arr[0] 的方式拿到了数组第一个元素的地址,但是数组名本身就是首元素地址,我们来进行测试:

#include <stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

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

	return 0;
}

输出结果:

在这里插入图片描述

我们发现数组名和数组首元素的地址打印出来一模一样,数组名就是数组首元素(第一个元素)的地址。

那么下面的代码会让大家产生疑惑:

#include <stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	printf("%zd\n",sizeof(arr));

	return 0;
}

输出结果:

在这里插入图片描述

如果arr是首元素地址,那么在32位/64位平台下的地址大小应该是4/8个字节,但是这里是40个字节,这又该怎么解释呢?

其实数组名就是首元素地址是对的,但是有两个例外:

  • sizeof(数组名)
    sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
  • &数组名
    这里的数组名表示的是整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素地址是有区别的)

除上述两种情况外,任何地方使用数组名,数组名都表示首元素地址

我们再试一下这个代码:

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

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


	return 0;
}

输出结果:

在这里插入图片描述

三个打印出来的值都一模一样,这下我们又迷了,arr和&arr到底有啥区别呢?

接下来再来试试这个代码:

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

	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);

	printf("&arr       = %p\n", &arr);
	printf("&arr+1     = %p\n", &arr + 1);


	return 0;
}

输出结果:

在这里插入图片描述

我们可以发现&arr[0]和arr 加1都相差4个字节,是因为&arr[0]和arr都是首元素地址,+1就是跳过一个元素,这里的元素是整型。

但是&arr和&arr+1相差了40个字节,这就是因为&arr是整个数组的地址,+1跳过的是整个数组。

画图演示:

在这里插入图片描述
这样我们就理解了arr与&arr的区别。

2.使用指针访问(一维)数组

有了前面的知识以及数组的特点,我们就可以使用指针访问数组了。

我们能够使用指针访问数组主要基于以下两个原因:

  1. 数组在内存中是连续存放的。
  2. 指针±整数运算,方便我们获得每一个元素的地址
int main()
{
	int arr[10] = { 0 };
	//使用指针来访问数组
	int sz = sizeof(arr) / sizeof(arr[0]);

	//输入10个值
	int* p = arr;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		//输入一个值
		scanf("%d", p + i);// p+i == &arr[i]
	}

	//输出10个值
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}

	return 0;
}

输出结果:

在这里插入图片描述

其实上述代码中的第13行与第19行可以进行变更修改:

在这里插入图片描述

这里由读者自行写代码试验。

3.(一维)数组传参的本质

要讨论(一维)数组传参的本质,我们可以先从一个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把数组传给一个函数后,在函数内部求数组的元素个数吗?

代码1:在函数外求元素个数

void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);

	return 0;
}

输出结果:

在这里插入图片描述

代码2:在函数内求元素个数

void print(int arr[])
{
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr);

	return 0;
}

输出结果:

在这里插入图片描述

从输出结果我们可以知道,在函数内外计算元素个数的两个程序运行的结果不同,代码2明显与我们的预期结果不相符,这是为什么呢?

要检查哪里出了问题,我们可以对代码2进行调试:

在这里插入图片描述

经过调试,我们发现了当程序执行到print函数计算sz时,结果并不是数组的元素个数10,而是1,所以for循环只执行一次,打印输出结果为1。

为什么sz=1呢?这是我们要思考的地方。

原因是因为主函数中print(arr)中的arr数组名就是数组首元素的地址,当我们实参传入arr时,实参本质上是用指针变量接收的,所以print函数我们也可以定义成:

void print (int * p)//这里应该是指针
{

}

所以数组传参的时候,形参是可以写成数组形式的,但是本质上还是指针变量
所以代码2中sizeof(arr)实际上计算的是一个指针变量的大小,在x86环境下大小为4,而一个元素的大小也是4,所以相除得sz=1。

综上所述,我们可以知道:

  1. 数组传参的本质是传递了数组首元素的地址,所以形参访问的数组和实参的数组是同一个数组。
  2. 形参的数组是不会单独再创建数组空间的,所以形参的数组可以省略掉数组的大小。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/387250.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CTF-web 之 burp suite 使用

burp suite 使用 一般其是作为一个辅助工具&#xff0c;直接使用来解题的部分是少数&#xff0c;我们可以使用它来观察请 求和响应&#xff0c;并且可以反复的提交&#xff0c;关键的是他还带有很多其他的功能&#xff0c;在我们做题的过程中&#xff0c; 使用的关键点包括&…

C++LNK1207中的 PDB 格式不兼容;请删除并重新生成

在打开别人发的C文件时&#xff0c;可能出现该报错 解决办法 打开资源管理器&#xff0c;找到原来的路径 进入Debug&#xff0c; 找到对应的PDB文件删除即可。

OpenAI ChatGPT 记忆功能怎么实现?

你的聊天助手现在能“记住”你的对话了&#xff01; 2月14日凌晨&#xff0c;OpenAI宣布正在测试ChatGPT的新功能——记住用户提问内容&#xff0c;并自由控制内存。这意味着&#xff0c;ChatGPT能帮你记住那些重要的聊天内容&#xff0c;让你的对话更流畅、更自然。 想象一下…

政安晨:【示例演绎】【用TensorFlow编写线性分类器】—— 同时了解一点TensorFlow与Keras的基本概念

环境准备 如果小伙伴们第一次接触TensorFlow与Keras&#xff0c;可以先看一下我的这篇文章做些环境准备&#xff08;可以先忽略这篇文章里面代码实现部分&#xff0c;仅查看这里的环境准备部分即可&#xff09;。 文章如下&#xff1a; 政安晨&#xff1a;【详细解析】【用T…

Ps:焦点堆栈

焦点堆栈 Focus Stacking是一种摄影和图像处理技术&#xff0c;通过合并多张在不同焦距拍摄的照片来创建一张具有更大景深的图像&#xff0c;特别适用于微距摄影、风景摄影和任何需要在整个场景中保持尖锐对焦的情况。 ◆ ◆ ◆ 拍摄注意事项 1、使用三脚架 为了确保图像之间…

Spring 事务原理总结四

作为一名认知有限的中国人&#xff0c;我对年的喜爱&#xff0c;胜过其他一切&#xff0c;因为它给了我拒绝一切的合理理由。每到这个时候&#xff0c;我都会用各种理由来为自己的不作为开脱&#xff0c;今年亦是如此。看着频频发出警报的假期余额&#xff0c;我内心的焦躁变得…

VSCode无法连接远程服务器的两种解决方法

文章目录 VSCode Terminal 报错解决方式1解决方式2you are connected to an OS version that is unsupported by Visual Studio Code解决方法 VSCode Terminal 报错 直接在terminal或cmd中使用ssh命令可以连接服务器&#xff0c;但是在vscode中存在报错&#xff0c;最后一行为…

第12讲创建图文投票实现

创建图文投票实现 图文投票和文字投票基本一样&#xff0c;就是在投票选项里面&#xff0c;多了一个选项图片&#xff1b; <view class"option_item" v-for"(item,index) in options" :key"item.id"><view class"option_input&qu…

第13章 网络 Page724 asio定时器

程序代码&#xff1a; 11行&#xff0c;声明一个ios对象 13行&#xff0c;使用ios对象作为参数声明一个定时器&#xff0c;此时&#xff0c;定时器和ios完成了关联&#xff0c;后面定时器如果有任务的话&#xff0c;就可以将任务交给ios 16行&#xff0c;为定时器设置一个定…

Vue3+Vite+TS+Pinia+ElementPlus+Router+Axios创建项目

目录 初始项目组成1. 创建项目1.1 下载项目依赖1.2 项目自动启动1.3 src 别名设置vite.config.ts配置文件tsconfig.json配置若新创项目ts提示1.4 运行测试2. 清除默认样式2.1 样式清除代码下载2.2 src下创建公共样式文件夹`style`2.3 main.js中引入样式2.4 安装`sass`解析插件2…

Unity(单元测试)在STM32上的移植与应用

概述 Unity Test是一个为C构建的单元测试框架。本文基于STM32F407为基础&#xff0c;完全使用STM32CubeIDE进行开发&#xff0c;移植和简单使用Unity。 单片机型号&#xff1a;STM32F407VET6 软件&#xff1a;STM32CubeIDE Version: 1.14.1 Unity Version&#xff1a;2.…

小结与数字的魅力的开篇

小结 本系列主要介绍了一些排序算法&#xff0c;包括冒泡排序、快速排序、直接插入排序、希尔排序、简单选择排序、堆排序、归并排序、计数排序、桶排序和基数排序。 排序算法本身并不难&#xff0c;但其涉及的知识点却星罗棋布&#xff0c;其变化莫测的思路更让人难以捉摸&am…

【数据结构】哈希桶封装出map和set

利用之前的哈希桶封装出unordered_map和unordered_set。 这个封装并不简单&#xff0c;迭代器的使用&#xff0c;模板参数的繁多&#xff0c;需要我们一层一层封装。 map是一个k - v类型&#xff0c;set是k类型&#xff0c;那么就明确了如果需要封装&#xff0c;底层的tables…

Python算法探索:从经典到现代(三)

一、引言 随着信息技术的飞速发展&#xff0c;数据已经成为现代社会不可或缺的资源。Python&#xff0c;作为数据处理和分析的利器&#xff0c;为我们提供了大量强大的库和工具&#xff0c;用于从经典到现代的各种算法探索。本文将带你领略Python在算法领域的魅力&#xff0c;从…

OpenAI宣布ChatGPT新增记忆功能;谷歌AI助理Gemini应用登陆多地区

&#x1f989; AI新闻 &#x1f680; OpenAI宣布ChatGPT新增记忆功能&#xff0c;可以自由控制内存&#xff0c;提供个性化聊天和长期追踪服务 摘要&#xff1a;ChatGPT新增的记忆功能可以帮助AI模型记住用户的提问内容&#xff0c;并且可以自由控制其内存。这意味着用户不必…

mysql5.6安装---windows版本

安装包下载 链接&#xff1a;https://pan.baidu.com/s/1L4ONMw-40HhAeWrE6kluXQ 提取码&#xff1a;977q 安装视频 1.解压完成之后将其放到你喜欢的地址当中去&#xff0c;这里我默认放在了D盘&#xff0c;这是我的根目录 2.配置环境变量 我的电脑->属性->高级->环境…

今日arXiv最热NLP大模型论文:清华提出LongAlign,打破长上下文对齐瓶颈,数据、训练策略、评估基准一网打尽

随着LLMs的发展&#xff0c;其支持的上下文长度越来越长。仅一年时间&#xff0c;GPT-4就从一开始的4K、8K拓展到了128k。 128k什么概念&#xff1f;相当于一本300页厚的书。这是当初只支持512个tokens的BERT时代不敢想象的事情。 随着上下文窗口长度的增加&#xff0c;可以提…

【优化数学模型】1. 基于Python的线性规划问题求解

【优化数学模型】1. 基于Python的线性规划问题求解 一、线性规划问题1.概述2.三要素 二、示例&#xff1a;药厂生产问题三、使用 Python 绘图求解线性规划问题1.绘制约束条件2.绘制可行域3.绘制目标函数4.绘制最优解 四、使用 scipy.optimize 软件包求解线性规划问题1.导入库2.…

springboot742餐厅点餐系统

springboot742餐厅点餐系统 获取源码——》公主号&#xff1a;计算机专业毕设大全

面试前的准备

面试前的准备 Java程序员校招与社招的区别 校招和社招都是企业招聘形式的一种&#xff0c;只是面向的对象不同。校招 只允许在校生参加&#xff0c;社招理论上是任何人都能参加的(包括在校生)。 但是&#xff0c;无论是社招还是校招&#xff0c;它的难度都取决于你的水平高低。…