900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > C和C++里的const常量 volatile修饰符的深层次说明

C和C++里的const常量 volatile修饰符的深层次说明

时间:2020-03-19 05:00:19

相关推荐

C和C++里的const常量 volatile修饰符的深层次说明

目录

一、写在前面

二、分析C和C++中const常量被修改后值的状态

2.1 C中const常量被修改后值的状态

2.2 C++中const常量被修改后值的状态

2.3C和C++中const常量被修改后值的状态分析结论

三、添加volatile修饰后,分析C和C++中const常量被修改后值的状态

3.1添加volatile修饰后,C中const常量被修改后值的状态

3.2添加volatile修饰后,C++中const常量被修改后值的状态

3.3添加volatile修饰后,C和C++中const常量被修改后值的状态分析结论

四、针对const常量在C和C++里分析volatile修饰前后值被修改的状态的结论

4.1 C中

4.2 C++中

五、原因

5.1 C中对const没有优化,C++存在const优化

5.2 局部const常量和全局const常量

5.3 借用评论区的说法

5.4 volatile优化

一、写在前面

1. 由于涉及到编译器层面,所以不同编译器结果可能不一样。本文使用的VS编译器。

2. 针对const修饰的整型常量:

const int local = 10;

3. 常量本质上也是一个变量,是变量就会有地址

因为直接修改const常量是不允许的(编译就会报错),所以只能通过指针修改其地址上的值来试验。如下:

这里注意下,用vs复制一个文件到另一个文件运行(比如这里程序都是一样的,一个在c运行,一个在cpp运行,复制过去不清理解决方案,运行的其实还是上一个程序),但是编译器选择不同时,需要重新生成解决方案,不然运行的还是上一次的程序。

4.分析的问题及结论

5.整体代码

c部分:

#include<stdio.h>#include<stdlib.h>#include<string.h>int main(void){const int local = 10;printf("修改前:\n");printf("修改前local本身的值 : %d \n", local);printf("修改前local的地址: %x \n", &local);int *ptr = (int*)&local;*ptr = 100;printf("修改后:\n");printf("修改后local本身的值: %d \n", local);printf("修改后local对应的内存空间中的值:: %d \n", *ptr);printf("修改后local的地址: %x \n", &local);printf("\n\n");printf("测试添加volatile修饰符:\n");/****** 添加volatile避免编译器的优化 ********/const volatile int local2 = 10;printf("修改前:\n");printf("修改前local2本身的值 : %d \n", local2);printf("修改前local的地址: %x \n", &local2);int *ptr2 = (int*)&local2;*ptr2 = 100;printf("修改后:\n");printf("修改后local2本身的值: %d \n", local2);printf("修改后local2对应的内存空间中的值:: %d \n", *ptr2);printf("修改后local2的地址: %x \n", &local2);return 0;}

输出结果:

C++部分

#include <iostream>#include<stdio.h>using namespace std;int main(){const int local = 10;printf("修改前:\n");printf("修改前local本身的值 : %d \n", local);printf("修改前local的地址: %x \n", &local);int *ptr = (int*)&local;*ptr = 100;printf("修改后:\n");printf("修改后local本身的值: %d \n", local);printf("修改后local对应的内存空间中的值:: %d \n", *ptr);printf("修改后local的地址: %x \n", &local);printf("\n\n");printf("测试添加volatile修饰符:\n");/****** 添加volatile避免编译器的优化 ********/const volatile int local2 = 10;printf("修改前:\n");printf("修改前local2本身的值 : %d \n", local2);printf("修改前local的地址: %x \n", &local2);int *ptr2 = (int*)&local2;*ptr2 = 100;printf("修改后:\n");printf("修改后local2本身的值: %d \n", local2);printf("修改后local2对应的内存空间中的值:: %d \n", *ptr2);printf("修改后local2的地址: %x \n", &local2);return 0;}

输出结果:

二、分析C和C++中const常量被修改后值的状态

这里讨论的值指常量本身的值(local)和常量对应的内存空间上的值(*ptr)。

2.1 C中const常量被修改后值的状态

代码如下:

#include<stdio.h>#include<stdlib.h>#include<string.h>int main(void){const int local = 10;printf("修改前:\n");printf("修改前local本身的值 : %d \n", local);printf("修改前local的地址: %x \n", &local);int *ptr = (int*)&local;*ptr = 100;printf("修改后:\n");printf("修改后local本身的值: %d \n", local);printf("修改后local对应的内存空间中的值:: %d \n", *ptr);printf("修改后local的地址: %x \n", &local);return 0;}

运行结果如下:

注意输出中选中区。

结论:通过指针的方式修改常量对应的内存空间中的值,常量本身的值(local)会被修改,常量对应的内存空间中的值(*ptr)也会被修改;

2.2 C++中const常量被修改后值的状态

代码如下:

#include <iostream>#include<stdio.h>using namespace std;/***为了节省时间 输出之间用的printf没用cout****/int main(){const int local = 10;printf("修改前:\n");printf("修改前local本身的值 : %d \n", local);printf("修改前local的地址: %x \n", &local);int *ptr = (int*)&local;*ptr = 100;printf("修改后:\n");printf("修改后local本身的值: %d \n", local);printf("修改后local对应的内存空间中的值:: %d \n", *ptr);printf("修改后local的地址: %x \n", &local);return 0;}

运行结果如下:

注意输出中选中区。

结论:通过指针的方式修改常量对应的内存空间中的值,常量本身的值(local)不会被修改,常量对应的内存空间中的值(*ptr)会被修改;

2.3C和C++中const常量被修改后值的状态分析结论

所以,局部const常量的值在C++和C里面是否能修改的结果是不一样的。在C里面常量本身的值和内存空间对应的值都会变,C++里常量本身发值不会变,但是对应的内存空间的值会变。原因在后面会说。

这里说的是局部,全局const是什么会在后面说。

三、添加volatile修饰后,分析C和C++中const常量被修改后值的状态

这里讨论的值指常量本身的值(local)和常量对应的内存空间上的值(*ptr)。

3.1添加volatile修饰后,C中const常量被修改后值的状态

代码如下:

#include<stdio.h>#include<stdlib.h>#include<string.h>int main(void){const int local = 10;printf("测试添加volatile修饰符:\n");/****** 添加volatile避免编译器的优化 ********/const volatile int local2 = 10;printf("修改前:\n");printf("修改前local2本身的值 : %d \n", local2);printf("修改前local的地址: %x \n", &local2);int *ptr2 = (int*)&local2;*ptr2 = 100;printf("修改后:\n");printf("修改后local2本身的值: %d \n", local2);printf("修改后local2对应的内存空间中的值:: %d \n", *ptr2);printf("修改后local2的地址: %x \n", &local2);return 0;}

运行结果如下:

注意输出中选中区。

结论:添加volatile修饰后,通过指针的方式修改常量对应的内存空间中的值,常量本身的值(local)会被修改,常量对应的内存空间中的值(*ptr)也会被修改;也就是说volatile修饰符在这里看不出是否跳过优化。

3.2添加volatile修饰后,C++中const常量被修改后值的状态

代码如下:

#include <iostream>#include<stdio.h>using namespace std;/***为了节省时间 输出之间用的printf没用cout****/int main(){const int local = 10;printf("测试添加volatile修饰符:\n");/****** 添加volatile避免编译器的优化 ********/const volatile int local2 = 10;printf("修改前:\n");printf("修改前local2本身的值 : %d \n", local2);printf("修改前local的地址: %x \n", &local2);int *ptr2 = (int*)&local2;*ptr2 = 100;printf("修改后:\n");printf("修改后local2本身的值: %d \n", local2);printf("修改后local2对应的内存空间中的值:: %d \n", *ptr2);printf("修改后local2的地址: %x \n", &local2);return 0;}

运行结果如下:

注意输出中选中区。

结论:添加volatile修饰后,通过指针的方式修改常量对应的内存空间中的值,常量本身的值(local)会被修改,常量对应的内存空间中的值(*ptr)也会被修改;也就是说volatile修饰符在这里成功避免编译器优化。

3.3添加volatile修饰后,C和C++中const常量被修改后值的状态分析结论

因为在C里面,const常量的值都能被修改,所以volatile无法提现;在C++里volatile成功避免了优化。

