指针的深入解读笔记

指针是什么

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

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

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

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.mzph.cn/news/727226.shtml

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

相关文章

详讲Spring的面向切片编程(AOP)二

个人名片: 🐼作者简介:一名大三在校生,喜欢AI编程🎋 🐻‍❄️个人主页🥇:落798. 🐼个人WeChat:hmmwx53 🕊️系列专栏:🖼️…

3月7日代码随想录组合及优化

77.组合 77. 组合 - 力扣(LeetCode) 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n 4, k 2 输出: [[2,4],[3,4],[2,3],[1,2],[1,3…

【手游联运平台搭建】游戏平台的作用

随着科技的不断发展,游戏行业也在不断壮大,而游戏平台作为连接玩家与游戏的桥梁,发挥着越来越重要的作用。游戏平台不仅为玩家提供了便捷的游戏体验,还为游戏开发者提供了广阔的市场和推广渠道。本文将从多个方面探讨游戏平台的作…

复习C++

统计每个月兔子的总数 #include<bits/stdc.h> using namespace std; int n,sum0; void f(int); int main() {int a[1000];cin>>n;a[1]1;a[2]2;for(int i3;i<1000;i){a[i]a[i-1]a[i-2];}cout<<a[n];return 0; } void f(int n){} 猴子吃桃子 #include<…

slice()函数的用法

1、slice没有参数&#xff0c;拷贝一个数组&#xff0c;跟原数组无关联 var arr1 [a, b, c]; var arr2 arr1.slice(); console.log(arr2,arr1arr2); 输出&#xff1a;[a, b, c] false 2、slice(start,end)有参数 从start下标开始(包括start)&#xff0c;到end&#xff…

前端算法之选择排序

2、选择排序&#xff08;Selection Sort&#xff09; 选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理&#xff1a;首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排…

el-table 表格多选, 批量删除功能

一、基础的多选el-table ElementUI 提供了多选行table&#xff0c;同时若依框架也提供了成熟的多选表格。 1.table基础结构 需要绑定selection-change方法 <el-tablev-loading"loading"stripe:data"productList"selection-change"handleSelect…

node的安装与介绍

安装 下载地址 node官网首页就会有两个安装选择&#xff0c;会根据当前电脑的系统自动显示对应的安装包&#xff0c;一个长期维护版&#xff08;LTS&#xff09;,一个是尝鲜版&#xff0c;记住选择LTS版本 安装指定版本下载截图 安装过程截图&#xff08;非常简单&#xff…

计算机设计大赛 深度学习花卉识别 - python 机器视觉 opencv

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &a…

C++ 路径问题

目录 例1 例2 例3 例4 例5 例6 例1 62. 不同路径 1.初始化 2.当前位置的条数&#xff0c;就是上面位置的条数 &#xff0c;加上其左边位置的条数&#xff0c;dp[i][j] dp[i - 1][j] dp[i][j - 1]; 参考代码 class Solution { public:int uniquePaths(int m, int n) …

关于前端的一些

前端侧重于人机交互和用户体验&#xff0c;后端侧重于业务逻辑和大规模数据处理。理论上&#xff0c;面向用户的产品里&#xff0c;所有问题&#xff08;包括产品、设计、后端、甚至看不见的问题&#xff09;的表现形式&#xff0c;都会暴露在前端&#xff0c;而只有部分问题&a…

个推与华为深度合作,成为首批支持兼容HarmonyOS NEXT的服务商

自华为官方宣布HarmonyOS NEXT鸿蒙星河版开放申请以来&#xff0c;越来越多的头部APP宣布启动鸿蒙原生开发&#xff0c;鸿蒙生态也随之进入全新发展的第二阶段。 作为华为鸿蒙生态的重要合作伙伴&#xff0c;个推一直积极参与鸿蒙生态建设。为帮助用户在HarmonyOS NEXT上持续享…

力扣刷题Days11第二题--141. 环形链表(js)

目录 1,题目 2&#xff0c;代码 2.1快慢指针 2.2&#xff0c;哈希表 3&#xff0c;学习与总结 3.1自己尝试写快慢指针 反思 1,题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&…

代码随想录day34:动态规划part2

文章目录 day34&#xff1a;动态规划part262.不同路径63.不同路径 II day34&#xff1a;动态规划part2 62.不同路径 class Solution {public int uniquePaths(int m, int n) {// dp[i][j]&#xff1a;到达(i, j)的路径数量int[][] dp new int[m][n];for (int j 0; j < n…

PostgreSQL安装教程

系统环境 下载压缩包 下载压缩包 解压压缩包 查看解压文件 编译安装 编译 安装 用户权限和环境变量设置 创建用户 创建数据目录和日志目录 设置权限 设置环境变量 初始化数据库 数据库访问控制配置文件 postgresql.conf pg_hba.conf PostgreSQL启动与关闭 手…

Web前端_html5+css3

C/S架构与B/S架构 C/S架构&#xff1a;客户端与服务器&#xff08;需要安装、偶尔更新、不跨平台、安全性要求高&#xff09; B/S架构&#xff1a;浏览器与服务器 HTML认知 注释 在VS Code中键入ctrl / 标签组成和关系 组成&#xff1a;①双标签 ②单标签 关系&#xff…

Docker:部署微服务集群

1. 部署微服务集群 实现思路&#xff1a; ① 查看课前资料提供的cloud-demo文件夹&#xff0c;里面已经编写好了docker-compose文件 ② 修改自己的cloud-demo项目&#xff0c;将数据库、nacos地址都命名为docker-compose中的服务名 ③ 使用maven打包工具&#xff0c;将项目…

Effective C++ 学习笔记 条款17 以独立语句将newed对象置入智能指针

假设我们有个函数用来揭示处理程序的优先权&#xff0c;另一个函数用来在某动态分配所得的Widget上进行某些带有优先权的处理&#xff1a; int priority();void processWidget(std::st1::shared_ptr<Widget> pw, int priority);由于谨记“以对象管理资源”&#xff08;条…

nginx读写锁的实现逻辑

我们一般认为nginx是一个多进程单线程的应用服务&#xff0c;虽然nginx在一个worker进程内是没有数据竞争问题的&#xff08;因为是单线程&#xff09;&#xff0c;但是不免nginx在多个进程间还有一些需要共享的数据&#xff0c;譬如ngx_http_upstream_zone_module模块将peers数…

Pytorch入门实战 P1-实现手写数字识别

目录 一、前期准备&#xff08;环境数据&#xff09; 1、首先查看我们电脑的配置&#xff1b; 2、使用datasets导入MNIST数据集 3、使用dataloader加载数据集 4、数据可视化 二、构建简单的CNN网络 三、训练模型 1、设置超参数 2、编写训练函数 3、编写测试函数 4、…