用最简单的案例带你掌握C++中各种指针

news2024/11/20 14:35:38

1、前言

指针,作为C/C++中最神秘、功能最强大的语法,着实是难以理解 、难以掌握、难以运用。😥
但是,能灵活的使用指针,对利用C/C++开发程序将有很大的帮助,让我们一起来了解了解吧。

2、啥是指针?

指针就是一个变量,一个存放着某个内存地址的变量,此变量需要开发者手动释放,否则会造成内存泄漏。作为程序猿,内存泄漏应该不陌生吧。

在这里插入图片描述
从上图可以看出,指针的本质就是一个变量,存放着某个变量的内存地址,
定义一个指针:

int a = 10;
int* point = &a;
  1. 定义指针:类型 * 变量名 = & 变量名;
  2. &为取址符(引用),获取某个变量的地址;
  3. *为解引用符,用于解出指针指向的内存地址。
  4. 任何类型的指针在32位处理器上是4个字节,在64为处理器上为8个字节。

3、指针的基本操作

3.1、常量指针

常量指针本质就是指向某个常量的指针。
指针的指向可以改变,但是指针指向的值不能改变,因为值是一个常量

#include <iostream>
using namespace std;

const int sum = 10;
const int number = 20;

int main() {

    const int* p = &sum; // 定义一个指针指向一个常量
    cout << *p << endl;
    p = &number; // 改变指针的指向
    cout << *p << endl;
    // *p = 10; // 此处会报错,因为改变了常量的值。
    return 0;
}

3.2、指针常量

指针常量本质是指针类型的常量。
指针的指向不可以改变,但是指针指向的值可以修改。

#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 20;
    int* const point = &a;
    cout << *point << endl; // 输出 10
    //point = &b; // 报错, 不能改变指针常量的指向
    *point = 30;
    cout << *point << endl; // 输出 30
    return 0;
}

在C/C++中有几个常见的指针常量:

  1. this指针,指向当前对象,通过它可以访问当前对象的所有成员。
  2. &变量(引用变量),可以改变变量的值,但是不能改变变量的指向。
  3. 数组名,数组名本质也是一个指针,指向内存的首地址,不能改变指向,但是可以改变每一个元素的值,

3.3、既是常量指针又是指针常量

既不能改变指针的指向,也不能改变指向指向的内存变量的值。

#include <iostream>
using namespace std;

int main() {
    const int a = 10;
    const int* const point = &a;
    cout << *point << endl;

    // 1、指向普通的变量
    int b = 20;
    point = &b; // 报错

    // 2、指向其他常量
    const int c = 30;
	point = &c;  // 报错

    //3、改变指针指向的内存中的值
    *point = 40; // 报错
    
    return 0;
}

3.4、指针作为函数的返回值

语法:返回值类型* 函数名称(参数类型 参数值)

#include <iostream>
using namespace std;

int* function(int a, int b){
    int* sum = new int;
    *sum = a + b;
    return sum;
}

int main() {
    int* result = function(10, 20);
    
    cout << *result << endl; // 输出 30
    
    return 0;
}

当函数返回的类型是指针类型的时候,返回值就必须是在堆上开辟的内存空间,绝对不能返回函数中的局部变量,如果返回了局部变量就会报异常:

address of local variable ‘a’ returned [-Wreturn-local-addr]

那么为什么返回局部的指针变量不会报错?
不是不会报错,而是需要分情况而定,如果局部指针指向的是一个局部的普通变量,那么也同样会报错,因为指针的数据还是在栈上开辟,而栈上的数据在函数 执行完毕后就会被释放。

int* function(int a, int b){
    int c = a + b;
    int* sum = &c;
    return sum; // 报错
}

而如果指针指向的数据是由new关键字开辟在堆上的数据,就不会报错。

int* function(int a, int b){
    int* sum = new int;
    *sum = a + b;
    return sum;
}

3.5、指针做函数的形参

语法:返回值类型 方法名称(指针类型* 变量名称)

#include <iostream>
using namespace std;

int function(int* a, int* b){
   int sum = (*a + *b);
   return sum;
}

int main() {
    int a = 10, b = 20;
    int result = function(&a, &b);
    cout << result << endl; // 输出 30
    return 0;
}

