[通俗易懂C++]:指针和const

news2025/2/24 5:53:28

之前的文章有说过,使用指针我们可以改变指针指向的内容(通过给指针赋一个新的地址)或者改变被保存地址的值(通过给解引用指针赋一个新值):

int main()
{
    int x { 5 };  // 创建一个整数变量 x,初始值为 5
    int* ptr { &x }; // 创建一个指针 ptr,指向 x 的地址(非 const 指针)

    int y { 6 };  // 创建一个整数变量 y,初始值为 6
    ptr = &y;  // 将 ptr 指向 y 的地址,我们可以改变它指向的地址

    *ptr = 7;  // 通过指针修改 ptr 所指向地址的值,即将 y 的值改为 7

    return 0;  // 程序成功结束
}

那么,如果我们想指向的值是const呢?

int main()
{
    const int x { 5 }; // x 现在是常量,值为 5
    int* ptr { &x };   // 编译错误:不能将 const int* 转换为 int*,因为 x 是常量,不能修改它的值

    return 0;  // 程序正常结束
}

这段代码会出现编译错误,因为我们尝试将一个指向 const int 类型的指针赋值给一个普通的 int* 指针,C++ 中不允许这样做。具体原因是 const 修饰符确保该变量的值在程序运行期间不能被修改,而普通的 int* 指针允许修改所指向的内容,所以会发生类型不匹配。


指向常量值的指针

指向常量值的指针(有时简称为指向 const 的指针)是一个(非 const)指针,它指向一个常量值。声明指向常量值的指针时,只需要在指针的数据类型之前使用const关键字。

int main()
{
    const int x {5};
    const int* ptr = {&x};
    
    *ptr = 6; // 非法操作:不能更改const值
   
    return 0;
}

在上面的示例中, ptr 指向一个 const int 。因为被指向的数据类型是 const,所以指向的值不能被更改。

然而,因此指向常量的指针本身不是常量(而是它的指向是一个常量),所以,我们可以通过给指针赋值=一个新的地址来改变指针指向的内容。

image-20250222224723856

int main()
{
    const int x {5};
    const int* ptr = {&x}; // ptr指向一个值为const的int类型的变量地址
    
    const int y {6};
    
    ptr = &y; // 指向一个新的地址
    
    std::cout << ptr << '\n';
    std::cout << *ptr << '\n';
    
    
    return 0;
}

就像 const 引用一样,指向 const 的指针也可以指向非 const 变量。指向 const 的指针将所指向的值视为常量,不管该地址上的对象最初是否被定义为 const

有点绕口,看个例子助消化:

int main()
{
    int x{ 5 }; // 非 const 变量,x 的初始值为 5
    const int* ptr { &x }; // ptr 是一个指向 const int 的指针,指向 x

    *ptr = 6;  // 不允许:因为 ptr 指向的是 "const int",所以不能通过 ptr 修改值
    x = 6; // 允许:因为 x 是非 const 的,直接修改 x 的值是可以的

    return 0; // 程序结束
}
  • **int x{ 5 };**这里定义了一个非 const 变量 x,并将其初始化为 5。
  • const int* ptr { &x };:定义了一个指向 const int 类型的指针ptr,它指向变量 x 的地址。虽然 x 是一个非 const 变量,但指针 ptr 被声明为指向 const 类型的对象。这样,ptr 不能修改所指向的值。
  • ***ptr = 6;**这是一个编译错误。虽然 x 是非 const 的,但由于 ptr 是指向 const int 的指针,编译器禁止通过该指针修改 x 的值。这是因为 ptr 被声明为指向常量,所以它会将 x 视为常量
  • **x = 6;**这行代码是允许的,因为 x 是非 const 的,直接通过变量名 x 修改其值没有问题。指针的 const 属性只影响通过指针访问数据时的行为,而不是通过普通的变量名。

常量指针

基于上述这些理论不难想到,我们也可以使指针本身成为常量。这就是常量指针,该指针是指其地址初始化后不能再次被更改。和普通的常量定义的概念是一致的,不过是将这个概念用在了指针类型上。

声明一个常量指针,在指针声明中的星号后面使用const关键字即可:

int main()
{
    int x{ 5 };
    int* const ptr { &x }; //星号之后的const意味着这是const指针

    return 0;
}
  • 在上述情况下, ptr 是一个指向(非 const)int 值的 const 指针。

  • 就像一个普通的 const 变量一样,const 指针必须在定义时初始化,并且这个值不能通过赋值来改变:

int main()
{
    int x{ 5 }; // 创建一个整数变量 x,初始值为 5
    int y{ 6 }; // 创建一个整数变量 y,初始值为 6

    int* const ptr { &x }; // const 指针 ptr 被初始化为指向 x 的地址
    ptr = &y; // 错误:一旦初始化,const 指针就不能改变它指向的地址

    return 0; // 程序正常结束
}

然而,因为所指向的值是非 const 的,可以通过解引用 const 指针来更改所指向的值:

 int main()
{
    int x{ 5 };
    int* const ptr { &x }; 

    *ptr = 6; 

    return 0;
}

最后,可以通过在类型和星号之前和之后都是用const关键字来声明一个指常量值的常量指针:

int main()
{
    int value { 5 };
    const int* const ptr { &value }; // 指常量值的常量指针

    return 0;
}

一个指向常量值的常量指针不能改变其地址,也不能通过该指针改变它所指向的值。它只能解引用以获取它所指向的值。


小结一下

  • 一个非 const 指针(例如 int* ptr )可以被分配另一个地址以改变它所指向的内容。
  • 一个常量指针(例如 int* const ptr )始终指向同一个地址,并且这个地址不能更改。
  • 一个指向非 const 值的指针(例如 int* ptr )可以改变它所指向的值。这些指针不能指向 const 值。
  • 一个指向常量值的指针(例如 const int* ptr )在通过该指针访问时将值视为常量,因此不能更改它所指向的值。这些指针可以指向常量或非常量左值(但不能指向右值,因为右值没有地址)。

感谢阅读、欢迎指正!

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

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

相关文章

大一高数(上)速成:导数和微分

目录 1.分段函数的可导性&#xff1a; 2.隐函数求导: 3.参数方程求导: 4.对数求导法: 5.函数的微分: 1.分段函数的可导性&#xff1a; 2.隐函数求导: 3.参数方程求导: 4.对数求导法: 5.函数的微分:

京东cfe滑块 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 headers {"accept&qu…

react 踩坑记 too many re-renders.

报错信息&#xff1a; too many re-renders. React limits the number of randers to prevent an infinite loop. 需求 tabs只有特定标签页才展示某些按钮 button要用 传递函数引用方式 ()>{} *还有要注意子组件内loading触发 导致的重复渲染

BGP分解实验·19——BGP选路原则之起源

当用不同的方式为BGP注入路由时&#xff0c;起源代码将标识路由的来源。 &#xff08;在BGP表中&#xff0c;Network为“i”&#xff0c;重分布是“&#xff1f;”&#xff09; 实验拓扑如下&#xff1a; R2上将来自IGP的路由10.3.3.3/32用network指令注入BGP;在R4上将来自I…

单机上使用docker搭建minio集群

单机上使用docker搭建minio集群 1.集群安装1.1前提条件1.2步骤指南1.2.1安装 Docker 和 Docker Compose&#xff08;如果尚未安装&#xff09;1.2.2编写docker-compose文件1.2.3启动1.2.4访问 2.使用2.1 mc客户端安装2.2创建一个连接2.3简单使用下 这里在ubuntu上单机安装一个m…

家用路由器的WAN口和LAN口有什么区别

今时今日&#xff0c;移动终端盛行的时代&#xff0c;WIFI可以说是家家户户都有使用到的网络接入方式。那么路由器当然也就是家家户户都不可或缺的设备了。而路由器上的两个实现网络连接的基础接口 ——WAN 口和 LAN 口&#xff0c;到底有什么区别&#xff1f;它们的功能和作用…

实操解决Navicat连接postgresql时出现‘datlastsysoid does not exist‘报错的问题

1 column “datlastsysoid“ does not exist2 Line1:SELECT DISTINCT datalastsysoid FROM pg_database问题分析 Postgres 15 从pg_database表中删除了 datlastsysoid 字段引发此错误。 决绝方案 解决方法1&#xff1a;升级navicat 解决方法2&#xff1a;降级pgsql 解决方…

3分钟idea接入deepseek

DeepSeek简介 DeepSeek 是杭州深度求索人工智能基础技术研究有限公司开发的一系列大语言模型&#xff0c;背后是知名量化资管巨头幻方量化3。它专注于开发先进的大语言模型和相关技术&#xff0c;拥有多个版本的模型&#xff0c;如 DeepSeek-LLM、DeepSeek-V2、DeepSeek-V3 等&…

树莓派理想二极管电路分析

如果 Vin Vout&#xff0c;比如说 5.0V&#xff0c;PNP 晶体管以当前的镜像配置偏置。晶体管 U14 的 Vb 将为 5-0.6 4.4V&#xff0c;镜像配置意味着 Vg 也将为 4.4V. Vgs 为4.4-5.0 -0.6V。mosfet 将处于关闭状态&#xff08;几乎打开&#xff09;。如果 Vout 略低于 Vin&a…

Unity贴图与模型相关知识

一、贴图 1.贴图的类型与形状 贴图类型 贴图形状 2.在Unity中可使用一张普通贴图来生成对应的法线贴图&#xff08;但并不规范&#xff09; 复制一张该贴图将复制后的贴图类型改为Normal Map 3.贴图的sRGB与Alpha sRGB&#xff1a;勾选此选项代表此贴图存储于Gamma空间中…

Linux--进程(进程虚拟地址空间、页表、进程控制、实现简易shell)

一、进程虚拟地址空间 这里以kernel 2.6.32&#xff0c;32位平台为例。 1.空间布局 在 32 位系统中&#xff0c;虚拟地址空间大小为 4GB。其中&#xff1a; 内核空间&#xff1a;占据高地址的 1GB &#xff0c;用于操作系统内核运行&#xff0c;包含内核代码、内核数据等&am…

中间件专栏之redis篇——redis基本原理、概念及其相关命令介绍

一、redis是什么 redis是remote dictionary service的简称&#xff0c;中文翻译为远程字典服务&#xff1b; redis是一种数据库&#xff0c;若按照类型来归类&#xff0c;则其可以被归入三个类型数据库&#xff0c;分别为&#xff1a;内存数据库、KV数据库、数据结构数据库&a…

在列线图上标记做为线性模型的局部解释

改造列线图做为线性模型的解释 除了使用列线图算法产生的meta数据和score数据进行线性模型的解释&#xff08;全局性解释和局部性解释&#xff09;&#xff0c;另外一种做法是改造列线图来作为线性模型的解释。这里尝试改造列线图来对线性模型进行全局性和局部性解释。 全局…

KubeKey一键安装部署k8s集群和KubeSphere详细教程

目录 一、KubeKey简介 二、k8s集群KubeSphere安装 集群规划 硬件要求 Kubernetes支持版本 操作系统要求 SSH免密登录 配置集群时钟 所有节点安装依赖 安装docker DNS要求 存储要求 下载 KubeKey 验证KubeKey 配置集群文件 安装集群 验证命令 登录页面 一、Ku…

车载诊断数据库 --- AUTOSAR诊断文件DEXT简介

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

庙算兵棋推演AI开发初探(5-数据处理)

碎碎念&#xff1a;这最近几个月过得那叫一个难受&#xff0c;研究生开题没过、需求评审会在4月和6月开了2次、7月紧接着软件设计评审会&#xff0c;加班干得都是文档的事情&#xff0c;还有开会前的会务和乱七八糟的琐事&#xff0c;我们干的还被规定弄的束手束脚&#xff0c;…

【MyBatis】#{} 与 ${} 的区别(常见面试题)

目录 前言 预编译SQL和即时SQL 什么是预编译SQL&#xff1f; 什么是即时SQL&#xff1f; 区别 #{} 与 ${}的使用 防止SQL注入 什么是SQL注入&#xff1f; 原理 排序功能 模糊查询 总结#{}和${}的区别 前言 在前面的学习中&#xff0c;我们已经知道了如果SQL语句想…

鸿蒙开发环境搭建-入门篇

本文章讲述如何搭建鸿蒙应用开发环境&#xff1a;新建工程、虚拟机运行、真机调试等。 开发工具: DevEco Studio 5.0.3.906 os系统: mac 参考文档&#xff1a;https://juejin.cn/post/7356143704699699227 官网鸿蒙应用开发学习文档&#xff1a;https://developer.huawei.com/c…

iOS开发 网络安全

iOS开发中的网络安全 在当前的数字化时代&#xff0c;任何应用程序都需要重视网络安全。尤其是对于iOS应用开发者而言&#xff0c;确保应用与服务器之间的数据传输安全是至关重要的。接下来&#xff0c;我们将学习“iOS开发 网络安全”的实现过程。 流程步骤 以下是实现iOS网…

MATLAB在投资组合优化中的应用:从基础理论到实践

引言 投资组合优化是现代金融理论中的核心问题之一&#xff0c;旨在通过合理配置资产&#xff0c;实现风险与收益的最佳平衡。MATLAB凭借其强大的数学计算能力和丰富的金融工具箱&#xff0c;成为投资组合优化的理想工具。本文将详细介绍如何使用MATLAB进行投资组合优化&#…