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");elseprintf("str1 and str2 are not same\n");if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("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.mzph.cn/news/240776.shtml

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

相关文章

Linux的/proc/self/学习

文章目录 /proc目录/proc/self的使用 在做SSTI模板注入的CTF题中&#xff0c;发现有师傅提到可以用/proc/self这个目录获取flag&#xff0c;所以也来学习一波主要参考. (我才知道&#x1f601;&#x1f601;&#x1f601;)可以通过/proc/$pid/来获取指定进程的信息&#xff0c…

Kioptrix-3

靶场下载地址 https://download.vulnhub.com/kioptrix/KVM3.rar 信息收集 # Nmap 7.94 scan initiated Thu Dec 21 21:52:25 2023 as: nmap -sn -oN live.nmap 192.168.1.0/24 Nmap scan report for 192.168.1.1 (192.168.1.1) Host is up (0.00048s latency). MAC Address:…

AI绘画中VAE压缩图像

介绍 在Stable Diffusion中,所有的去噪和加噪过程并非在图像空间直接进行,而是通过VAE模块将图像编码到一个低维空间。 这个低维空间的“分辨率”低于原始图像空间,有利于快速地完成加噪和去噪过程。 最后再将编码空间中的噪声表示解码恢复为图像空间,完成去噪或加噪操作。 …

【Element】el-table 使用 el-table-infinite-scroll 插件实现滚动加载

虽然 el 官方提供了 Infinite Scroll 无限滚动 组件 但是却不支持 el-table 组件&#xff0c;这就很难受了&#xff0c;还好已经有大佬写好了插件&#xff0c;并且支持 element-plus/infinite-scroll 组件的所有选项。 el-table-infinite-scroll el-table-infinite-scroll 看…

【数据结构入门精讲 | 第十二篇】考研408、公司面试树专项练习(一)

在上一篇文章中我们介绍了树的知识点&#xff0c;在这一篇中我们将进行树的专项练习。 目录 判断题选择题填空题二叉树的宽度R6-1 是否二叉搜索树 方法介绍&#xff1a; 已知中序及后序&#xff0c;求前序 如后序为DABEC,中序为DEBAC&#xff0c;求前序 则后序倒着写&#xff…

北京Modbus转Profinet网关的作用

背景&#xff1a;随着工业自动化的快速发展&#xff0c;各种仪器设备迅速崛起&#xff0c;但是在仪器出厂前需要很多的零部件来构建出需要的设备及功能&#xff0c;由于自动化设备的零部件不是统一生产商供应的&#xff0c;这样很容易出现某个零部件的通讯协议不匹配&#xff0…

搜索二叉树(超详解)

文章目录 前言查找搜索二叉树的结构insertfinderase递归版本Findinserterase 二叉树的拷贝问题搜索二叉树的应用Key模型Key/Value的模型 前言 普通二叉树其实意义不大&#xff0c; 如果用二叉树存储数据的话&#xff0c;还不如顺序表&#xff0c;链表这些。 搜索二叉树它的意义…

7.5组合总和②(LC40-M)

算法&#xff1a; 相比于上一题&#xff0c;数组candidates有重复元素&#xff0c;而要求不能有重复的组合&#xff0c;所以相对于39.组合总和 (opens new window)难度提升了不少。 如何去重&#xff1f; 先把candidates排序&#xff0c;让重复的元素都在一起 单层递归时&a…

MyBatis的关联查询!!!(一对一、一对多、多对多)

准备工作&#xff1a; 1.创建Maven工程&#xff0c;还没有配置Maven的和还不会的去看这里啦&#xff1a;maven的下载安装与配置环境变量&#xff01;&#xff01;&#xff01;&#xff08;全网最详细&#xff09;-CSDN博客 Account.java : (pojo类) &#xff08;这里我…

认识Linux背景

