C和C++里的const常量、volatile修饰符的深层次说明
目錄
一、寫在前面
二、分析C和C++中const常量被修改后值的狀態
2.1 C中const常量被修改后值的狀態
?2.2 C++中const常量被修改后值的狀態
2.3?C和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.分析的問題及結論
| C環境下 | 會被修改 | 會被修改 |
| C++環境下 | 不會被修改 | 會被修改 |
| C環境下 | 會被修改 | 會被修改 |
| C++環境下 | 會被修改 | 會被修改 |
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.3?C和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產生影響(即常量放在符號表,無論是否對其進行操作,常量本身的使用都是從符號表中去取。所以就算從內存空間對其進行了修改,內存空間的值變化了,但是常量的值還是從符號表中拿,值還是沒變)。
示例:
輸出:
在C中卻不是這樣,C沒有constant folding的概念,用const定義一個常量的時候,編譯器會直接開辟一個內存空間存放該常量,不會進行優化,所以從內存空間對其進行了修改,內存空間的值變化了,常量本身的值也就變化了(跟變量一樣,只不過常量不能直接用常量名二次賦值或初始化)。
示例:
輸出:
28ff18 28ff18 30 305.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 確實能避免編譯器優化。
總結
以上是生活随笔為你收集整理的C和C++里的const常量、volatile修饰符的深层次说明的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32----SysTick实现监控
- 下一篇: ps如何修改图片大小尺寸_PS常用操作