深入理解并打败C语言难关之一————指针(3)

news2024/11/24 17:20:42

前言:

  昨天把指针最为基础的内容讲完了,并且详细说明了传值调用和传址调用的区别(这次我也是做到了每日一更,感觉有好多想写的但是没有写完),下面不多废话,下面进入本文想要说的内容

目录:

1.数组名的理解

1.1数组名代表着什么

1.2.数组名的几种特殊情况

2.使用指针访问数组

2.1指针如何访问数组

3.一维数组传参的本质

4.冒泡排序算法的实现(先预热一下,这个太重要我写在下一篇了)

5.二级指针相关知识点

5.1二级指针是什么

5.2二级指针中的解引用操作

6.指针数组相关知识点

6.1指针数组

6.2利用指针数组模拟二维数组

正文:

1.数组名的理解

1,1数组名是什么

  其实数组名我在之前就解释过了,现在我们正式的来介绍它,数组名是数组首元素的地址,通过代码的撰写就可以看出来二者是一样的,代码以及展示图如下:

#include<stdio.h>
int main()
{
    int arr[10] = {0};
    int *p = &arr[0];
    printf("%p",arr);
    printf("%p",p);
    return 0;
}

  在说上面之前,我想起来我忘记说%p这个占位符的作用了,这个占位符是代表着地址的占位符,想要打印地址就需要它,不难看出,上面两个地址打印出来的结果是一样的,所以可以更好的理解数组名就是数组首元素的地址,不过,这也不是绝对的,下面一种特殊情况数组名就不是数组第一个元素的地址。

1.2数组名的几种特殊情况

  1.2.1sizeof(arr)

  我们知道,sizeof运算符是用来计算长度的,上面讲了,arr指的是数组第一个元素的地址,按道理来讲,我们计算arr的长度应该就是第一个元素的长度即四个字节,但是看见下面的代码和打印结果:

#include<stdio.h>
int main()
{
    int arr[10] = {0};
    printf("%zd",sizeof(arr));
    return 0;
}

  我们不难看出,这里打印的结果是40个字节,为什么会打印出40呢?因为这是arr整体的元素个数,所以此时sizeof(arr)并不是第一个元素的长度,而是整个元素的长度,这个一定要记住,在某些企业的笔试题中或者正常的考试题中可能会出一个类似这样的题来迷惑你,所以我们要记住这个特殊情况! 不要出错,不要出错,不要出错!

  1.2.2&arr

  上面小编说过,arr是数组名,代表着数组第一个元素的地址,那么我们如果直接对arr去地址呢?这种情况我们先通过代码打印看一下:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr);
	return 0;
}

  看到这里可能有些读者朋友会想,这三个的地址不都是相同的吗?是不是&arr就是数组第一个元素的地址呢?当然,如果真一样的话,我写这个小标题就一点意义都没有了,先不要着急,我们再看一下下面这个代码就可以知道&arr到底指的是什么了:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr + 1);
	printf("%p\n", &arr[1]);
	printf("%p\n", &arr + 1);
	return 0;
}

  这个时候就可以很清楚的发现三者的不同了,前两个肯定是一样的(前面的知识点的运用),而第三个,经过我们的计算,发现比前两个多了36个字节的内存空间 ,这是为什么呢?我也不卖关子了,其实取地址arr取到的是整个数组的地址,所以我们在进行加1操作的时候,其余都是加了一个字节,而&arr是跳跃了一个完全的数组,从而我们在想要取到地址的时候,一定要知晓取到的应该是一个完整的数组地址,不过刚开始指向的是数组第一个元素的地址罢了,在进行加减的时候一定要小心。

小结:

  我们在使用数组名的时候一定要注意这两个特殊情况,除了这两个特殊情况意外,其余的数组名都是代表首元素的地址

2.使用指针去访问数组