1.发展史 Linux从哪里来&#xff1f;它是怎么发展的&#xff1f;在这里简要介绍Linux的发展史 要说Linux&#xff0c;还得从UNIX说起 UNIX发展的历史 1968年&#xff0c;一些来自通用电器公司、贝尔实验室和麻省理工学院的研究人员开发了一个名叫Multics的特殊操作系统。Mu…

分布式锁常见问题及其解决方案

一、为什么要使用分布式锁&#xff1f; 因为在集群下&#xff0c;相当于多个JVM&#xff0c;就相当于多个锁&#xff0c;集群之间锁是没有关联的&#xff0c;会照成锁失效从而导致线程安全问题 分布式锁可以分别通过MySQL、Redis、Zookeeper来进行实现 二、redis分布式锁的实…

华为发布全闪备份一体机旗舰新品,并宣布备份软件开源

[中国&#xff0c;上海&#xff0c;2023年12月20日]在20日举行的OceanProtect数据保护新品发布会上&#xff0c;华为发布全闪备份一体机旗舰新品&#xff0c;并宣布备份软件开源&#xff0c;以应对智慧金融、自动驾驶等场景对数据备份效率及数据安全方面的新诉求&#xff0c;为…

工业信息采集平台的五大核心优势

关键字&#xff1a;工业信息采集平台,蓝鹏数据采集系统,蓝鹏测控系统, 生产管控系统, 生产数据处理平台,MES系统数据采集, 蓝鹏数据采集平台通过实现和构成其他工业数据信息平台的一级设备进行通讯&#xff0c;从而完成平台之间的无缝对接。这里我们采用的最多的方式是和PLC进行…

神经网络:深度学习基础

1.反向传播算法&#xff08;BP&#xff09;的概念及简单推导 反向传播&#xff08;Backpropagation&#xff0c;BP&#xff09;算法是一种与最优化方法&#xff08;如梯度下降法&#xff09;结合使用的&#xff0c;用来训练人工神经网络的常见算法。BP算法对网络中所有权重计算…

Redis取最近10条记录

有时候我们有这样的需求&#xff0c;就是取最近10条数据展示&#xff0c;这些数据不需要存数据库&#xff0c;只用于暂时最近的10条&#xff0c;就没必要在用到Mysql类似的数据库&#xff0c;只需要用redis即可&#xff0c;这样既方便也快&#xff01; 具体取最近10条的方法&a…

Go 代码检查工具 golangci-lint

一、介绍 golangci-lint 是一个代码检查工具的集合&#xff0c;聚集了多种 Go 代码检查工具&#xff0c;如 golint、go vet 等。 优点&#xff1a; 运行速度快可以集成到 vscode、goland 等开发工具中包含了非常多种代码检查器可以集成到 CI 中这是包含的代码检查器列表&…

DBA-MySql面试问题及答案-上

文章目录 1.什么是数据库?2.如何查看某个操作的语法?3.MySql的存储引擎有哪些?4.常用的2种存储引擎&#xff1f;6.可以针对表设置引擎吗&#xff1f;如何设置&#xff1f;6.选择合适的存储引擎&#xff1f;7.选择合适的数据类型8.char & varchar9.Mysql字符集10.如何选择…

第九周算法题(哈希映射,二分,Floyd算法 (含详细讲解) )

第九周算法题 第一题 题目来源&#xff1a;33. 搜索旋转排序数组 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a;整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 <…

全网最全ChatGPT指令大全prompt

全网最全的ChatGPT大全提示词&#xff0c;大家可以进行下载。 AIGC ChatGPT 职场案例 AI 绘画 与 短视频制作 PowerBI 商业智能 68集 数据库Mysql 8.0 54集 数据库Oracle 21C 142集 Office 2021实战应用 Python 数据分析实战&#xff0c; ETL Informatica 数据仓库案例实战 E…

【JAVA面试题】什么是引用传递?什么是值传递?

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 前言 博客的正文部分可以详细介绍Java中参数传递的机制&#xff0c;强调Java是按值传递的&#xff0c;并解释了基本数据类型和对象引用在这种传…