浅谈C/C++的常量const、指针和引用问题

今天我们来探讨C/C++中const、指针和引用的相关问题。这些概念是编程中的重要组成部分,它们的正确使用对于代码的可读性和可维护性至关重要。通过深入了解const的不可变性、指针的灵活性以及引用的简洁性,我们能够更好地掌握编程的精髓,并写出更加健壮和高效的代码😊😊😊

浅谈C/C++

    • (1)const + 一级指针
    • (2)const + 二级(多级)指针
    • (3)引用操作符 &

先来看看对const的描述,C/C++中的const关键字用于声明一个常变量,即一个“不可修改的值”,通过使用关键字const,我们可以明确地指定一个变量、函数参数、函数返回值或类成员函数为只读,从而禁止对其进行修改,增强程序的健壮性。可见我们对于 const 的描述,常见为一个不可修改!!!,但在C和C++中,编译器对于 const 的反应也是 有差异的

1、我们先来看看.c文件 -》 C语言中的const,猜猜有何问题?

#include<stdio.h>
int main()
{const int a;int array[a] = { 0 };const int b = 0;b = 5;int* p = (int*)&a; *p = 30;printf("%d %d %d\n", a, *p, *(&a));
}

我们不难想到

  • array[a] 会报错,因为a没有被初始化,定义数组需要有一个常量值作为数组的长度,而a不可推测;
  • b = 5,变量b为常变量,不可被修改
  • 然而,对于涉及到变量p的语句,朋友是否有些疑惑🤔🤔🤔,p是指向变量a地址的普通指针,而*p = 30就是将它指向的地址上对应的值改为30,即分配给变量a的地址空间中的值 被改了🤣🤣🤣,我们去掉错误语句,加上a = 20后,看看执行的结果
    在这里插入图片描述

由此可见,我们可以归纳出 C语言编译器 下有关const的特性

		在C语言编译器中
a) const修饰可以不进行初始化,被看作常变量
b) 被const修饰的变量只是不能作为左值被修改,通过指针或引用的方式能够修改


2、我们再来看看.cpp文件 -》C++语言中的const,猜猜有何问题?

int main()
{const int b;int c = 5;const int a = c;//const int a = 20;int array[a] = { 0 };int before = a;int* p = (int*)&a; *p = 30; int after = *(int*) & a;cout << before << endl << after << endl; cout << a << " " << *p << " " << *(&a) << endl;return 0;
}

不难想到

  • const int b;会报错,因为在C++中const修饰的变量被看作常量,不能够不对它进行初始化
  • array[a]报错,变量a为变量c赋值而来,而c是一个变量,赋给变量a后,变量a也作为变量,而array[]里需要一个常量,所以出现错误,如下图
    在这里插入图片描述
    除去错误语句,把const int a = 20后的执行结果在这里插入图片描述

由此我们可以了解到

		在C++语言编译器中
a) const修饰的变量必须初始化,不能作为左值
b) 被const修饰的变量只是不能作为左值被修改,通过指针或引用的方式能够修改
c) 被const修饰的变量被叫做常量,若直接赋值一个变量,不能直接作为数组的长度
c) 在C++编译器中,执行程序遇到const变量的时候,会直接被看作是常量,直接用初值替换,即*(&a)  ->   20

本来我们使用const修饰变量的目的就是保证变量不被修改,然上面我们能够看到,如果我们把一个常量的地址泄露给了一个普通指针或者引用,那么就会有被修改的风险,因此我们需要对此问题做相应的处理,也就是下面我们将要介绍的 const + 指针的结合

📢📢📢注意:在C++语言规范中,const修饰的类型是离它最近的类型

(1)const + 一级指针

我们先来看看一级指针 + const的情况,如下:

//根据上面的注意项,有
const int *p; -> const修饰int,即值*p不可修改,指针可以任意指向p = &a
int const *p; -> const修饰int,同上
int* const p; -> const修饰int*,即指针指向p不可修改,值*p可以任意
int* const *p; -> const同时修饰int*int,都不能修改

在这里插入图片描述
看到这,我们就能够自己解决:当泄露了常量的指针或引用时,常量可能被修改的问题。但是对于const指针的各种组合类型中,也并非都能够相互进行强制转化的。

📢📢📢这里又需要注意一点:const右边如果没有指针*的话,const是不会参与进推测的类型