2.1指针如何访问数组

  其实用指针数组我在上一节课的代码中就已经实现了,以后我还是得改掉超前讲的坏毛病,不多废话,我们知道数组名就是数组第一个元素的地址,所以我们在使用输入函数的时候便可以用数组名来替代&arr[i]类似这样的写法,这样可以减少我们在使用scanf函数的时候忘记运用取地址符的尴尬情况(小编就时常忘记写这个,我还抱怨是编译器的问题,😅),在打印的时候我们就可以利用解引用操作符来实现操作,光说不练等于白说,下面来进行一个久违的环节,小小的例题

例1.利用指针访问数组随机打印五个数


#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		scanf("%d", arr + i);
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(arr + i));  //在这里arr[i] == *(arr + i),甚至还可以写成[i]arr,我们在这里可以看出[]仅仅就是一个运算符罢了,不过为了视觉效果所以才这么用的
	}
	return 0;
}

  可以看出这个功效和我们正常使用一位数组是一样的,不过正如我前面所说的,这样写可以避免一些错误的出现,这也让我们之后写数组的方式变得更多了 ,一定要合理运用(scanf函数会时时常忘记写的读者朋友们可以这么写呦~)。

3.一维数组传参的本质

  数组我们是知道的,函数我们也是知道的,但是我们之前在利用函数传数组的参数的时候,我们也是传参的数组名,当时我们并没有考虑到数组名到底是干什么的,也不知道数组名就是数组第一个元素的地址,现在根据我们前面讲过的知识,下面小编出一个小小的题目,我们可以不可以通过函数来计算数组元素的个数呢?读者朋友们可以先思考一下,下面是相关代码:

#include<stdio.h>
void ceshi(int arr[10])
{
	printf("%zd", sizeof(arr));
}
int main()
{
	int arr[10] = { 0 };
	ceshi(arr);
	return 0;
}

   可以看出打印出来的结果是8(我这是在X64环境下写的),不难看出,此时打印的数组元素的地址的大小,并没有打印出整个元素的字节长度,原因很简单,我们知道arr是数组名,代表的是第一个元素的地址,所以我们传过去的是地址,计算长度的时候自然就是计算地址的长度了!所以我们每次想要传数组的时候,需要把数组的个数也传递过去。对于数组传参,其实是有两种形式的,下面来展示一下:

  

#include<stdio.h>
void ceshi1(int arr[10])
{
	//我就不写内容了,看外观别看内容
}
void ceshi2(int* arr)
{
	//这两个是一样的,小编比较喜欢写第一个的形式(说实在的这个形式在我复习的时候我才记着有)
}
int main()
{
	int arr[10] = { 0 };
	ceshi1(arr);
	ceshi2(arr);
	return 0;
}

  正如我在代码页所写的,两种形式均可,可以按着自己习惯来进行书写,第二个可以更好的展示为什么字节数是4 / 8个,因为这本来就是计算地址的长度!

小结:

  对于这部分的知识点,大家一定要好好掌握(客套话),下面进入二级指针(冒泡排序我觉着单独出一篇来写去,感觉写在中间有点显的它不重要) 。

4.冒牌排序算法的实现(下一篇见喽)

5.二级指针相关知识点

5.1.二级指针是什么

  我们知道,指针变量也是变量,同样也是有地址的,对于普通变量地址的存放是在指针里面,那么指针变量的地址应该存放到哪里呢,显然易见,它会放到二级指针里面,所以用大白话来讲,二级指针就是指针的指针,对于它如何进行初始化,下面献上代码:​​​​​​​

#include<stdio.h>
int main()
{
	int a = 20;
	int* p = &a;
	int** m = &p;   //可以这么记,有多少个*就代表着几级指针在初始话的时候
	return 0;
}

  正如我上问所说,经过我的观察,我发现*数决定着几级指针,其实二级指针目前我也没有设计很多,大多数题目设计到的都是一级指针,二级指针先知道大概就行,小编觉着这个在后期(数据结构,我还没学不知道大概)使用的应该更广一点。 

5.2.二级指针的解引用操作

  我们知道,在一级指针的时候进行解引用操作的时候,会直接取到这个数的本身,所以我们类比二级指针,在二级指针进行解引用的时候,会直接对应到一级指针的内容,也就是一级指针对应元素的地址,详情的代码如下:

#include<stdio.h>
int main()
{
	int a = 20;
	int* p = &a;
	int** m = &p;
	printf("%p\n", p);
	printf("%p", *m);
	return 0;
}

 

  从上面可以很显然的看出二者是相同的,这更加的证实了我上面所说,对二级指针进行第一次解引用的时候,得出的是一级指针的详情内容,划重点(第一次),所以有一必有二,二级指针也可以进行二次解引用,试想一下,第一次解引用的时候取到的是一级指针,对二级指针进行解引用的时候就是对一级指针再次进行解引用,得到了数的本身,很有趣吧,知识都是环环相扣的,下面是代码展示:

#include<stdio.h>
int main()
{
	int a = 20;
	int* p = &a;
	int** m = &p;
	**m = 12;
	printf("%d\n", a);
	printf("%d\n", **m);
	return 0;
}

 

  上面的代码很好的证实了我刚才说的正确性,所以二级指针是可以进行二次解引用的,当然,除了二级指针,还有三级指针,四级指针等等 ,不过这些都不经常使用,所以,指针记住一级指针=和二级指针就好,我们记住对我们有用的,记太多是容易遗忘的。

小结:

  二级指针的知识已经全部都讲完了,希望读者朋友们好好的理解,下面进入最后一小节喽!

6.指针数组相关知识点 

6.1指针数组

  在讲到这个之前,记住这是指针数组,一定要跟后期的数组指针区分开, 这俩真容易弄混。好了,进入正题,指针数组这个名字,可以类比记忆,类似整型数组,浮点型数组,顾名思义,这是个存放指针的数组,数组中的每一个元素都是指针(地址),下面来介绍一下它是如何进行初始化的:

int * arr[5] = {0};

  int代表的是类型,这一个不唯一,我只是当作例子展示一下,也可以是float,double等等,* 代表的是指针类型,arr是指的名字,arr[5]代表着存了五个地址,一定要记住它的写法,后期有很多指针类型等着我们记,我们一定不要搞混,这便是指针数组的初始化,下面可以进入一个小小的案例:用指针数组来模拟实现二维数组(小重点)! 

 

6.2.利用指针数组模拟二维数组 

  二维数组我们都知道(这部分我没有出博客,因为我有点懒),我们可以通过我们刚刚了解到的指针数组来进行模拟实现二维数组,下面先呈上代码供各位观看(代码下面是讲解

#include<stdio.h>
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 1,2,3,4,5 };
	int arr3[5] = { 1,2,3,4,5 };
	int* arr[] = { arr1,arr2,arr3 };   //存放的都是数组第一个元素的地址,存放地址的叫做指针数组
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", *(*(arr + i) + j));    //也可以写成arr[i][j]
			//上面这个是挨着访问的,具体理解我写到博客里面,在这里我不好展开
		}
		printf("\n");
	}
	return 0;
}

 

  下面我们来对上面的代码进行更详细的解释 ,首先我们先要设置三个数组,里面都放着想对应的元素,是为了后续二维数组更加的完善,我们先把每个数组的首地址放到指针数组里面,为了可以把每个元素都打印出来,我们设置了两层循环,外层循环三次的原因是因为先选好每一个数组是什么,就比如i是0的时候,这时候开始访问第一个数组的地址,我们之后在进行循环就可以打印出第一个数组的内容了,这里的知识点涉及到了上面小编写过的《使用指针访问数组》就写到了如何用指针来写出数组,之后每次i++就会打印每一行数组的内容了,然后我们便模拟出了二维数组,小编画了两张抽象图片供读者理解:

 

  这个图虽然有点抽象,但我也画出了大概的雏形,就比如右图代表着设置了三个数组,左边代表着指针数组里面包含着三个数组首元素的地址。

小结:

  数组指针是指针中较为重要的知识点,读者朋友们一定要好好的领悟这个,不然后期真的很容易和数组指针弄混,小编建议大家可以看完文章后敲一下我在这篇文章写过的代码一定会有收获的!