四、针对const常量在C和C++里分析volatile修饰前后值被修改的状态的结论

4.1 C中

无论是否进行volatile修饰,常量对应内存空间中的值(*ptr)都会被修改;

无论是否进行volatile修饰,常量本身的值(local)都会被修改。

4.2 C++中

无论是否进行volatile修饰,常量对应内存空间的值(*ptr)都会被修改;

如果不采用volatile修饰const常量,常量本身的值(local)不会被修改,volatile修饰后常量本身的值(local)会被修改。

五、原因

5.1 C中对const没有优化,C++存在const优化

c++中用const定义了一个常量后,不会分配一个空间给它,而是将其写入符号表(symbol table),这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

通过指针的方式修改常量对应的内存空间中的值时,这种修改不会影响到常量本身的值,因为用到该常量(local)的时候,编译器根本不会去进行内存空间的读取。这就是c++的常量折叠(constant folding),即将const常量放在符号表中,而并不给其分配内存,编译器直接进行替换优化。除非需要用到local的存储空间的时候,编译器迫不得已才会分配一个空间给local,但之后local的值仍旧从符号表中读取,不管local的存储空间中的值如何变化,都不会对常量local产生影响(即常量放在符号表,无论是否对其进行操作,常量本身的使用都是从符号表中去取。所以就算从内存空间对其进行了修改,内存空间的值变化了,但是常量的值还是从符号表中拿,值还是没变)。

示例:

int main(){const int local = 2;int* ptr = (int*)(&local);*ptr = 30;cout<<&local<<endl;cout<<ptr <<endl; cout<<local<<endl;cout<<*ptr <<endl;}

输出:

0x28ff080x28ff08230

在C中却不是这样,C没有constant folding的概念,用const定义一个常量的时候,编译器会直接开辟一个内存空间存放该常量,不会进行优化,所以从内存空间对其进行了修改,内存空间的值变化了,常量本身的值也就变化了(跟变量一样,只不过常量不能直接用常量名二次赋值或初始化)。

示例:

int main(){const int local = 2;int* ptr = (int*)(&local);*ptr = 30;printf("%x\n",&local);printf("%x\n",ptr);printf("%d\n",local);printf("%d\n",*ptr);return 0;}

输出:

28ff1828ff183030

5.2 局部const常量和全局const常量

1. 针对定义在函数内部(包括main函数)的const常量,不管是c还是c++,本质上都只是将其当成一个普通的局部变量来对待,都只是在栈上分配空间。所以const根本就不能起到阻止修改其内存空间的作用,一个合法的强制类型转换就可以轻松搞定。C++比C好的地方就在于使用了constant folding的机制,使得常量的值跟对应的内存空间无关,从而保护了该常量值。

2. 对于全局const常量(定义在main之外),在c和c++中如果我们仍然用int *ptr = (int*)(&local);这种方法来修改它内存中的值,编译时不会报错(这里看编译器,gcc不报错,vs编译时就会报错),但是运行时会报段错误,因为local是放在只读的全局数据区中,修改该区中的数据会引发段错误,也就是不能进行修改。

3. 总结来说,全局const常量放在常量只读区,C和C++里都没法修改;局部const常量放在栈上,能够通过操作内存的方式(int *ptr = (int*)(&local))修改其内存空间中的值。

注意这里局部const常量被修改的是常量对应内存空间的值,常量本身的值是否改变取决于是C还是C++,C++里有优化,常量本身的值不会变,而C没有优化,所以常量本身的值会被修改。

5.3 借用评论区的说法

在C中,const修饰的变量,是指不可修改的变量(旨在提示程序员)。如何使这个变量不可修改呢?在编译期间对这个变量进行检查,如果有语句试图修改这个变量,就报编译错误,使编译不通过。虽然如此,但仍然可以通过指针间接修改const变量的值(绕过编译器检查)来证明这是一个变量。作为函数参数,是限定函数只对该参数(这里的参数是引用类型,引用使用址传递效率高)进行读,而不允许写(避免误操作,因为修改参数引用会直接改变原数据)。

在C++中,常量就真的是常量了。

这也就是为什么我们说c里面常量只是只读,并不是真正的常量的原因。

5.4 volatile优化

volatile 确实能避免编译器优化。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。