只要是指针,然后符合语法规范,便可以变成常量指针、指针常量等。
而在发开中,我们也推荐使用指针作为函数的形参:
在C/C++中,我们可以使用sizeof关键字看到某个对象所占用的内存空间,当对象的属性非常多或者属性所占的内存比较多的时候,函数的形参也会复制实参的所有属性在内存中开辟相应的内存空间,那么此时就会十分的消耗内存。
上面我们了解到:任何类型的指针变量就只占四个或者八个字节,所以我们可以使用指针作为函数的形参,以此来减少内存的开销。

#include <iostream>
#include "headers/Person.hpp"
#include "string"
using namespace std;

int main() {
    cout << sizeof(int *) << endl; // 输出 8
    cout << sizeof(Person<string,string> *) << endl; // 输出 8

    return 0;
}

因为我的电脑是64位的笔记本,所以任何指针类型的变量都只占8个字节。

4、引用类型(&引用指针)

引用指针本质就是一个指针常量,在有的书上也称为别名,是一个变量的副本,
引用的特点:

  1. 引用变量就是某个变量或者对象的别名
  2. 引用变量不占用内存
  3. 引用变量必须在声明时完成赋值,完成变量与引用的绑定
#include <iostream>
#include "headers/Person.hpp"
#include "string"
using namespace std;

int main() {

    Person<string,string> person = Person<string ,string>("10001","abc"),
            &point = person;
    cout << sizeof(point) << endl; // 输出 64
    cout << sizeof(person) << endl; // 输出 64

    return 0;
}

不是说引用不占用内存空间吗?为什么引用和变量所占用的内存空间是一样的咧?
因为在表达式中,使用引用实际上就像使用变量本身一样,所以直接用sizeof是得不到引用本身的大小的。
但是我们可以查看地址,看引用的地址是否和变量的地址是同一个。

int main() {
    Person<string,string> person = Person<string ,string>("10001","abc"),
            &point = person;
    cout << &person << "---->" << &point << endl;
    return 0;
}

输出:

0xb00a9ff830---->0xb00a9ff830
可以看出是同一个地址。

4.1、引用类型作为函数的返回值

语法:返回值类型& 函数名(形参类型 形参名称)
好处:最大的好处是在内存中不产生返回值的副本

#include <iostream>
using namespace std;

int& function(int a, int b){
    int sum = a + b;
    return sum;
}

int main() {
    cout << function(10, 20) << endl;
    return 0;
}

此段程序报错,这里和用指针做函数的返回值是一样的错误,因为引用的本质还是指针,所以不能返回在栈上开辟的内存。
如果把function中的sum这个局部变量变为全局变量,那么这段程序就是正确的,或者使用new 在堆上开辟内存。

#include <iostream>
using namespace std;

int& function(int a, int b){
    int* sum = new int;
    *sum = (a + b);
    return *sum;
}

int main() {
    cout << function(10, 20) << endl; // 输出 30
    return 0;
}

4.2、引用类型作为函数的形参

语法: 返回值类型 方法名(类型& 变量名)

#include <iostream>
using namespace std;

int function(int& a, int& b){
   return a + b;
}

int main() {
    int a = 10, b = 20;
    cout << function(a, b) << endl;  // 输出 30
    return 0;
}

当我们使用引用类型作为函数的形参的时候,需要注意一个点:当我们修改了引用的值的时候,其实本质变量的值也会发生改变,因为引用只是一个别名,是会修改本质变量的值的。

#include <iostream>
#include "string"
using namespace std;

int function(int& a, int& b){
    int sum = a + b; 
    a = 20;
    b = 10;
   return sum;
}

int main() {
    int a = 10, b = 20;
    cout << function(a, b) << endl;  // 输出 30
    cout << "a=" << a << ", b=" << b;  
    //输出a=20, b=10   此时a 和 b的值发生了交换。
    //所以当别名修改了值之后,本质变量的值也会发生修改。
    return 0;
}

