当前位置: 首页 > news >正文

04C++const增强、枚举的增强

#include <iostream>

int main(void)
{
    //const定义常量--->const意味着只读
    const int a;
    int const b; // 第一个和第二个意思一样,代表一个常整数型
    
    const int *c;//第三个 c是一个指向常整型数的指针(所指向的内存数据不能被修改,但是本身可以修改)

    int * const d;//第四个 d常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)

    const int * const e;//第五个 e一个指向常整型的常指针(指针和它所指向的内存空间均不能被修改)

    return 0;
}

1.在C语言和C++中const的表现是不同的。

 

先看一下const基础知识。

 

对const的初级理解:const是定义常量,const意味着只读。

 

掌握const最基本的关键点是:指针变量和它所指向的内存空间变量是两个不同的概念,看const是修饰指针本身,还是修饰了指针所指向的内存空间。

 

举个例子:

 

 

定义一个结构体Teacher,在operatorTeacher01函数中,函数形参 const Teacher *pt,指针所指向的内存空间不能被修改,尝试修改指针所指向的内存空间 pt->age=10 ,就会编译错误。

 

在operatorTeacher02函数中,函数形参 Teacher *const pt,这句话就是说pt不能被修改,也就是这个指针不能被修改,但是指针所指向的内存空间可以被修改。

 

再看函数operatorTeacher03(const Teacher *const pt),根据形参,这个pt指针不能被修改,指针指向的内存空间也不能被修改。

 

运行程序:

 

 

红色代表可修改,蓝色代表不可修改

合理的利用const的好处:
(1)指针做函数参数,可以有效的提高代码可读性,减少bug;
(2)清楚的分清参数的输入和输出特性。

如:

int setTeacher_err( const Teacher *p)
const修改形参的时候,在利用形参不能修改指针所指向的内存空间。

2、C语言中的“冒牌货”

#include <stdio.h>
 
int main()
{
     const int a = 10;
     int *p = (int*)&a;
     print("a===>%d\n", a);//a=10
 
     *p = 11;
     print("a===>%d\n", a);//a=11
     //C中是只读变量,并不是真正的变量
     return 0;
}
#include <iostream>
using namespace std;

int main(void)
{
    const int a = 10;//a是真正的常量
    int *p = (int*)&a;
    cout << "a=" << a << endl;//;a = 10
    cout << "*p=" << *p << endl;//* p = 10

    *p = 20;//改变的是临时开辟的temp变量
    cout << "a=" << a << endl;//a = 10
    cout << "*p=" << *p << endl;//* p = 20
    return 0;
}

 

在.c文件中有程序:

 

 

编译就知道C语言编译器会报错,说变量a是常量,常量是不能当左值的,这样看来,好像a是定义的一个常量,不能修改!

 

修改程序:

 

 

定义一个指向变量a的指针变量p,通过指针修改指针p所指向位置的值,也就是a的值,编译运行:

 

 

我们发现a的值变成了20,被修改了!

 

也就是说我们通过指针间接修改了a的值,也就是说在c语言中const是个“冒牌货”。

 

同样的代码,我们运行在C++环境下:

 

 

 

发现运行结果跟C语言环境不同,在C++中并没有改变变量a的值。

 

所以,在C++中,const是一个真正的常量。

 

问题原因分析:

 

因为C++编译器对const做了加强,当C++编译器扫描到const常量声明时,它不会像C语言一样为const单独分配内存。

 

在C++中,int const a = 10;之后,C++编译器会将a放到一个符号表里面,符号表的存在形式:

 

我们定义常量a=10,key就是a,value就是10,这个值就是锁死了,不能变的,当使用a的时候,就会从符号表里面把10拿出来。

 

当对a变量取地址的时候,C++编译器会为a单独的开辟一块内存空间,p = (int *)&a;然后这个内存空间赋给指针p,就是p指向这个内存空间。

 

 

通过*p去间接修改的值,是这个新开辟的内存空间的值,而不是符号表中a的值,我们让*p=20、30、100…修改的都是新开辟内存空间中的值。我们可以通过打印*p去验证这两个区域并不是一回事。

 

 

 

结论:

 

C语言中的const变量

 

C语言中的const变量是只读变量,有自己的存储空间。

 

C++中的const变量

 

可能分配存储空间,也可能不分配存储空间;

 

当const常量为全局,并且需要在其它文件中使用时分配内存空间;

 

当使用&操作符取const常量的地址分配内存空间;

const是在编译器编译期间还是在执行阶段分配内存的?

答案:const是在编译器编译阶段分配内存的。

 

也就是说编译器扫描到const变量的时候就分配内存了(const变量分配内存条件参考笔记十七)。

 

通过程序验证:

 

 

 

我们知道变量a、b、c三个变量应该是连续压栈,我们打印出它们的地址,看一下b的地址是不是处于a和c之间,在a和c之间就是它们一块分配内存了,通过笔记十七已经知道对const变量取地址时才分配内存。

C++中的const修饰的是一个真正的常量

程序一:

我们知道数组的下标不能为变量,必须是一个确定的值。在C语言中看程序:

#define a 10

int main()

{

//第一部分

int c=20;

int d=10;

int arr[c+d];

//第二部分

//int const a=20;

//int const b=10;

//int arr[a+b];

return 0;

}

 

理所当然的编译错误,因为数组下标不能是变量。

 

在C++中看相同的程序。在C++中这点没有区别,数组下标不能为变量。同样会编译错误。

 

