C语言易错知识点九(指针(part three))

❀❀❀ 文章由@不准备秃的大伟原创 ❀❀❀

♪♪♪ 若有转载,请联系博主哦~ ♪♪♪

❤❤❤ 致力学好编程的宝藏博主,代码兴国!❤❤❤

   许久不见,甚是想念,本大忙人已经很久没有更新博客了,我想大概我的粉丝们早已按耐不住了吧(假)!好的啊,今天的话继续我们仍未结束的指针。

        在上一篇有关指针的文章中我们谈论了有关野指针assert断言以及与指针有关的计算。还没看过的小伙伴们可以戳戳这里传送哦~  ----->  指针 (Part two) 

        那么今天的话给大伙们介绍一下几种指针类型:字符指针,指针数组,数组指针和函数指针(当然最基础的指针变量大家都会的吧~)     什么,不会?那不赶紧戳一戳这里------>   指针 (Part one)

        嗯哼,好的,接下来开始今天的正题:

                  一.字符指针

        我们既然有了整形指针,那么没有字符指针是不是说不过去?整形指针是int*,那么字符指针不就是char*吗?

        好的,字符指针结束了。

         ...........................................................

        大家都知道我的尿性吧?我说结束了不是结束,而屏幕前的你说结束才是结束,所以,实不相瞒,想要个赞(✿◡‿◡)

        这时候问题就出来了,我们用字符指针存个 'a' 'C' 都是没有什么问题的吧,但 ? 字符串呢?存放的是整个字符串吗?还是只存首元素?还是存放的是首元素的地址?

        如下面一段代码:

int main()
{
 const char* pstr = "hello world!";//pstr里面到底存的是什么
 printf("%s\n", pstr);
 return 0;
}

        在我们运行过后发现,打印的是整个hello world! ,那么我们的pstr存放的就是整个字符串。

—————— 对吗?

        仔细考虑的同学就会发现疑点,“ 既然我用char str[]就可以存放一个字符串,那么char* 还有必要存在吗? ” 是的,我们这么仔细一想就发现,char*存放的才不是元素呢?是地址!是这个字符串的首元素的地址!但这时又有同学有疑惑了“既然存的是地址,那为什么还能打印出整个字符串呢?” 诶,好问题,这就要联系到我们的内存问题了,这部分博主以后会在博客里谈到,这里就简单给大家解释一下:

      我们计算机中任何数据的存储都会占用一定的空间,而每一部分的空间都有自己的独自的地址,我们可以通过地址来寻找到这个空间存放的值。这和数组传参本质是一个道理,我们同样可以通过一个数组的首元素地址来找到数组后面的内容。

        有意思吧?接下来看一个更有意思的代码:

#include <stdio.h>
int main()
{
 char str1[] = "hello bit.";
 char str2[] = "hello bit.";
 const char *str3 = "hello bit.";
 const char *str4 = "hello bit.";
 if(str1 ==str2)
 printf("str1 and str2 are same\n");
 else
 printf("str1 and str2 are not same\n");

 if(str3 ==str4)
 printf("str3 and str4 are same\n");
 else
 printf("str3 and str4 are not same\n");

 return 0;
}

         怎么样,汗流浃背了?这其实没什么,记住我们数组传参的本质是地址,所以我们两个if比较的就是地址。所以答案是....?

