const声明的是常量,常量基本上只能读不能写。其实x也是能写的,但他只是在x初始化的时候完成了写操作。

int main()
{const int x=4;//x是const int型。在初始化时写入4。//int const x=4;//这样写和上边是完全等价的。std::cout<<x<<“\n”;}

常量这个概念是编译期的概念,比如说我们声明一个变量,声明一个变量编译器是

-------------------------------------涉及指针的常量------------------------------

指针是一种间接类型,一个指针通常涉及到两块内存,第一块内存是这个指针内部所保留的地址,第二块内存是这个地址所指向的对象所保留的数据;这是一个指针,但是常量的本质就是说限制我在这一块内存里边的东西呢不能够被修改,由于一个指针通常来讲他会涉及到两块内存,因此本质上来讲呢就是要说明是哪一块内存不能被修改,比如下图中的0x1234不能被修改还是24不能被修改,所以就需要在这个指针里边进行相应的设计了。

第一种限制:

#include<iostream>int main()
{int x=4;int* const ptr=&x;//如果使用的是int* const ptr的话基本上表示ptr他这个指针不能修改,即ptr他只能指向x,不能指向其他的东西。int y=3;ptr=&y;//如果这样写的话编译的时候是会报错的,因为我们的ptr他是一个常量指针,常量指针就是他指向了一个对象那么他就不能再指向其他的对象了。如果我们把const删除掉,他是可以正常编译运行的。}

上述是第一种限制,限制的是指针里边的这一个数据不能修改。即写为int* const ptr=&x;表示第一块内存里边的东西不可以改变,但是第一块内存对应指向的第二块内存里边的东西可以改变。

第二种限制:

#include<iostream>int main()
{int x=4;const int* ptr=&x;//表示ptr他指向了x,但是我们不能使用Ptr来改变x的值。这句话表示我这个ptr里边的内容可以发生改变,但是如果ptr指向了某一个东西,这个东西是不能发生改变的,//*ptr=3;//这样写编译的时候系统会报错,因为我们不能使用ptr来改变x的值。//反过来按照下边写是可以的int y=3;ptr=&y;//因为我们并没有说限制ptr他是一个常量指针,}

第二种限制不是限制指针里边的这一个数据不能修改,限制的是指针不能去修改他指向的这个数据的内容。即写为const int* ptr=&x;表示第一块内存里边的东西可以改变,但是第一块内存对应指向的第二块内存里边的东西不能改变。

解引用的三种情况:

第一:

int* ptr1=...;
*ptr1;//对ptr1进行解引用,那么*ptr1的类型是int型(即int* 去掉*就是int类型。)
int x=4;
int* const ptr3=&x;
*ptr3;//他的类型是int(即int*去掉*);是int我们就可以改变他的值。
int x=4;
const int* ptr2=&x;
*ptr2;//他的类型是const int;所以就不能通过*ptr2来改变他其中包含的数据的值。

PS:总结如何判断ptr的值他到底是他自己的值不能改变还是说指向的值不能改变呢?基本上来讲我们可以在*这个地方画一条竖线,如果这个const出现在这个*的右边的话,表示的是这个指针本身不能发生改变;如果这个const出现在这个*的左边的话,表示ptr指向的内容不能发生改变(即地址可以变但是地址指向的内容不能改变)。(注意此处说的是const出现在*的最左端,不是说const出现在整个变量声明的最左端,即如果我们写成int const*ptr的话和const int* ptr没有区别)

顶层常量(top-level const):只是限制这个指针本身不能修改。(即对应int* const ptr;)

底层常量():限制的是这个指针指向的内容不能被修改。(即对应const int* ptr;)

即是顶层常量也是底层常量(const int* const p):(因为从*那里画上一道的话表示左边p首先不能被修改,其次p指向的内容不能修改)

常量指针(就是指向常量的指针)可以指向变量:

下边是将int*类型转化为const int*类型,是可行的。

#include<iostream>int main()
{int x=4;//x是一个变量//&x;//对x取地址,x的类型就变成了int*(即&x;//这个东西的类型是int*)const int* ptr=&x;//这一行本质上是ptr的一个构造,这个构造是通过拷贝构造函数来完成的;使用等号,等号的左边是const int*,等号的右边是一个int*;这一行是一个赋值是一个初始化,为什么这一行可以正常编译是因为在初始化的时候进行了隐式的类型转换,隐式的类型转换是把int*转换成了const int*;转化为const int*之后把这个值来赋值给ptr。//这种转换int*到const int*是合理的,是增加了限制,原先的int*我们可以利用它来读写所指向的这样的int的值,那么我们的const int*我们就只能读不能写了,这样就相当于说我们增加了限制,这种增加限制是合理的;但是反过来如果想从const int*隐式的转化为int*是不行的,
}

下边是将const int*类型转化为int*类型,是不行的。

#include<iostream>int main()
{const int x=4;//x是一个变量&x;//这个东西的类型是const int*int* ptr=&x;//ptr是int*
}

但是如果是const int*赋值给const int*是可以的。因为不会涉及到任何的类型转换。

#include<iostream>int main()
{const int x=4;//x是一个变量&x;//这个东西的类型是const int*const int* ptr=&x;//ptr是int*
}

-----------------------上述是涉及到指针的时候常量会产生的一些影响-----------------------------上述是指针有常量这样的一个概念

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

----------------------------------------------引用也是有常量的概念的---------------------

引用和指针是很相似的东西,它的底层也是使用指针来实现的,只不过他引用了额外的限制。

定义常量的引用:(常量引用也是可以绑定变量的,具体原因和常量指针是相同的,)

int main()
{int x=3;const int& ref=x;
}
//这样写是增加了它的限制,原因是原先是可读可写,现在呢我只能读不能写。但是如果写成下边的就会出现编译错误:int main()
{const int x=3;int& ref=x;//这个是int引用
}
//这样写相当于放松了限制,程序编译是会报错的。

const引用的用途:基本上const引用

int main()
{int x=3;const int& ref=x;//ref只能指向x,因为引用他所绑定的一个元素或者说他到底指向的是哪一个元素的别名,这个事情实际上是在初始化的时候就确定了,然后呢此处又是只能读不能改,那么此处为什么要这么写呢?这么写的原因是什么呢?此处的常量引用他连指向其他的对象他都指不了了。所以为什么要引入常量引用这样的一个概念呢?主要的用途是用于函数的形参。
const int* ptr=&x;//现在的ptr可以指向x,那么在后来的程序中ptr还能够指向其他的对象。
}

常量引用主要用途用作函数的形参:


//1、在mian里调用,在fun()里使用的是一个int类型的对象。int类型或者其他的一些内建数据类型,基本上都有一个特点他的尺寸比较小,尺寸比较小意味着我去进行数据的拷贝,拷贝并不需要耗费多大的资源,但是如果说我们在下边的例子中再定义一个结构体的话。void fun(int param)
{}
int main()
{int x=3;fun(x);//在调用的时候会把x拷贝给param。
}//2、尺寸很大这种拷贝呢,首先系统需要开辟一大块内存,一大块额外的内存,来去准备拷贝,接下来他要花费很多的这种计算资源,把x里边所有的内容拷贝给param才能实现fun的调用,才能实现fun的调用,整个的这样的过程是非常耗时耗力的;我们实际上可以对这个结构体进行一些限制,比如这个结构体不支持拷贝(比如说这个结构体表示了一个独占的文件,我不希望他拷贝两份),如果这个结构体不支持复制的话我们整个的代码是无法通过编译的,这个时候一种解决办法是传param的指针.
Struct Str
{//这个结构体里边包含了很多很多的内容,所以尺寸很大。 };
void fun(Str param)
{}
int main()
{Str x;fun(x);//在调用的时候会把x这个结构体拷贝给param。
}//3、传param的指针
//但是传指针其实有一个问题,在fun函数内部他就要判断指针的有效性,如果在其他的地方我调用fun传了一个nullptr即fun(nullptr);我传了一个指向0的空指针,或者他指向的指针本身就是一个非法的,这个时候fun的处理相对来说就比较麻烦一些,就是我们必须要在fun里边写一个代码判断一下param是否有效,如果param不有效,就是他是无效的话我们就要进行一个相应的处理,比如抛出异常或者打出错误信息终止程序。还有一种解决办法就是传引用Struct Str
{//这个结构体里边包含了很多很多的内容,所以尺寸很大。 };
void fun(Str* param)
{}
int main()
{Str x;fun(&x);//在调用的时候会把x这个结构体拷贝给param。
}//4、传引用;传引用的话至少不需要担心这个空指针的问题,因为param他一定是绑定了一个对象,指针和引用他在底层的实现都是类似的,基本上来讲按照下边方式传递的话,这个fun他在底层还是使用指针实现的,这样调用fun的话本质上来讲他还是一个地址的拷贝,相对于来讲比较cheap,而且也不会出现不能拷贝的情况。但是如果传引用的话他会有另外的一个问题,实际上Str& param这个东西它是可以修改的,如果在结构体里边使用Param=...,我们不小心对他进行了修改,实际上引用是对象的别名,我们无论从概念上来讲还是从它的底层实现来讲我们这种修改都会直接影响到这个x的值,我们很多情况下是不希望这种情况产生的。这种情况下我们就可以进行修改fun函数里边的参数,参数为const Str& param.
Struct Str
{//这个结构体里边包含了很多很多的内容,所以尺寸很大。 };
void fun(Str& param)
{}
int main()
{Str x;fun(x);//在调用的时候会把x这个结构体拷贝给param。
}//5、构造常量引用作为形参;使用const进行修饰如果我们在fun函数里边进行修改的话编译器就会直接报错。
Struct Str
{//这个结构体里边包含了很多很多的内容,所以尺寸很大。 };
void fun(const Str& param)//构造了一个常量引用
{}
int main()
{Str x;fun(x);//在调用的时候会把x这个结构体拷贝给param。
}

int占了4个字节,地址占了8个字节。

int main()
{int x=3;int& ref=x;int& ref=3;//这样是错的const int& ref=3;//这样是对的,常量引用可以绑定到字面值}int main()
{int x;std::cin>>x;
//在这个里边定义了两个常量一个叫做y1;一个叫做y2;但是y1和y2是有本质上的区别的。const int y1=x;//y1的值是在运行期确定的,根据运行期输入的不同y1的值不同。const int y2=3;//但是y2的值是在编译期确定的。}
程序运行的流程是:是先编译出来才运行的。

constexpr声明常量表达式:

使用constexpr来声明常量表达式。常量表达式的一个作用就是用来声明这种编译期的常量。所以上述代码可以修改:

int main()
{int x;std::cin>>x;const int y1=x;//y1的值是在运行期确定的,根据运行期输入的不同y1的值不同。constexpr int y2=3;//但是y2的值是在编译期确定的。
}

const int y2=3;与 constexpr int y2=3;他的区别:

constexpr int y2=3;//相当于更明确的向编译器表示我这个东西就是编译器的常量,他就是等于3,基本上来讲编译器就可以完全的放心用这个信息来进行后续的优化。如果我们声明了一个常量表达式那么y2的类型是const int(注意y2的类型不是constexpr int型,因为constexpr是一个关键字,这个关键字实际上更像是一种指示符,就是限定符,他不是一个类型的组成部分,y2的类型仍然是const int;此处使用constexpr实际上是为了说它不仅仅是一个常量,同时它的初始值是在编译期被确定了)

const int y2=3;//这样写其实是还是有办法去修改y2的值的。

------常量表达式指针:

int main()
{constexpr const int* ptr=nullptr;// constexpr const int*这个首先说明了ptr是一个指针,这个指针指向const int;ptr的类型是它本身是一个常量表达式,就是常量表达式constexpr它本身是修饰ptr的,不是修饰const int*;所以ptr他的类型是const int* constconstexpr const char* ptr2="123";}

查看类型,以及判断两者的类型是否一致:一致的话输出1,不一致的话输出0;

#include<iostream>
#include<type_traits>int main()
{constexpr const int* ptr=nullptr;std::cout<<std::is_same_v<decltype(ptr),const int* const> <<std::endl;//为什么ptr的类型是const int* const不是const int*类型啊?你把这个翻译成英语来读就比较清楚了,a const pointer to a const int。
}

constexpr (const int* ptr)//ptr 是一个 int* 它是 const 的 constexpr 说的是这个整体又是一个const的。constexpr会给你在中间加个const,在int*和prt中间加一个const。

C++:const的使用(普通常量、指针、引用)相关推荐

  1. 【C++---const引用】数组进行指针引用传递给函数error: non-const lvalue reference of type ‘int*‘ to an rvalue

    目录 原因 类型转换 手动转换 自动转换 关于临时量 关于常量引用(const的引用) const引用的对象不是不能被修改了吗,这里为什么被修改了? 不是说是const引用吗?为什么又变成了引用所绑定 ...

  2. const修饰的指针常量和常量指针

    const char * p   指的是:p 是一个指针(变量),它指向一个常量字符(const char) char * const p   指的是:p 是一个常量指针(const p),它指向一个 ...

  3. 【C 语言】const 关键字用法 ( 常量指针 - const 在 * 左边 - 修饰数据类型 - 内存不变 | 指针常量 - const 在 * 右边 - 修饰变量 - 指针不变 )

    文章目录 一.const 普通用法 二.常量指针 ( 指向 常量 的指针 | 被指向的内存不能被修改 ) 三.指针常量 ( 指针不能被修改 ) 三.指向 常量 的 指针常量 四.const 在 * 左 ...

  4. C++ 笔记(27)— 指针变量、数组和指针、指针数组、数组指针、指针常量与常量指针

    1. 什么是指针变量? 指针变量是专门有一个变量来存放指针. int main(int argc, char *argv[]) {int a = 10;int *p = &a; //通过取地址 ...

  5. C语言程序设计 | 指针(二):常量指针和指针常量、数组参数和指针参数、函数指针数组

    指针的进阶(二)目录: 常量指针和指针常量 数组参数和指针参数 函数指针数组 常量指针和指针常量 在我们日常中,经常会用到一个关键字const const是一个C语言(ANSI C)的关键字,具有着举 ...

  6. 理清C++常量指针和指针常量这团乱麻

    写在前面: 与其说C++中的常量指针和指针常量是一块很有嚼头的语法糖,不如说它是一块相当难啃的骨头.其实本来没什么,这无非是const int *p与int* const p的区别, 但一涉及到起名字 ...

  7. C/C++中的常量指针与指针常量

    常量指针 常量指针是指向常量的指针,指针指向的内存地址的内容是不可修改的. 常量指针定义"const int *p=&a;"告诉编译器,*p是常量,不能将*p作为左值进行操 ...

  8. 常量指针、指针常量以及指向常量的指针常量

    三个名词虽然非常绕嘴,不过说的非常准确.用中国话的语义分析就可以很方便地把三个概念区分开.  一)常量指针. 常量是形容词,指针是名词,以指针为中心的一个偏正结构短语.这样看,常量指针本质是指针,常量 ...

  9. 指针常量、常量指针和常量指针常量

    1.const可以存在的位置 1)指针常量 int *const ptr; 2)常量指针 const int *ptr; int const *ptr; 3)常量指针常量 const int *con ...

  10. C++ Primer P0~P72 字面值常量、引用指针和const、constexpr、decltype

    看第一的章还觉得轻松,结果第二章开始就多了很多没学过的新内容,还是得做笔记. 字面值常量 形如42.'h'."str" 这类以字面形式存在,在编译时就能确定类型的值被称为字面值常量 ...

最新文章

  1. c语言可视化_这些算法可视化网站助你轻松学算法
  2. FCKeditor使用详解
  3. 学习WINDOWS内核好书
  4. String与Date转换
  5. 分享篇--esp32直连天猫精灵
  6. 天鹅给癞蛤蟆的回信[转贴]
  7. 为什么mysql与eclipse_mysql的用户名和密码都是正确的,但是eclipse启动说不对
  8. python如何搜索关键字_Python遍历目录和搜索文件中的关键字
  9. 如何在C预处理器中可靠地检测Mac OS X,iOS,Linux,Windows? [重复]
  10. cocos2d-x 通过JNI实现c/c++和Android的java层函数互调 .
  11. Helm 3 完整教程(十):Helm 函数讲解(4)加密函数、编码和解码函数
  12. 动态包含与静态包含的区别
  13. 动态载入.ascx用户控件
  14. java 生成pdf 分页_java根据模板动态生成PDF实例
  15. cisco思科模拟器中断translating域名翻译快捷键
  16. 典型相关分析(Canonical correlation analysis)(四): 中国城市竞争力与基础设施的相关分析
  17. 【编程不良人】SpringSecurity实战学习笔记04---RememberMe
  18. 想做抖音手工号,没有思路创意怎么办?分析大V总结经验
  19. 短信的发送(SMS)的发送
  20. 2021年要毕业了,有必要考研吗?工作经验重要还是学历重要 ?

热门文章

  1. 2021年大数据基础(一):大数据概念
  2. Centos7 下安装python3及卸载
  3. CCF CSP 201609-2 火车购票
  4. python之路-day11-迭代器闭包
  5. 百度地图轨迹回放,自定义路书,边走边画线
  6. 用JavaScript和CSS实现“在页面中水平和垂直居中”的时钟
  7. 使程序在后台执行,并将日志输出至文件
  8. Linux命令行与命令
  9. sqlserver trigger
  10. Android应用系列:完美运行GIF格式的ImageView(附源码)