程序二:

继续在C语言中看程序修改:

//#define a 10

int main()

{

//第一部分

//int c=20;

//int d=10;

//int arr[c+d];

//第二部分

int const a=20;

int const b=10;

int arr[a+b];

return 0;

}

 

 

发现编译错误!错误原因跟修改前一致!

 

也就是说:C语言中const定义的a、b实质并不是常量!还是变量!所以称之为“常变量”

 

在C++中看相同的程序:

int main()

{

//第一部分

//int c=20;

//int d=10;

//int arr[c+d]; 

//第二部分

int const a=20;

int const b=10;

int arr[a+b];

return 0;

}

编译通过!!!

结论:

C++中的const修饰的是一个真正的常量,而不是C中变量(只读)。C++中在const修饰的常量编译期间,就已经确定下来了。

3、const 和 #define 

(1)C++中的const修饰的,是一个真正的常量,而不是C中变量(只读),在const修饰的常量编译期间,就已经确定下来了;

(2)C++中的const常量类似于宏定义:const int c = 5; ≈ #define c 5

C++中的const常量与宏定义不同:const常量是由编译器处理的,提供类型检查和作用域检查;宏定义由预处理器处理,单纯的文本替换。

C语言中的const变量
    C语言中const变量是只读变量,有自己的存储空间
C++中的const常量
    可能分配存储空间,也可能不分配存储空间;
    当const常量为全局,并且需要在其它文件中使用,会分配存储空间;
    当使用&操作符,取const常量的地址时,会分配存储空间;
    当const int &a = 10; const修饰引用时,也会分配存储空间。 

const 和 #define 的对比

第一,const与#define的相同点

C++中的const常量类似于宏定义

const int c = 5 ≈ #define c 5

const是用来替换#define的一个手段。

//程序一:
int main()
{
const int a=10;
printf("a=%d\n",a);
return 0;
}
//程序二:
#define a 10
int main()
{
//const int a=10;
printf("a=%d\n",a);
return 0;
}

这两个程序运行结果都是一样的,都是定义一个常量a,注意#define最后是没有分号的。下面这两个程序也说明了这一点:

#define a 10
#define b 10
int main()

{

int arr[a+b];

system("pause");

return 0;

}

编译成功!

#define a 10

#define b 10

int main()

{

int arr[a+b];

return 0;

}

编译成功!

 

第二,const与#define的不同点

 

看程序:

 

E:\Users\lsj\Desktop\7.png

 

现在a是一个宏定义,我们知道宏定义,凡是变量a所在的地方预处理编译器都进行替换,也就是将a替换为10。因此在函数fun1中定义的a在函数fun2中是可以使用的,也就是说宏定义是没有作用域检查的。运行可以通过。

那么如果想将a的作用域限制在函数fun1中,可以使用“卸载宏”或者称作“取消宏” #undef 来达到目的。

#undef a —— 此处往下取消a的宏定义;

#undef —— 此处往下取消所有宏定义。

在看看const作用域检查,我们在fun1中定义变量b,其作用域就局限在fun1函数中了,在fun2函数中是不可用的,可以取消 //printf("b=%d\n",b); 的注释,发现编译时错误的!

结论:

C++中的const常量与宏定义不同

const常量是由编译器处理的,提供类型检查和作用域检查;

宏定义由预处理器处理,单纯的文本替换。

4、真正的枚举

C语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而C++中枚举变量, 只能用被枚举出来的元素初始化。

#include <iostream>
using namespace std;

enum season{spring,summer,autumn,winter};

int main(void)
{
    enum season s = spring;
    //s = 0;    //error,但是C语言可以通过
    s = summer;
    cout << "s=" << s << endl;    //1
    return 0;
}

 

 https://feng.ifeng.com/author/948426

转载于:https://www.cnblogs.com/xinmomoyan/p/10605749.html

相关文章:

  • 阿里云API、SDK和CLI应用实践方案
  • 带你开发类似Pokemon Go的AR游戏
  • Ubuntu18.04安装netstat
  • 「实战篇」开源项目docker化运维部署-搭建mysql集群(四)
  • 同城艺龙code
  • main.js中封装全局登录函数
  • Greenplum 5.16.0初探
  • Gym - 100920E 2010-2011 OpenCup IX Onsite, II Yandex Summer School E.Paint 状压DP
  • 第三章:回收方法区
  • Kubernetes Ingress 日志分析与监控的最佳实践
  • MAYA安装未完成,某些产品无法安装的解决方法
  • DB
  • Java的新项目学成在线笔记-day7(三)
  • MySQL优化技巧
  • maven maven上传jar到nexus本地仓库
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • ESLint简单操作
  • Hibernate最全面试题
  • IOS评论框不贴底(ios12新bug)
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • java中具有继承关系的类及其对象初始化顺序
  • JS函数式编程 数组部分风格 ES6版
  • React的组件模式
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 测试如何在敏捷团队中工作?
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 在Unity中实现一个简单的消息管理器
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (k8s中)docker netty OOM问题记录
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (十六)串口UART
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (一一四)第九章编程练习
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .Mobi域名介绍
  • .mysql secret在哪_MySQL如何使用索引
  • .net Application的目录
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .net mvc 获取url中controller和action
  • .net操作Excel出错解决
  • .NET分布式缓存Memcached从入门到实战
  • .NET设计模式(2):单件模式(Singleton Pattern)
  • @RequestMapping用法详解
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [Avalon] Avalon中的Conditional Formatting.