深入理解指针(二)

目录

1. 数组名的理解

2. 使用指针访问数组

3. ⼀维数组传参的本质

4. 冒泡排序

5. 二级指针

6. 指针数组

7. 指针数组模拟二维数组


1. 数组名的理解

有下面一段代码:

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];//取出第一个元素的地址,放到p里面。return 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("%d\n", sizeof(arr));return 0;
}

运行代码:

既然arr是数组首元素的地址,是地址的话就应该是4或者8个字节,那么为什么这里会输出40呢?

其实数组名就表示数组首元素的地址,这个说话是正确的,但是有两个例外:

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

2.&数组名,这⾥的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)。

除此之外,所以的数组名都表示数组首元素的地址。

我们知道sizeof(数组名)中的数组名表示整个数组,上面也举了例子,那么&数组名和数组名有啥区别,也就是说整个数组的地址和数组首元素的地址有啥区别。

#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);printf("&arr    = %p\n", &arr);return 0;
}

 运行代码:

三个地址的结果居然一摸一样。

数组首元素的地址和整个数组它们的地址是一样的,就说明它们的起始位置是一样的,那么arr和&arr的地址一样,本质上有啥区别呢?

前面我们说过指针类型决定了指针的差异,整型指针加1跳过4个字节,字符指针加1跳过1个字节。

#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[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;
}

运行代码:

 

2. 使用指针访问数组

#include <stdio.h>
int main()
{int arr[10] = { '\0' };//输入for (int i = 0; i < 10; i++){scanf("%d", &arr[i]);}//输出for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

 上面这种代码是使用下标的方式来访问数组,那我们也可以使用指针的方式来访问数组。 

#include <stdio.h>
int main()
{int arr[10] = { '\0' };//输入for (int i = 0; i < 10; i++){scanf("%d", arr+i);//scanf函数需要的是地址,arr表示首元素的地址,加i来遍历我的数组}//输出for (int i = 0; i < 10; i++){printf("%d ", *(arr + i));//arr表示首元素的地址,arr+i来遍历数组,*解引用就拿到地址中的值}return 0;
}

由此可以看出,使用指针也是可以的。

将*(arr+i)换成arr[i]也是能够正常打印的,所以本质上p[i] 是等价于 *(p+i)。 

同理arr[i]应该等价于*(arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移 量求出元素的地址,然后解引用来访问的。

我们知道加法是支持交换的,a+b就等价于b+a,那么*(arr+i)也可以写成*(i+arr)。

 i[arr]和arr[i]等价,本质上没什么区别,在编译器底层也是转换成指针,但是可读性不高。

总结:

1.数组就是数组,是一块连续的空间,是可以存放一个或者多个数据的。

2.指针变量是一个变量,是可以存放地址的变量。

3.数组和指针不是一回事,但是可以使用指针来访问数组。

为什么可以使用指针来访问数组呢?

1.数组在内存中是连续存放的。

2.指针的加减可以很方便的遍历数组,取出数组的内容(指针的运算)。

3. ⼀维数组传参的本质

求数组的元素个数。

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sz);return 0;
}

那如果我想写一个函数来计算数组元素个数呢?

 

这样写就是在函数内部求数组元素个数啊,可为什么是1呢?

 数组传参的时候,形参可以写成数组,也可以写成指针,数组传参的本质是传递的首元素的地址,所以形参即使写成数组的形式,本质上也是一个指针变量。

显然,在函数内部求数组元素的格式是不行的,那么函数参数就需要多加一个参数。

#include <stdio.h>
void func(int* arr,int sz)
{for (int 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]);func(arr,sz);return 0;
}

我们需要将数组元素个数计算出来然后传给函数形参。

4. 冒泡排序

冒泡排序的核心思想就是:两两相邻的元素进行比较。

#include <stdio.h>
void BubbleSorting(int* arr, int sz)
{for (int i = 0; i < sz - 1; i++){for (int j = 0; j < sz - i - 1; j++){if ((*(arr + j) > *(arr + j+1))){int tem = *(arr + j);*(arr + j) = *(arr + j + 1);*(arr + j + 1) = tem;}}}
}int main()
{int arr[10] = { '\0' };int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++)scanf("%d", arr + i);BubbleSorting(arr, sz);for (int i = 0; i < sz; i++)printf("%d ", *(arr + i));return 0;
}

上面的代码虽然可以完成我们的需求,但是还是不够好,加入是1,2,3,4,5,6,7,8,9,10这样的数字呢?明显就是升序啊,而上面的代码还会一一去比较大小,这样就是在浪费时间,当我内置的循环第一次结束的时候而一对数字都没有交换的话就说明数组本来就是升序,这个时候就不需要进行第二轮的比较了。

优化后:

#include <stdio.h>
void BubbleSorting(int* arr, int sz)
{for (int i = 0; i < sz - 1; i++){int flag = 0;//假设是有序的for (int j = 0; j < sz - i - 1; j++){if ((*(arr + j) > *(arr + j + 1))){flag = 1;//不是有序的int tem = *(arr + j);*(arr + j) = *(arr + j + 1);*(arr + j + 1) = tem;}}if (!flag)break;}
}int main()
{int arr[10] = { '\0' };int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++)scanf("%d", arr + i);BubbleSorting(arr, sz);for (int i = 0; i < sz; i++)printf("%d ", *(arr + i));return 0;
}

为了看出效果,我们可以拿优化前和优化后的代码做个对比。 

 对于这种以及是升序的代码,优化前需要比较45次,而优化后只需要比较9次。

5. 二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址就存放在⼆级指针 。

int main()
{int a = 10;int* p = &a;//p是一级指针int** pa = &p;//pa是二级指针return 0;
}

那么二级指针该怎么用呢?

 当有一天我需要将指针变量的地址存起来的时候就可以使用二级指针,二级指针和二维数组没有对应的关系。

6. 指针数组

指针数组是指针还是数组呢?

如果实在不理解,我们可以换个方式。

char ch[10];//字符数组  --  存放字符的数组
int arr[10];//整型数组  --  存放整型的数组

那么指针数组就是存放指针的数组,数组的每个元素其实都是指针类型。

那么我们可以来写一下指针数组。

char* ch[5];//存放字符指针的数组,每个元素是char*类型,一个5个元素
int* arr[5];//存放整型指针的数组,每个元素是int*类型,一个5个元素

写个代码来理解一下吧。

 

#include <stdio.h>
int main()
{int a = 10;int b = 20;int c = 30;int* arr[3] = { &a,&b,&c };for (int i = 0; i < 3; i++)printf("%d ", *(arr[i]));return 0;
}

我们怎么放进去的就怎么拿出来,但是这种写法比较死板。

7. 指针数组模拟二维数组

#include <stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* arr[] = { arr1,arr2,arr3 };for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++)printf("%d ", arr[i][j]);//使用二维数组的方式访问//arr[i][j] <===> *(*(arr+i)+j)printf("\n");}return 0;
}

用指针数组也可以模拟实现二维数组。

 arr[i]是访问arr数组的元素,arr[i]找到的数组元素指向了整型⼀维数组,arr[i][j]就是整型⼀维数组中的元素。

上述的代码模拟出⼆维数组的效果,实际上并非完全是⼆维数组,因为每一行并非是连续的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年6月14日 十二生肖 今日运势

小运播报&#xff1a;2024年6月14日&#xff0c;星期五&#xff0c;农历五月初九 &#xff08;甲辰年庚午月己酉日&#xff09;&#xff0c;法定工作日。今天世界献血日&#xff0c;捐献新鲜血液&#xff0c;挽救更多生命&#xff0c;每位献血者都是英雄&#xff01; 红榜生肖…

美创科技入选“2024网络安全提供商创新排行榜”

近日&#xff0c;DBC德本咨询公布了“2024网络安全提供商创新排行榜”&#xff0c;美创科技凭借近20年的数据安全创新耕耘&#xff0c;荣誉上榜。 此次&#xff0c;与360、华为、腾讯等互联网、网络安全头部厂商并肩上榜&#xff0c;是行业对美创的再次认可。 数据安全的发展离…

什么是基于风险的漏洞管理RBVM,及其优势

文章目录 一、什么是漏洞管理二、什么是基于风险的漏洞管理RBVM三、RBVM的基本流程四、RBVM的特点和优势 一、什么是漏洞管理 安全漏洞是网络或网络资产的结构、功能或实现中的任何缺陷或弱点&#xff0c;黑客可以利用这些缺陷或弱点发起网络攻击&#xff0c;获得对系统或数据…

【log4】log4cplus:使用详解(一)

1、源码下载 源码下载地址:https://sourceforge.net/projects/log4cplus/files/log4cplus-stable/ 最新稳定版本为2.1.1(2023-11-17) github中有最新的源码:https://github.com/log4cplus/log4cplus 2、源码编译 1)解压后,进入源码目录中,执行配置命令: ./confi…

闪烁圆点加载动画