int* q1 = nullptr;
int* const q2 = nullptr;
const int* q3 = nullptr;
int const* q4 = nullptr;
int* const* q5 = nullptr;
cout << typeid(q1).name() << endl; //  -> int*
cout << typeid(q2).name() << endl; //  -> int*
cout << typeid(q3).name() << endl; //  -> const int*  / int const*
cout << typeid(q4).name() << endl; //  -> const int*  / int const*
cout << typeid(q5).name() << endl; //  -> int* const*
//以上代码,朋友可以自己去验证一下是否正确

这里我们再给出一段代码,来看看是否正确🤔🤔🤔

#include<iostream>
using namespace std;int main()
{int a = 5;const int* pp = &a;//const int* pp = nullptr;int* qq = p;//cout << typeid(pp).name() << endl;char s = 'a';const char* ss = &s;char* sss = ss;return 0;
}

整形常量指针p指向的是普通变量a的地址,整形变量指针qq指向p,也是普通变量a的地址,既然是普通变量,那么普通变量a的值应该是能被修改是吧🤫🤫,然当我们放在visual studio 2022上时,发现它报错了!!!,其实这更变量a无关,若我们将pp指向nullptr,仍然是一样的结果。因为在类型上,编译器是禁止将 const int* 类型的数据转换成 int* 类型的,使用char等其他类型也是如此

(2)const + 二级(多级)指针

我们再来看看const + 二级(多级)指针的情况,如下:

const int**q;  -> **q 不能被赋值
int* const *q;  ->  *q 不能被赋值
int** const q;   ->  q不能被赋值

📢📢📢注意:对于const + 多级指针 的结合,涉及到类型转化时

错误:const int*   -> int*        ;int**        -> const int** ;int* const*  -> int** //const修饰一级指针《=》 const* -> *《=》 const int* -> int* 是错误的!const int**  -> int**       ;正确:int*         -> const int*  ;int**        -> int* const* 《=》 * -> const*《=》 int* -> const int* 是正确的!

这里我们给出一段代码,来看看是否正确🤔🤔🤔

#include<iostream>
using namespace std;
int main()
{int a = 10;int* p = &a;const int **q = &p;  // const int**  <-  int**return 0;
}

在这里插入图片描述
这里我们可以把换换const int* *q,即*q是整形常量的指针 《=》p,所以当我们把一个整形常量的地址赋值给*q时,const int b = 20; *q = &b,相当于直接就修改了p,使其指向了常量的地址,把变量b的指针间接地泄露给了普通指针*p,系统报错!!!
—>
解决方法:const int* const*q = &p;

  • 我们让其不能够给*q赋值,即禁用*q = &b

(3)引用操作符 &

引用是C++种的一种特殊数据类型,它提供了对现有别名的访问,通过引用可以使用相同的变量名来访问同一个内存地址的内容,一旦被初始化后,它将一致引用同一个对象,并且不能再引用其他对象,因此引用也被叫做更安全的指针!对比一下指针和引用的区别:

  • 引用是必须初始化的,指针可以不被初始化
  • 引用只有一级,而指针有多级
  • 在汇编层面上,定义或修改引用变量和指针变量是一样的

从以下代码即图例可以看出:

#include<iostream>
using namespace std;
void swap(int& a, int& b) { int t = a; a = b; b = t; } //引用底层还是转化为指针>实现
void swap(int* a, int* b) { int t = *a; *a = *b; *b = t; }int main()
{int a = 10, b = 20;swap(&a, &b);swap(a, b);int *p = &a;int &q = a;int array[5] = {};int (&vv)[5] = array;return 0;

在这里插入图片描述
在这里插入图片描述

引用又可分为两种:左值引用 和 右值引用

  • 左值引用: 对象的一个命名引用,它绑定到左值(如一个具名变量),即有名字、有内存,通过左值引用,可以修改被引用对象的值。
  • 右值引用: 对临时对象或将要销毁的对象的引用,它绑定到右值(如一个临时对象,一个匿名对象,或一个将要销毁的对象),通过右值引用,可以实现资源的高效转移和移动语义
#include<iostream>
using namespace std;
{int a = 10;//int &&b = a;  //错误右值引用变量无法绑定到左值int &&c = 20;  //c++11提供了右值引用,汇编指令上产生临时量const int &d = a; //左值引用可以指向左值int &e = a;int &f = c; //右值引用本身是一个左值return 0;

在这里插入图片描述

📢📢📢注意:左值引用变量能够拥有右值和左值,但右值引用变量只能引用右值

上面就是关于const 、 指针 、 引用的问题,学而不思则罔,思而不学则殆,下面给出几道题目,来检验一下我们的学习成果

A)  int a = 10const int *p = &aint *q = p  xxx
B)  int a = 10int* const p = &aint *q = p				gggC) 	int a = 10int* const p = &aconst int *q = p	  	gggD)	int a = 10int *p = &aconst int **q = &p		xxxE)	int a = 10int *p = &aint* const* q = &p		gggF)	int a = 10int *p = &aint ** const q = &p		gggG)	int a = 10int* const p = &aint **q = &p			int**   <-  int* const*xxxH)	int a = 10const int*p = &aint* const* q = &p		int* const*   <-   const int**《=》 int*          <-   const int*xxx引用是不参与推测类型的I)	int a = 10int *p = &aint *&q = p			《=》 int **q = &pgggJ)	int a = 10int* const p = &aint *&q = p			《=》 int **q = &pxxxK)	int a = 10int *p = &aconst int* &q = p;	《=》 const int**q = &pggg