c315d0e3dfad47dda397f39dd7efa2ae.png

        对,答案是 not same和same。博主上面也讲到了吧?模拟实现二维数那里。我们创建的数组空间是不连续的,所以str1[] 和 str2[] 指向的是两块不同的空间,虽然他们的内容一样,但是我们if比较的是地址啊?所以是not same;而我们给字符指针str3和字符指针str4定义的是同一个内容(常量字符串), 而在C/C++中会把常量放到一块独立的空间里(常量区),这也就说明,我们的str3和str4指向的是同一块空间,所以是same。

                  二.指针数组 

        诶,在开始前我先问大家一个问题,指针数组是指针还是数组?<(* ̄▽ ̄*)/  我们其实可以类比一下:整形指针是存放整形的指针,字符指针是存放字符的指针,那么指针数组是存放指针的数组。即:指针数组的每个元素都是⽤来存放地址(指针)的。 

        那我们一般定义一个数组是怎么定义的?如存放整形的数组:int arr[10] = {0},每个元素存放的是整形,对吧? 那么同样的,存放指针的数组我们一般就这么定义:int *arr[10] = {0}; 每一个元素存放的是int*,即指针(地址)。

        诶,但是这个时候我想到了一个很好玩的东西,既然我们可以定义个一维的指针数组,那可不可以用指针数组模拟实现个二维数组呢?

        答案是可以的(不然博主也不会提出这个问题了对吧?)

        博主有个思路:先创建普通的一维数组,在通过指针数组将一维数组放进去,即通过寻找每一个一维数组的首元素地址再往后找各个元素。代码实现如下:

#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*的,就可以存放在parr数组中
	int* parr[3] = { arr1, arr2, arr3 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

答案如下: 

a7f6d01b2e6e4f718b90c34fef73fcb9.png

        是不是很容易理解?我们这样就通过指针数组模拟实现了一个二位数组,但是!但是!但是!上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的(每一个一维数组向内存申请的空间是不连续的)。 

        指针数组的知识大概就到这里了,怎么样,是不是没有觉得很难?

                   二.数组指针

         那博主把铁汁们当一次傻子,数组指针内存放的是什么? 啊对对对,是指针,看来铁汁们有好好看上面的内容,博主很是欣慰。o(^▽^)o

        那        int *p1[10];         int (*p2)[10];         这两个哪个是数组指针?“不就是第二个吗?第一个不是指针数组?” 嗯嗯嗯,好好好,看到铁汁们这么快反应过来真好,哈哈。是的,我们将指针数组的变量名与*括起来就是表达的数组指针。

        原理呢?不懂得可以听博主解释一下哦~:

解释:p和*结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。所以 p是⼀个指针,指向⼀个数组,叫 数组指针。

        指针数组可以存放多个指针,那么数组指针有什么用呢?既然存在了,怎么会没有用呢?就如同现在的你我可能还没有发觉自己的价值,但是如果你我是无价值的,上帝怎么可能会将我们放到人间呢?对吧,所以大家要对自己有信心!加油!

        呃哼,扯远了,我们继续:如果要存放个数组的地址,就得存放在数组指针变量中,如下: 

int(*p)[10] = &arr;

我们调试也能看到 &arr 和 p 的类型是完全⼀致的: 

7fb891e56a3741b8b39ef3f08d429b1d.png

         既然把数组指针学了,接下来二维数组的传参本质:

先给结论:arr[i] = *(arr+i)     arr[i][j] = *(*(arr+i)+j)

        那我们以前是怎么给函数传二维数组的呢?

void test(int a[3][5], int i, int j);//函数申明
test(arr, 3, 5);//main函数内部的传址和传值

        ⾸先我们再次理解⼀下⼆维数组,⼆维数组起始可以看做是每个元素是⼀维数组的数组,也就是⼆维 数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。 

        而现在我们学了数组指针,我们就有新的玩法了: 

     根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀ 维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类 型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀ ⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。

        所以我们可以这么写:

void test(int (*p)[5], int r, int c);//函数申明
test(arr, 3, 5);//main函数内部的传址和传值

         总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。

                  三.函数指针

        之前博主说过,任何数据都是有地址的,那,函数也包括在这个 ‘数据’ 里面吗?我们可以做个测试:

#include <stdio.h>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("test: %p\n", test);
 printf("&test: %p\n", &test);
 return 0;
}

 cee8283f33d448e5ab1e43345a31c7f7.png

        我们可以看到确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的⽅式获得函数的地址。 

         如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数指针变量的写法其实和数组指针 ⾮常类似。如下:

void test()
{
 printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)()= test;