效果图: 完整代码: <!DOCTYPE html> <html> <head><meta charset="UTF-8" /><title>闪烁圆点加载动画</title><style type="text/css">body {background: #ECF0F1;display: flex;justify-content: center;al…

最实用的AI软件开发工具CodeFlying测评

就在上个月&#xff0c;OpenAI宣布GPT-4o支持免费试用&#xff0c;调用API价格降到5美元/百万token。 谷歌在得到消息后立马将Gemini 1.5 的价格下降到0.35美元/百万token。 Anthropic的API价格&#xff0c;直接干到了0.25美元/百万token。 国外尚且如此&#xff0c;那么国内…

高创新 | CEEMDAN-VMD-BiLSTM-Attention双重分解+双向长短期记忆神经网络+注意力机制多元时间序列预测

目录 效果一览基本介绍模型设计程序设计参考资料 效果一览 基本介绍 高创新 | CEEMDAN-VMD-BiLSTM-Attention双重分解双向长短期记忆神经网络注意力机制多元时间序列预测 本文提出一种基于CEEMDAN 的二次分解方法&#xff0c;通过样本熵重构CEEMDAN 分解后的序列&#xff0c;复…

Multimodal Dynamics:用于多模态融合背景下的分类

Multimodal Dynamics&#xff08;MD&#xff09;是可信赖的多模态分类算法&#xff0c;该算法动态评估不同样本的特征级和模态级信息量&#xff0c;从而可信赖地对多模态进行融合。 来自&#xff1a;Multimodal Dynamics: Dynamical Fusion for Trustworthy Multimodal Classi…

嵌入式linux中设备树使用of函数操作基本方法

各位开发者大家好,今天主要给大家分享一下,如何使用of操作函数,获取对应设备树节点先关的属性信息。 第一:of_find_property函数 of_find_property 函数用于在设备树中查找节点下具有指定名称的属性。如果找到了该属性,可以通过返回的属性结构体指针进行进一步的操作,比…

【Linux】进程_2

文章目录 五、进程2. 操作系统3. 进程 未完待续 五、进程 2. 操作系统 我们知道了操作系统是一个进行 软硬件 资源 管理 的 软件 。为什么要有操作系统呢&#xff1f;或者说&#xff0c;为什么要有操作系统的管理呢&#xff1f;操作系统的存在目的是为了对上提供一个良好的运行…

机器学习第四十三周周报 aGNN

文章目录 week43 aGNN摘要Abstract1. 题目2. Abstract3. 网络架构3.1 aGNN3.1.1 输入与输出模块3.1.2 嵌入层3.1.3编码器解码器模块&#xff1a;带有多头注意力层的GCN 3.2 可释性模型&#xff1a;SHAP 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1 实验区域以及场…

SpringMVC:拦截器(Interceptor)

1. 简介 拦截器&#xff08;Interceptor&#xff09;类似于过滤器&#xff08;Filter&#xff09; Spring MVC的拦截器作用是在请求到达控制器之前或之后进行拦截&#xff0c;可以对请求和响应进行一些特定的处理。拦截器可以用于很多场景下&#xff1a; 1. 登录验证&#xf…

2024年最新Microsoft Edge关闭自动更新的方法分享

这里写自定义目录标题 打开【服务】 打开【服务】 windows中搜索服务&#xff0c;如下图&#xff1a; 打开服务界面&#xff0c;找到“Microsoft Edge Update Service (edgeupdate)” 及 “Microsoft Edge Update Service (edgeupdatem)” 两个服务&#xff0c;设置为禁用

matlab-1-函数图像的绘制

常识 如何建一个新文件 创建新文件&#xff0c;点击新建&#xff0c;我们就可以开始写代码了 为什么要在代码开头加入clear 假如我们有2个文件&#xff0c;第一个文件里面给x赋值100&#xff0c;第二个文件为输出x 依次运行&#xff1a; 结果输出100&#xff0c;这是因为它们…

Landsat8的质量评估波段的一个应用

Landsat8一直是遥感界的热门话题。这不仅延续了自1972年以来NASA连续对地观测&#xff0c;而且这颗卫星为科学界带来了一些新的东西——质量评估波段&#xff08;the Quality Assessment (QA) Band&#xff09;。根据USGS Landsat Missions webpage&#xff0c;“QA通过标示哪个…

强大高效,推荐这两款分析文章和抠图的AI工具

ChatDOC ChatDOC是一款基于ChatGPT的AI阅读辅助工具&#xff0c;旨在通过与用户指定的文档进行对话来处理用户的专属数据。它能够帮助用户快速提取文档中的信息&#xff0c;支持多种文件格式&#xff0c;并提供准确的答案。此外&#xff0c;ChatDOC还具备智能格式化、自动摘要生…

大模型微调出错的解决方案(持续更新)

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

鲁教版八年级数学下册-笔记

文章目录 第六章 特殊平行四边形1 菱形的性质与判定2 矩形的性质与判定3 正方形的性质与判定 第七章 二次根式1 二次根式2 二次根式的性质3 二次根式的加减二次根式的乘除 第八章 一元二次方程1 一元二次方程2 用配方法解一元二次方程3 用公式法解一元二次方程4 用因式分解法解…

css系列:音频播放效果-波纹律动

介绍 语音播放的律动效果&#xff0c;通俗来说就是一个带动画的特殊样式的进度条&#xff0c;播放的部分带有上下律动的动画&#xff0c;未播放的部分是普通的灰色竖状条。 实现中夹带了less变量、继承和循环遍历&#xff0c;可以顺带学习一下。 结果展示 大致效果如图所示…

防火墙安全管理

大多数企业通过互联网传输关键数据&#xff0c;因此部署适当的网络安全措施是必要的&#xff0c;拥有足够的网络安全措施可以为网络基础设施提供大量的保护&#xff0c;防止黑客、恶意用户、病毒攻击和数据盗窃。 网络安全结合了多层保护来限制恶意用户&#xff0c;并仅允许授…