🌻🌻🌻以上就是浅谈C/C++的常量const、指针和引用的有关问题,如果聪明的你浏览到这篇文章并觉得文章内容对你有帮助,请不吝动动手指,给博主一个小小的赞和收藏 🌻🌻🌻

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

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

相关文章

Jupyter Notebook 怎么在虚拟环境之间切换

前提已经在虚拟环境 venv01 中安装并配置好了 Jupyter Notebook 要在新的虚拟环境 venv02 中使用之前安装的 Jupyter Notebook 并将其切换到这个环境&#xff0c;你可以按照以下步骤操作&#xff1a; 激活新的虚拟环境&#xff1a; 打开命令行或终端&#xff0c;激活你的新虚拟…

产品推荐 - 基于FPGA XC7K325T+DSP TMS320C6678的双目交汇视觉图像处理平台

一、产品概述 TES601是一款基于FPGA与DSP协同处理架构的双目交汇视觉图像处理系统平台&#xff0c;该平台采用1片TI的KeyStone系列多核浮点/定点DSP TMS320C6678作为核心处理单元&#xff0c;来完成视觉图像处理算法&#xff0c;采用1片Xilinx的Kintex-7系列FPGA XC7K325T作为视…

【定向合作计划】学生时期学习资源同步-全栈开发基础学习书单

原创作者&#xff1a;田超凡&#xff08;程序员田宝宝&#xff09; 版权所有&#xff0c;引用请注明原作者&#xff0c;严禁复制转载 【定向合作计划】以下为本人在学生时期2015-2017学习全栈开发基础的书单&#xff0c;因为资料太多&#xff0c;外接硬盘一共大概10个T&…

vue项目中使用highcharts记录(甘特图)

使用npm添加到项目中&#xff1a; npm install highcharts npm install highcharts-vue// 我在实际使用时用上面两条命令安装后&#xff0c;引入时会报错 // 所以按照下面的示例中的版本安装的指定版本(vue版本为2.6.14)npm install highcharts7.1.3 npm install highchart…

串的模式匹配(简单匹配、KMP以及手工算next/nextval)

简单模式匹配 思路&#xff1a;主串和字串进行匹配&#xff0c;设置i、j、k&#xff08;主串、子串和匹配起始下标&#xff09;主串和子串一开始都是从第一个位置&#xff08;k i&#xff09;&#xff0c;若当前主串和子串匹配成功那么i、j&#xff0c;若匹配不成功k、j 1&am…

叶顺舟:手机SoC音频趋势洞察与端侧AI技术探讨 | 演讲嘉宾公布

后续将陆续揭秘更多演讲嘉宾&#xff01; 请持续关注&#xff01; 2024中国国际音频产业大会(GAS)将于2024年3.27 - 28日在上海张江科学会堂举办。大会将以“音无界&#xff0c;未来&#xff08;Audio&#xff0c; Future&#xff09;”为主题。大会由中国电子音响行业协会、上…

Python中的变量是什么类型?

一、 Python中的变量是什么类型&#xff1f; 在Python中&#xff0c;变量本身是没有类型的&#xff0c;变量可以指向任何类型的数据对象。这意味着你可以将一个整数赋值给一个变量&#xff0c;稍后又可以将一个字符串赋值给同一个变量。Python是一种动态类型语言&#xff0c;它…

利用卷积神经网络进行人脸识别

利用卷积神经网络&#xff08;Convolutional Neural Networks, CNNs&#xff09;进行人脸识别是计算机视觉领域的一个热门话题。下面是一个简化的指南&#xff0c;涵盖了从理论基础到实际应用的各个方面&#xff0c;可以作为你博文的基础内容。 理论基础 卷积神经网络简介&am…