5、总结

  1. 指针作为C/C++中的重点知识,还是要弄明白的好,不然大佬写的代码实在难以看懂。😰😨
  2. 指针的本质上也就是对内存的应用,但不是开辟内存,开辟内存还得使用calloc等函数,这两大点也就是这门语言令人着迷的地方,通过源码可以看到C/C++中大量的使用了指针,熟悉好这些内容这门语言也算是入了门了。
  3. 指针和引用本身都是一样的,只不过是引用相当于对指针进行了一层封装,知识点也差不多,只是多多少少存在一些坑,慢慢理解就行了。
  4. 以上是本人的个人观点,有不足之处还望指出🙂🙂🙂
    在这里插入图片描述

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

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

相关文章

参加《2022 中国开发者影响力盛典》我的 4 重收获!

感谢 CSDN 邀请&#xff0c;西红柿有幸参加了 2022 中国开发者影响力盛典暨 CSDN 企业生态汇&#xff0c;让我有了一个不虚此行的下午&#xff0c;也跟大家分享一下我在会上的 4 重收获吧~第一重收获&#xff1a;互联网圈大佬 会议聚焦开发者生态建设主题&#xff0c;分享了 CS…

分布式基础篇4 —— 基础篇完结

分类维护一、三级分类后端实现准备工作跨域问题关闭 ESLint 检查前端实现二、分类删除前端完善分类列表后端实现——删除配置发送请求代码片段前端实现——删除三、分类增加前端实现四、分类修改五、拖拽菜单拖拽效果实现拖拽数据收集拖拽功能完成拖拽功能完善六、批量删除品牌…

JS知识补充-JS原型链

概述JS原型链别名&#xff1a;隐式原型链作用&#xff1a;根据一定路径查找属性&#xff08;方法&#xff09;作用举例&#xff1a;我们定义一个构造函数Fn&#xff0c;使用此构造函数创建一个对象fn1&#xff0c;接着使用创建的对象fn1去调用toString方法并打印&#xff0c;我…

【阶段三】Python机器学习03篇:机器学习中的函数、机器学习中的梯度下降、机器学习的数据结构:张量与机器学习概率与统计基础

本篇的思维导图: 机器学习中的函数 函数描述了输入与输出的关系。在函数中,一个事物(输出)随着另一个(或一组)事物(输入)的变化而变化,如下图所示。 输入与输出的关系一般情况下,用x(或x1,x2,x3,…)表示输入,用y表示输出,并把它们叫作变量,…

Java设计模式中的设计原则/开闭原则、里氏代换原则和依赖倒转原则又是什么,怎么用

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 3.设计原则 3.1 目的 提高软件系统可维护性与可复用性增加软件可扩展性与灵活性节约开发成本与维护成本 3.2 开闭原则 3.2.1 特点 对扩展开放&#xff0c;对修…

实战干货|自研数据存储迁移MySQL实战

背景 最近公司内部在做某自研数据存储的下线工作&#xff0c;这里我们暂且化名其为DistributeSQL&#xff0c;由于DistributeSQL不再进行服务支持&#xff0c;需要迁移项目中使用到该存储到其他数据存储中。 本篇来聊聊这次在数据存储迁移过程中的方案设计思路、实现的大致细节…

中老年服装电商小程序开发

近年来&#xff0c;随着网络的发展&#xff0c;中老年服装电商小程序开发有了很大的进步。这种平台不仅可以方便用户购买到最新最时尚的产品&#xff0c;而且还能帮助商家提高销售业绩。 1&#xff1a;中老年服装电商小程序开发的优势 中老年人对商品信息需求大、容易接受新鲜…

实验⼀:Windows主机漏洞利⽤攻击实践

永恒之蓝简介 永恒之蓝&#xff08;Eternal Blue&#xff09;爆发于2017年4月14日晚&#xff0c;是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限&#xff0c;以此来控制被入侵的计算机。甚至于2017年5月12日&#xff0c; 不法分子通过改造“永恒之蓝”制作了wannacry…

【ROS】—— ROS重名问题(九)

文章目录前言1. ROS工作空间覆盖2. ROS节点名称重名2.1 rosrun设置命名空间与重映射2.1.1 rosrun设置命名空间2.1.2 rosrun名称重映射2.1.3 rosrun命名空间与名称重映射叠加2.2 launch文件设置命名空间与重映射2.3 编码设置命名空间与重映射2.3.1 重映射2.3.2 C 实现:命名空间3…