总结:

  可算是写完这篇文章了,这篇文章可以看作是指针的进阶篇,指针从这便开始上强度了,大家一定要好好的学会指针,学会指针C语言便会打下半壁江山,加油,以后大厂的程序员们!如果文章出现了差错,欢迎从评论区指出,希望各位可以多点点赞,您的点赞便是我进步的动力,我们下一篇见啦!

 

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

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

相关文章

JavaScript之内置对象

内置对象 JavaScript中的对象分为3种&#xff1a;自定义对象、内置对象、浏览器对象前面两种对象是javascript基础内容&#xff0c;属于ECMAScript&#xff1b;第三个浏览器对象属于我们javascript独有的&#xff0c;我们javascript API讲解 内置对象就是指javascript语言自带…

18.菜单logo制作

菜单logo制作 1. 在项目assets里面加入logo 2. layout下新建MenuLogo.vue组件 MenuLofo.vue <template><div class"logo"><img :src"MenuLogo" alt""><span class"logo-title">{{ title }}</span>…

docker怎么拉取全部镜像,打包所有镜像

因为docker&#xff0c;所以我把电脑上之前的镜像全部打包出来了 你们也可以打包&#xff0c;我提供一个脚本&#xff0c;你运行即可 export_docker.sh #!/bin/bash# 导出目录 EXPORT_DIR"docker_images_backup" mkdir -p "$EXPORT_DIR"# 获取所有镜像 …

Spring Boot集成websocket实现webrtc功能

1.什么是webrtc&#xff1f; WebRTC 是 Web 实时通信&#xff08;Real-Time Communication&#xff09;的缩写&#xff0c;它既是 API 也是协议。WebRTC 协议是两个 WebRTC Agent 协商双向安全实时通信的一组规则。开发人员可以通过 WebRTC API 使用 WebRTC 协议。目前 WebRTC…

MD8002D 3W单声道AB类音频功放芯片IC

芯片功能说明 MD8002D是一颗带关断模式的音频功放IC。在5V输入电压下工作时&#xff0c;负载(4Q)上的平均功率 为3W&#xff0c;且失真度不超过10%。而对于手提设备而言&#xff0c;当VDD作用于关断端时&#xff0c;MD8002D将会进入关断模式&#xff0c;此时的功耗极低…

智慧校园的构建要素是什么

在当今数字时代&#xff0c;智慧校园的构建已成为教育界的热门话题。随着技术的不断进步&#xff0c;学校不再只是传统的教学场所&#xff0c;而是一个充满智能化和创新的环境。那么&#xff0c;智慧校园的构建要素究竟是什么呢&#xff1f; 基础设施建设 高速、稳定的网络覆…

智慧校园的作用是什么?

在近几年&#xff0c;智慧校园以其独有的姿态&#xff0c;悄然改变着教育的面貌。想象一下&#xff0c;当物联网、大数据、人工智能这些前沿技术与传统校园深度融合&#xff0c;教育空间不再局限于实体教室&#xff0c;知识获取也不再受制于时间与地点&#xff0c;一个更加开放…

10份精选竞品分析报告,一文了解10个竞品

小伙伴们&#xff0c;继上周需求文档大放送之后&#xff0c;本周小编又精心整理了十份竞品分析文档给大家&#xff0c;十个竞品包括&#xff1a; ↘ ACFUN ↘ Keep ↘ POP IM ↘ Soul APP ↘ 网易云音乐 ↘ 微信读书 ↘ 悟空问…

八个精品ETL工具,总有一款适合您的业务需求!

在数字经济高速发展的今天&#xff0c;数据的价值愈发凸显。ETL&#xff08;Extract, Transform, Load&#xff09;工具作为数据集成的关键一环&#xff0c;不仅帮助企业高效管理海量数据&#xff0c;还能为商业决策提供实时洞察。本文将深入探讨目前市场上的8款领先ETL工具&am…

Export S parameter sweep data 导出 S 参数扫描代码

Export S parameter sweep data 导出 S 参数扫描代码 正文正文 相信有不少小伙伴们会比较苦恼一件事情,就是 Lumerical Script 中的绘图并不智能。功能较为简陋以至于图像展现时不够美观,因此,很多时候我们需要将仿真数据导出使用。那么如何导出仿真数据呢?在 Lumerical S…