22 OpenCV 直方图计算

文章目录 直方图概念split 通道分离函数calcHist 计算直方图normalize 归一化函数示例 直方图概念 上述直方图概念是基于图像像素值&#xff0c;其实对图像梯度、每个像素的角度、等一切图像的属性值&#xff0c;我们都可以建立直方图。这个才是直方图的概念真正意义&#xff0…

uni-app多次触发事件,防止重复点击

在uni-app中&#xff0c;经常会碰到多次触发事件&#xff0c;重复点击&#xff0c;导致造成业务出现问题。 虽然在后端可以进行操作&#xff0c;防止业务问题。那么前端该如何实现呢&#xff1f;1.在根目录下新建common文件并创建noclick.js文件 // 防止处理多次点击 function…

使用Python进行股票市场分析:基于历史数据的统计分析

一、引言 股票市场作为金融市场的重要组成部分&#xff0c;一直是投资者关注的焦点。通过对股票市场的历史数据进行统计分析&#xff0c;可以帮助我们更好地理解市场趋势&#xff0c;为投资决策提供依据。本文将介绍如何使用Python编程语言&#xff0c;对股票市场的历史数据进…

js中怎样添加、移出、插入、复制、创建?

在 JavaScript 中&#xff0c;可以使用以下方法来添加、移除、插入、复制和创建元素&#xff1a; 添加元素&#xff1a; 使用 appendChild() 方法将一个子元素添加到指定父元素的末尾。使用 insertBefore() 方法将一个子元素插入到指定父元素的指定位置之前。 移除元素&#xf…

HarmonyOS鸿蒙开发常用4种布局详细说明

介绍一下鸿蒙开发常用4种布局 1、线性布局 2、层叠布局 3、网格布局 4、列表布局 ​1. 线性布局&#xff08;Column/Row&#xff09; 线性布局&#xff08;LinearLayout&#xff09;是开发中最常用的布局&#xff0c;通过线性容器Row&#xff08;行&#xff09;和Column&…

软件杯 深度学习 python opencv 动物识别与检测

文章目录 0 前言1 深度学习实现动物识别与检测2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存…

网络原理(2)——TCP协议

目录 一、TCP协议段格式 二、确认应答 三、超时重传 TCP全称为&#xff1a;"传输控制协议 Transmission Control Protocol)"。协议如其名&#xff0c;要对数据的传输进行一个详细的控制。 一、TCP协议段格式 源 / 目的端口号&#xff1a;表示数据从哪个进程来&am…

【Web开发】CSS教学(超详细,满满的干货)

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;【Web开发】CSS教学(超详细,满满的干货) &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 CSS一. 什么是CSS?1.1 基本语法规范1.2 引入方式1.3 规范 二. CSS选…

C++中用什么函数开启新进程、线程?

在C中&#xff0c;创建新进程和线程通常需要使用特定的库或API。C标准库本身并不直接提供创建进程或线程的功能&#xff0c;但你可以使用操作系统提供的API或者第三方库来实现这些功能。 创建新进程 在C中创建新进程通常需要使用操作系统的API。在类Unix系统&#xff08;如Li…

详细聊一聊vue2和vue3的区别在哪里

Vue 3相对于Vue 2有许多重要的改进和新增功能,主要区别如下: Vue.js 3 带来了许多新特性和改进,其中一些主要的新特性包括: Composition API: Vue 3 引入了 Composition API,这是一种新的组织组件代码的方式,可以更灵活地组织逻辑代码,将相关逻辑归类到一个功能性单元,…

第三篇 - 概述- IAB受众和技术标准 - IAB视频广告标准《数字视频和有线电视广告格式指南》

第三篇 - 概述- IAB受众和技术标准​​​​​​​ - 我为什么要翻译介绍美国人工智能科技公司IAB技术标准系列&#xff08;2&#xff09; 本文目录 一、IAB技术实验室简介 二、概述及IAB受众 三、资源- IAB倡导的相关视频广告技术标准 四、案例分享-介绍一家数字化营销服务…

MySQL中出现‘max_allowed_packet‘ variable.如何解决

默认情况下&#xff0c;MySQL的max_allowed_packet参数可能设置得相对较小&#xff0c;这对于大多数常规操作来说足够了。但是&#xff0c;当你尝试执行包含大量数据的操作&#xff08;如大批量插入或大型查询&#xff09;时&#xff0c;可能会超过这个限制&#xff0c;从而导致…