int Add(int x, int y)
{
 return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;

        给大家提供两段很有意思的代码,大家私下可以仔细思考思考:

(*(void (*)())0)();
 void (*signal(int , void(*)(int)))(int);

        OK,那么今天的任务也就到此为止了,下一篇part four是指针的最后一篇了,希望大家继续多多支持大伟,加油! 

        Life if full of a lot of pain,the truly great personality is not standing on high,standing in the flowers and applause.The truly great personality is at the bottom of life,in this training,you can still cheer up,still have courage,still have hope,and still will not be casually crushed by disaster.   ————黄色安全帽(抖音)

   本篇博客也就到此为止了,送大家一碗鸡汤,勉励自己以及这世界上所有追逐梦想的赤子趁年华尚好努力提升自己,莫欺少年穷! e2f7b450148f4bf5b53f823b95fd5406.jpeg

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

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

相关文章

数据库原理及应用·关系数据库标准语言SQL

4.1 SQL概述 4.1.1 SQL的产生和发展 1.产生 1974年&#xff0c;SQL语言的雏形最早由美国IBM公司的Raymond F. Boyce和Donald D. Chamberlin提出 1975-1979年&#xff0c;在System R上首次实现&#xff0c;由IBM的San Jose研究室研制&#xff0c;称为SEQUEL 2.发展 1986年推…

文件夹共享功能的配置 以及Windows server2012防火墙的配置

目录 一. 配置文件夹共享功能 1.1 为什么需要配置文件夹共享功能 1.2 配置文件夹共享 1.3 访问共享文件夹 1.4 配置取消 用户名和密码认证 二. windows server 2012防火墙配置 思维导图 一. 配置文件夹共享功能 1.1 为什么需要配置文件夹共享功能 我们在工作和生活中经…

RM3100 stm32驱动(硬件i2c)

目录 RM3100接线HAL库I2C函数HAL_I2C_Mem_ReadHAL_I2C_Mem_WriteHAL_I2C_Master_Transmit / HAL_I2C_Master_Receive例子 HSHAKE寄存器 cubemx配置RM3100寄存器驱动最终效果 RM3100接线 原理图 SA0 SA1接地&#xff0c;此时i2c设备地址为0100000&#xff0c;即0x20 如果SA0接…

Scikit-Learn线性回归(一)

Scikit-Learn线性回归一 1、线性回归概述1.1、回归1.2、线性1.3、线性回归1.4、线性回归的优缺点1.5、线性回归与逻辑回归2、线性回归的原理2.1、线性回归的定义与原理2.2、线性回归的损失函数3、Scikit-Learn线性回归3.1、Scikit-Learn库3.2、Scikit-Learn线性回归API3.3、Sci…

Spring-2-配置和Springboot

bean的生命周期 挂钩到bean的创建 通过了解初始化的时间&#xff0c;bean可以检查是否满足其所需的所有依赖项。 尽管Spring可以帮助我们检查依赖项&#xff0c;但它几乎是一种全有或全无的方法&#xff0c;并且不会提供任何机会来将其他逻辑应用于依赖项的解析过程中。 假设…

JavaWeb的Servlet的入门和使用方法

1 什么是Servlet Servlet是Server Applet的简称&#xff0c;是用Java编写的是运行在 Web 服务器上的程序&#xff0c;它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet&#xff0c;可以收集来自网页表单的用户输…

猴子目标检测数据集VOC格式1300张

猴子是一种充满活力和灵巧的灵长类动物&#xff0c;分布广泛&#xff0c;生活在世界各地的热带雨林、草原、沼泽和山区。猴子通常以水果、坚果、种子、昆虫和植物叶子为食&#xff0c;它们拥有灵敏的双手和手指&#xff0c;可以灵活地采摘食物。猴子的智商相当高&#xff0c;有…

杜邦6150up同级抛光树脂:生产超纯水的关键

超纯水的制备不仅对单晶硅电池的制造至关重要&#xff0c;而且对整个清洁能源领域的发展具有深远影响。随着全球对可再生能源的需求不断增长&#xff0c;提高太阳能电池的效率和降低成本已成为当务之急。超纯水的使用直接关系到电池的性能和寿命&#xff0c;因此&#xff0c;掌…

家校互通小程序实战开发02首页搭建

目录 1 创建应用2 搭建首页总结 我们上一篇介绍了家校互通小程序的需求&#xff0c;创建了对应的数据源。有了这个基础的分析之后&#xff0c;我们就可以进入到开发阶段了。开发小程序&#xff0c;先需要创建应用。 1 创建应用 登录控制台&#xff0c;点击创建应用&#xff0c…

【译文】IEEE白皮书 6G 太赫兹技术的基本原理 2023版

第一章 简介 太赫兹波是介于微波和光波之间的光谱区域&#xff0c;频率从 0.1THz ~ 10THz 之间&#xff0c;波长在 3mm ~ 30μm 之间。提供大块连续的频带范围以满足对 Tbit/s 内极高数据传输速率的需求&#xff0c;使该区域成为下一代无线通信&#xff08;6G&#xff09;的重…

ARCGIS PRO SDK GeometryEngine处理独立几何图形的函数

1、面积类&#xff1a;pol为Polygon 1).Area&#xff1a;获取几何图形的面积。这是使用二维笛卡尔数学来计算面积的平面测量 double d GeometryEngine.Instance.Area(pol) 2).GeodesicArea:获取几何图形的椭球面积 …

FPGA-AMBA协议、APB协议、AHB规范、AXI4协议规范概述及它们之间的关系

FPGA-AMBA协议、APB协议、AHB协议、AXI&#xff14;协议规范概述 笔记记录&#xff0c;AMBA协议、APB协议、AHB规范、AXI&#xff14;协议规范概述&#xff0c;只是概述描述&#xff0c;具体详细的协议地址传输、数据传输等内容将在下一章节详细说明。 文章目录 FPGA-AMBA协议…

爬虫系列----Python解析Json网页并保存到本地csv

Python解析JSON 1 知识小课堂1.1 爬虫1.2 JSON1.3 Python1.4 前言技术1.4.1 range1.4.2 random1.4.3 time.sleep1.4.4 with open() as f: 2 解析过程2.1 简介2.2 打开调试工具2.3 分析网址2.3.1 网址的规律2.3.2 网址的参数 2.4 爬取第一页内容2.5 存入字典并获取2.6 循环主体数…

【LeetCode刷题笔记(13-1)】【Python】【回文数】【反转整数】【简单】

文章目录 引言回文数题目描述提示 题意分析解决方案1&#xff1a;【反转字符串】解决方案2&#xff1a;【反转整数】题外话结束语 9. 回文数 引言 编写通过所有测试案例的代码并不简单&#xff0c;通常需要深思熟虑和理性分析。虽然这些代码能够通过所有的测试案例&#xff0…

blender scripting 编写

blender scripting 编写 一、查看ui按钮对应的代码二、查看或修改对象名称三、案例&#xff1a;渲染多张图片并导出对应的相机参数 一、查看ui按钮对应的代码 二、查看或修改对象名称 三、案例&#xff1a;渲染多张图片并导出对应的相机参数 注&#xff1a;通过ui交互都设置好…

基于springboot+vue的教材管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

windows操作系统蓝屏错误日志分析日志通用方法全网唯一程序员必学技能

windows11操作系统蓝屏错误分析日志通用方法全网唯一程序员必学技能 一、背景介绍 本人是一个老程序员&#xff0c;DBA&#xff0c;架构师&#xff0c;开发工作10年了&#xff0c;我们平常用的开发电脑以及游戏电脑时不时会遇到操作系统突然蓝屏&#xff0c;一般情况下都是一…

C++力扣题目232--用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int peek() 返回队列开头…

ThingsBoard 3.4 -- 规则链使用

规则链的使用 创建规则链 调试模式用于调试日志的输出&#xff0c;测试阶段建议选择 配置规则 点击规则链&#xff0c;进入详情页面 打开规则链进行规则配置 规则链节点类型分为6类&#xff0c;每一类下面又包含多个小类 过滤器/筛选器&#xff1a;过滤器节点用于消息过滤…

springboot对接WebSocket实现消息推送

1.修改pom文件 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 2.增加配置WebSocketConfig.java import org.springframework.context.annotation.Bean…
最新文章