基于WPF技术的换热站智能监控系统17--项目总结

1、项目颜值&#xff0c;你打几分&#xff1f; 基于WPF技术的换热站智能监控系统01--项目创建-CSDN博客 基于WPF技术的换热站智能监控系统02--标题栏实现-CSDN博客 基于WPF技术的换热站智能监控系统03--实现左侧加载动画_wpf控制系统-CSDN博客 基于WPF技术的换热站智能监…

深度学习 --- stanford cs231学习笔记四(神经网络的几大重要组成部分)

训练神经网络1 1&#xff0c;激活函数&#xff08;activation functions&#xff09; 激活函数是神经网络之于线性分类器的最大进步&#xff0c;最大贡献&#xff0c;即&#xff0c;引入了非线性。 1&#xff0c;1 Sigmoid sigmoid函数的性质&#xff1a; 结合指数函数的图像可…

路由策略和策略路由实践

文章目录 路由策略&策略路由实验一、实验概述&#xff08;1&#xff09;、实验要求 二、实验实施&#xff08;1&#xff09;、路由器配置-AR1接口IP地址OSPF配置策略路由路由策略 &#xff08;2&#xff09;、路由器配置-AR2接口IP地址OSPF配置 &#xff08;3&#xff09;、…

如何快速提高自己的论文写作水平?

以下是一些可以帮助快速提高论文写作水平的方法&#xff1a; 广泛阅读&#xff1a; - 阅读大量优秀的论文&#xff0c;包括本领域权威期刊的文章&#xff0c;学习其结构、思路、论证方法和语言表达。 - 同时阅读相关的专业书籍&#xff0c;拓宽知识储备。 确定清晰的结构&am…

国际期货投机交易的常见操作方法:

一、在开仓阶段&#xff0c;入市时机的选择&#xff1a; &#xff08;1&#xff09;通过基本分析法&#xff0c;判断市场处于牛市还是熊市 开仓阶段&#xff0c;入市时机的选择&#xff1a;当需求增加、供给减少&#xff0c;此时价格上升&#xff0c;买入期货合约&#xff1b…

【JS】上传文件显示文件的为空,显示的文件参数内容只有uid

上传的文件参数file里面只包含uid&#xff0c;没有其他信息 例子解决办法 例子 例如使用elment ui的el-upload组件上传文件&#xff0c;会导致上传的文件参数file里面只包含uid&#xff0c;没有其他信息&#xff0c;如图&#xff1a; 正确应为如下图&#xff1a; 解决办法 …

XX市政府数据交换共享平台—技术方案(812页WORD)

方案介绍&#xff1a; 该方案紧紧围绕建设数据强市的总目标&#xff0c;坚持“先行先试、鼓励创新、宽容失败&#xff0c;政府引导、市场主导&#xff0c;加强统筹、优化布局&#xff0c;注重安全&#xff0c;有序推进”的原则&#xff0c;通过三大体系、七大平台、十大工程的…

19.面包屑导航制作

面包屑导航制作 官网&#xff1a;组件 | Element 1. 在layout下新建BreadCrumb.vue BreadCrumb.vue <template><div class"bread-text"><el-breadcrumb class"bred"separator"/"><el-breadcrumb-item v-for"item in…

ARDUINO NRF24L01

连线 5v 3.3皆可 gnd Optimized high speed nRF24L01 driver class documentation: Optimized High Speed Driver for nRF24L01() 2.4GHz Wireless Transceiver 同时下载同一个程序 案例默认引脚ce ces &#xff0c;7&#xff0c;8 可以 修改为 9,10 安装库 第一个示例 两…

《征服数据结构》双端栈

摘要&#xff1a; 1&#xff0c;双端栈的介绍 2&#xff0c;双端栈的代码实现 1&#xff0c;双端栈的介绍 双端栈主要利用了“栈底位置不变&#xff0c;栈顶位置动态变化” 的特点&#xff0c;可以让两个栈共享一块存储空间。在前面我们讲到用数组实现栈的时候&#xff0c;如果…