Maven基础学习——依赖配置(1):配置同一项目下的三个工程

依赖配置一、前言二、创建第一个工程三、新建第二个工程四、创建第三个工程五、配置1.每个工程的.xml文件2.文件配置六、结语一、前言 在讲述依赖配置时&#xff0c;需要使用实例来说明&#xff0c;在B站黑马课程&#xff08;第12小节&#xff09;中没有讲到如何配置基础的三个…

[Effective Objective] 熟悉Objective-C

了解 Objective-C Objective_C 是一种面向对象的语言。但与jave、C等语言不同&#xff0c;它使用了消息结构&#xff08;messaging structure&#xff09;而非函数调用&#xff08;function calling&#xff09;。Objective-C由Smalltalk演化而来&#xff0c;后者是消息语言的…

React 学习笔记总结(六)

文章目录1. redux 介绍2. redux 工作流程3. redux 的使用4. redux 完整结构补充5. redux的 异步action6. react-redux库 与 redux库7. react-redux库的 实战8. react-redux的connect 最精简写法9. react-redux的 Provider组件作用10. react-redux 整合UI组件 和 容器组件11. re…

webgl图形平移、缩放、旋转

文章目录前言平移图示代码示例缩放图示代码示例旋转公式推导代码示例总结前言 在webgl中将图形进行平移、旋转、缩放的操作称为变换或仿射变换&#xff0c;图形的仿射变换涉及到顶点位置的修改&#xff0c;通过顶点着色器是比较直接的方式。本文通过着色器实现对webgl图形的仿…

ArcGIS基础实验操作100例--实验65按字段调整点符号方向

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验65 按字段调整点符号方向 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff0…

计算机组成原理_总线

计算机组成原理总目录总线概述 1. 总线介绍 我们知道计算机中有CPU、主存、辅存&#xff0c;以及打印机、键盘、鼠标等等的一些外设 那么各个设备之间肯定是要进行数据传输的&#xff0c;这就需要许多线路将它们连接起来 第一种方法&#xff1a;两两相联 外设数量越多&#xf…

35、基于STM32的电子钟(DS1302)

编号&#xff1a;35 基于STM32的电子钟&#xff08;DS1302&#xff09; 功能描述&#xff1a; 本设计由STM32单片机液晶1602按键DS1302时钟组成。 1、采用STM32F103最小系统。 2、利用DS1302芯片提供时钟信号 3、液晶1602实时显示年月日、时分秒、星期等信息。 4、三个按键可…

隐形AR眼镜厂商Mojo Vision裁员75%,专注Micro LED技术

1月7日青亭网报道&#xff0c;隐形AR眼镜厂商Mojo Vision官方宣布了一项重大调整&#xff0c;其中因为产品进展问题&#xff0c;同时还有融资进展受阻等面临大裁员&#xff0c;将进行一系列中心调整&#xff0c;据了解本次裁员比例高达75%。重点关注&#xff1a;1&#xff0c;M…

【Day5】力扣第328题,奇偶链表

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#x1f680;&#x1f680;&#x1f680;&#xff0c;今天带大家刷一个力扣链表题&#xff0c;有人可能会说&#xff0c;一道题够嘛&#xff0c;刚开始刷题别着急&#xff0c;毕竟&#xff0c;心急吃不了热豆腐&#xff0c;&…

Mathorcup数学建模竞赛第六届-【妈妈杯】B题:小区车位分布的评价和优化模型(附特等奖获奖论文和Java代码)

赛题描述 随着现代社会经济的快速发展,房地产成为国家经济发展中重要的经济增长点之一。而小区内汽车停车位的分布对于小区居民的上下班出行影响很大。请建立数学模型,解决下列问题: 问题1:分析评判小区汽车停车位分布是否合理的几个关键指标,建立评判车位分布合理的数学…

嵌入式Linux-对子进程的监控

1. 进程的诞生与终止 1.1 进程的诞生 一个进程可以通过 fork()或 vfork()等系统调用创建一个子进程&#xff0c;一个新的进程就此诞生&#xff01;事实上&#xff0c;Linux系统下的所有进程都是由其父进程创建而来&#xff0c;譬如在 shell 终端通过命令的方式执行一个程序./…