指针的深入解读笔记

news/2024/4/13 10:56:15/文章来源:https://blog.csdn.net/forccct/article/details/136502798

指针是什么

        指针是指向内存单元的编号(地址),可以快速访问地址,加快程序运行速度.

        在指针中一般用到两个操作符:

                * 解引用操作符 也是定义指针时候的操作符                  

int *p;//定义一个类型为 int 的 指针

*p = 0;解引用p指向的地址 并且赋值为0

                & 取地址操作符

int a;

int *p;

p = &a;//把a的地址给p

        int * p  中 *说明p是指针变量 int 说明 p 指向的类型是整型

        而且p中存储的是地址 *p是解引用p指向了 变量  也就是 p(地址) --> a(变量)

         指针本身也占有大小,而且在32位平台下是4字节 64位平台下是8字节 而且指针大小与它所指向的东西无关,只于它运行的平台有关

指针类型意义

        指针变量的类型虽然与大小无关,但是却决定每次或下次访问时访问的字节大小有关

                比如char*类型的指针,每次只访问一个字节

                       int*类型的指针,每次访问四个字节

        而且它也决定 与 + - 运算时访问字节的大小 也就是指针的类型决定了指针向前或者向后走一步有多大距离.

                比如char*类型的指针每次 +1 跳过1个字节

                       int*类型的指针每次 +1 跳过四个字节

        而且指针的地址是可以求差的,用差除去每个元素的长度可以得到两个地址之间的元素个数

        而且还有一种特殊指针 void* 它是无具体类型的指针(泛型指针),这种指针可以接收任何类型的地址,但是不可以与 + - 和 解引用 因为它无法确定 步长.

                一般情况下void*指针是使用在函数参数的部分,用于接收不同类型的地址,可以实现泛型编程的效果,例如

                qsort使用-CSDN博客 中 qsort模拟部分

        而且指针使用可以和 const(不可修改) 来修饰 而且 const的位置决定可改变和不可改变的位置

const int * p;//决定的是*p不能改变

int const *p;//同理

int * const p;//决定的是p指向的地址不可更改,但是地址包含的东西可以更改

        野指针是指向未知位置的指针,一般是由于没有初始化,越界访问,指向了已经被释放的空间等.我们要规避野指针的存在,可以初始化的时候赋值NULL(空地址0) ,在使用完不使用指针的时候及时赋NULL,避免返回局部变量的地址等方法.

                也可以使用assert断言 也就是assert.h的头文件包含的assert(),用于在运行时确保程序符合指定条件,如果不符合就报错并且终止运行程序

assert(p != NULL);//如果p不是空指针就报错

如果assert接收的返回值是0就回报错,不是0就继续运行,而且要关闭断言可以使用

#define NDEBUG

 数组名指针

         首先举个小例子

int arr[5] = {1,2,3,4,5};

int *pp = &arr[1];//取到了元素2的首地址 每次 + 1 会跳过一个元素

int *ppp = arr;//取到了数组第一个元素的首地址 每次 + 1会跳过一个元素

int *pppp = &arr;//取到了整个数组首元素地址 每次+1会跳过一个数组

         我们根据例子也可以倒推如何使用指针去找元素,就和+1 -1和取得是什么类型的指针有关

printf("%d",arr[1]);

printf("%d",*pp);

printf("%d",pp[0]);

printf("%d",*(pp+0);

        这四个代码的效果是一样的 其实[]可以和*一样有解引用的功能

        在本质上pp[i]和*(p+i)是等价的 同理arr[i]和*(arr+i)也是等价的  

                数组元素的访问在编译器处理的时候,也是转换成首元素的地址 + 偏移量 然后求出元素的地址,然后再解引用访问.        

一维数组的本质

        本质上素组窜惨本质上传递的是首元素(或者选择的元素)的地址,因为传过去的仅仅只是地址,所以在自定义函数里面无法直接用sizeof求出数组的大小,只能求出这个地址的大小.而且可以用指针的形式接收数组,因为是地址.

二级指针

        指针变量也是变量,所以指针本身也有地址,而且这个地址也可以由其它指针接收,也就是二级指针.

int a;

int *p = a;

int * *pp = a;

         在这种情况下 pp解引用调用p的内容(a的地址) 再对pp二次解引用才能找到a pp中存放的是p的地址 p中存放的是a的地址.

指针数组(与数组指针区分一下)

        对于普通的数组 它存放的是int类型的内容

                int a[5] == { int,int.......}

        对于指针数组 他存放的是是int*类型的内容 也就是存放的全是地址 而且每个地址由可以单独指向一个区域

                int* a[5] == { int*,int*,....}

指针数组模拟二维数组

        以下两个东西是等价的

int arr[3][3];

int arr1 [3];

int arr2 [3];

int arr3 [3];

int* arr[3] = {arr1,arr2,arr3}

        其中的arr的效果是相同的,而且根据 arr[i]和*(arr+i)是等价的 可以用相同的方式访问某个元素

                也就是 arr[1][1] 等于 *(arr[1] + 1) 等于 *(*(arr + 1) + 1)

字符指针变量

        再指针的类型中 还有 char* 的类型 ,它每次访问一个字节,可以逐字节访问

char a = 'w';

char *p = a;//那么p指向a的地址 解引用就是字符w

const char * pw ="wwww";//这个pw指向的是在内存中的字符串"wwww"的首地址

 const char * pa ="wwww";//这个pa指向的首地址和pw相同

数组指针变量

int *pa[10];这个是指针数组

int (*pb)[10]; 这个是数组指针

         int (*pb)[10] pa与*结合说明p是一个指针变量变量,然后指向的是一个大小为10个的整型的素组,p是指针,指向数组,所以是数组指针,( []的优先级高于*,所以要用()保证*先于P结合.

int arr[10] = {0};

int (*p)[10] = &arr;//这里的p就是arr的地址了 

         &arr 和 p的类型都是 int[10]*

        其中 int是p指向的数组的元素类型, p是数组指针的变量名 [10]是p指向数组的元素个数

二维数组的传参本质

        首先理解二维数组,二维数组可以看作是每个元素是一维数组的数组,这个数组中的每个元素都是一个一维数组,那么二维数组的首元素就是一个一维数组,取二维数组的数组名时,二维数组的数组名就是第一行的地址,取出来是一个一维数组.

        第一行的地址类型是 int(*)[i],那就意味着二维数组传参本质上是传递了地址,传递的是第一行这个以为新数组的地址,那么形参也可以写成指针形式

void test(int (*p)[5]);//用这个来接收有五行的二维数组arr[5][i]

函数指针变量 

        在内存中创建的自定义函数本身也是有地址的,可以通过 &函数名 的方式得到函数的地址,如果我们要把函数的地址存起来,就要创建函数指针变量,函数指针变量的写法和数组指针类似

void test()

{

return 0;

}

void (*pf1)() = &text;

void (*pf2)() = test;

int add(int x,int y)

{

return x + y;

}

int(*pf3)(int,int) = add;

int(*pf4)(int x, int y) = &add;

        对于 int(*pf3)(int,int) = add; 解析 int 是pf3指向的函数的返回类型 (*pf3)是函数指针变量名 

(int,int)是主席昂的函数的阐述类型和个数

        这个指针的类型是 int(*)(int,int)

而且用的方法和之间用函数一样的

a = (*pf3)(1,1);

a = pf3(1,1);

        这两行结果相同

typedef关键字

        typedef是重命名例如把 unsigned int 重命名成 unit

typedef unsigned int uint; 

函数指针数组

        int (*p[5])(int,int);

                 这是定义了一个 有五个元素,返回类型是int 参数是两个int 的 函数指针数组

        类型是 int(*)(int,int)

        这个结合到使用的时候也就是转移表了

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

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

相关文章

Python爬虫——Scrapy-1

目录 简介 安装 基本使用 1. 创建爬虫的项目 2. 创建爬虫文件 3. 运行爬虫代码 scrapy项目组成 scrapy工作原理 ​编辑 58同城 scrapy架构组成 汽车之家 总结 简介 Scrapy 是一个基于 Python 的开源网络爬虫框架,它可以帮助开发者快速、高效地构…

nginx代理访问Kuboard, 解决日志无法查看问题

错误方式 这种代理方式在点击追踪日志按钮, 会无法查看日志, 因为日志是通过weboscket传输 worker_processes 1; #设置 Nginx 启动的工作进程数为 1。events {worker_connections 1024; ##设置每个工作进程的最大并发连接数为 1024。 }http {include mime.types; #该…

kafka如何保证消息顺序性?

kafka架构如下: Kafka 保证消息顺序性的关键在于其分区(Partition)机制。在 Kafka 中,每个主题(Topic)可以被分割成多个分区,消息被追加到每个分区中,并且在每个分区内部&#xff0c…

【C++】C++模板基础知识篇

个人主页 : zxctscl 文章封面来自:艺术家–贤海林 如有转载请先通知 文章目录 1. 泛型编程2. 函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的原理2.4 函数模板的实例化2.5 模板参数的匹配原则 3. 类模板3.1 类模板的定义格式3.2 类模板的实例化…

3DES算法的起源与演进:保障信息安全的重要里程碑

title: 3DES算法的起源与演进:保障信息安全的重要里程碑 date: 2024/3/8 21:25:19 updated: 2024/3/8 21:25:19 tags: 3DES算法起源安全性增强三次迭代加密密钥管理复杂效率对比AES应用场景广泛Python实现示例 一、3DES算法的起源与演进 3DES算法是DES算法的增强版…

目标检测论文模型笔记——YOLO系列

1. YOLOv1的核心思想: YOLOv1:使用整张图作为输入,直接在输出层回归bounding box和类别;(one-stage)Faster RCNN:使用用整张图作为输入,但整体采用了RCNN: proposalclas…

18个惊艳的可视化大屏(第23辑):电子政务,一目了然如胸。

hello,我是贝格前端工场老司机,这是第23期了,本次带来可视化大屏在电子政务领域的应应用案例,喜欢文章的别忘点赞关注,文章底部也有其他行业的案例。 数据展示与监控: 可视化大屏可以将政务数据以图表、地…

Lwip之TCP服务端示例记录(1对多)

前言 实现多个客户端同时连接初步代码结构已经实现完成(通过轮训的方式) // // Created by shchl on 2024/3/8. // #if 1#include <string.h> #include "lwip/api.h" #include "FreeRTOS.h" #include "task.h" #include "usart.h&…

Java后端八股笔记

Java后端八股笔记 Redis八股 上两种都有可能导致脏数据 所以使用两次删除缓存的技术&#xff0c;延时是因为数据库有主从问题需要更新&#xff0c;无法达到完全的强一致性&#xff0c;只能达到控制一致性。 一般放入缓存中的数据都是读多写少的数据 业务逻辑代码&#x1f44…

机器学习——神经网络压缩

神经网络压缩 需要部署&#xff0c;设备内存和计算能力有限&#xff0c;需要进行模型压缩&#xff0c;在设备上运行的好处是低延迟&#xff0c;隐私性。 目录 不考虑硬件问题&#xff0c;只考虑通过软件算法优化。 修剪网络 参数过多或者没有用的参数&#xff0c;可以将其剪…

Node.Js编码注意事项

Node.js 中不能使用 BOM 和 DOM 的 API&#xff0c;可以使用 console 和定时器 APINode.js 中的顶级对象为 global&#xff0c;也可以用 globalThis 访问顶级对象 浏览器端js的组成 Node.js中的JavaScript组成 相比较之下发现只有console与定时器是两个API所共有的&#xff…

Linux运维:实现光盘开机自动挂载、配置本地yum源教程

Linux运维&#xff1a;实现光盘开机自动挂载、配置本地yum源教程 一、光盘开机自动挂载1、检查光驱设备2、创建挂载点3、编辑/etc/fstab文件4、测试挂载 二、配置本地yum源(挂载光盘或ISO文件)1、挂载ISO文件2、创建YUM仓库配置文件3、清理YUM缓存并测试 &#x1f496;The Begi…

【QT】创建第一个QT程序

下面的前7个可以先不看&#xff0c;直接从8开始看 1. 创建Qt程序 一个Qt程序的组成部分&#xff1a;应用程序类&#xff0c;窗口类应用程序类个数&#xff1a;有且只有一个QApplication a;如何查看类对应的模块&#xff1a;光标移动到类上&#xff0c;F1qmake模块的名字 2. …

2024 批量下载公众号文章内容/阅读数/在看数/点赞数/留言数/粉丝数导出pdf文章备份(带留言):公众号记忆承载近1500篇历史文章在线查看,找文章方便了

关于公众号文章批量下载&#xff0c;我之前写过很多文章&#xff1a; 视频更新版&#xff1a;批量下载公众号文章内容/话题/图片/封面/音频/视频&#xff0c;导出html&#xff0c;pdf&#xff0c;excel包含阅读数/点赞数/留言数 2021陶博士2006/caoz的梦呓/刘备我祖/六神读金…

分享2024年在家轻松兼职赚钱的5个副业

今天在网上看到这么一句话&#xff0c;真的让我深有感触&#xff1a;“职场人一定要有居安思危的意识&#xff0c;创业的人一定要三思而后行”。在这个瞬息万变的时代&#xff0c;连被视为铁饭碗的公务员、教师等体制内工作都不能保证一辈子的稳定。发展副业&#xff0c;似乎成…

【k8s管理--两种方式安装prometheus】

1、k8s的监控方案 1.1 Heapster Heapster是容器集群监控和性能分忻工具&#xff0c;天然的支持Kubernetes和CoreOS。 Kubernetes有个出名的监控agent–cAdvisor。在每个kubernetes Node上都会运行cAdvisor&#xff0c;它会收集本机以及容器的监控数(cpu,memory,filesystem,ne…

谈谈鸿蒙的跨端技术方案

这两年要说技术上最火的关键字&#xff0c;我想肯定离不开“鸿蒙”两个字。 不管是技术社区还是身边的开发者多多少少都在关注鸿蒙的发展趋势&#xff0c;特别是HarmonyOS NEXT版本将进入独立生态体系&#xff0c;不再兼容安卓应用&#xff0c;在开发者和各个企业间激起了不小…

Reqable爬虫抓包工具(国产网络调试工具)

官网界面截图&#xff1a; 官网地址&#xff1a;https://reqable.com/zh-CN/windows/ 历史由来&#xff1a; Reqable的前身是HttpCanary&#xff08;一款Android平台应用程序&#xff09;&#xff0c;但是国内开发者推翻了所有的技术栈&#xff0c;并用C和Flutter重写&#x…

指纹芯片系列——ACM32FP0 二合一(主控+TK)锁控芯片,ACM32FP4三合一(主控+算法+语音)等介绍

随着智能设备的持续发展&#xff0c;指纹识别技术成为了现在智能终端市场和移动支付市场中占有率最高的生物识别技术。凭借高识别率、短耗时等优势&#xff0c;被广泛地运用在智能门锁、智能手机、智能家居等设备上。 我们推荐的在2015年进入指纹识别应用领域&#xff0c;自研高…

数据结构——lesson7二叉树 堆的介绍与实现

前言&#x1f49e;&#x1f49e; 啦啦啦~这里是土土数据结构学习笔记&#x1f973;&#x1f973; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1f4a5; 所属专栏&#xff1a;数据结构学习笔记 &#x1f4a5;对于数据结构顺序表链表有疑问的都可以在上面数据结…