[英]What is the difference between the const qualifier in C and the const qualifier in C++?
[英]Usage wise difference between const & volatile qualifier in C?
我已經經歷了問題的答案@ C中的const和volatile指針有什么區別? 我了解以下解釋:
const修飾符表示此代碼無法更改變量的值,但這並不意味着該值不能通過此代碼外部的方式進行更改。 但是,volatile說“此數據可能會被其他人更改”,因此編譯器將不對該數據做任何假設。
這意味着兩種類型的變量都可以通過外部事件進行更改。
但是,那么const和volatile在用法上的區別在哪里?
在C語言中,編譯器優化對const是否有效?
volatile和const在許多方面都不同,它們是兩個截然不同的功能。
像聲明const一樣聲明變量絕不會意味着“我希望在程序外部修改此變量”,但我不確定您從何處得到了這個想法。 如果您希望在代碼外修改const變量,則必須將其聲明為volatile const
否則編譯器可能會認為該變量從未更改。
默認情況下,普通const變量就像任何類型的變量一樣,它們根本無法被程序本身修改。
就像普通變量一樣,const變量的行為在很大程度上取決於聲明它們的范圍。 通常,它們在文件作用域中聲明,然后與具有靜態存儲持續時間的其他變量一樣起作用,除非它們(可能)保存在內存的不同部分。 如果在本地范圍內聲明它們,則在調用它們所駐留的函數時可能會不時更改。
因此,在很多情況下可以優化const變量。 一種常見的優化是“字符串池”,其中編譯器檢查相同的常量字符串文字是否在代碼中出現兩次,然后為它們使用相同的地址。 如果您希望從外部來源更改這些字符串,但沒有將它們聲明為易失性,則會收到奇怪的錯誤。
至於volatile變量,它們可以由外部源進行修改,但與const變量不同,它們也可以由程序進行修改。
具有const
限定類型的對象與您可以在程序中聲明的其他對象一樣,只是您無權對其進行修改。 底層對象可能會發生變化,例如通過別名進行更改,並且編譯器必須注意所有其他對象(如果可能發生了此類事件)。 例如
void toto(double const* pi, double* x) {
printf("%g %g\n", *pi, *x);
printf("%g %g\n", *pi, *x);
*x = 5.0;
printf("%g %g\n", *pi, *x);
}
在這里用toto(&a, &a)
類的東西調用toto
是完全可以toto(&a, &a)
因此在函數pi
和x
指向相同的內存。 對於第二個printf
,編譯器可以假定,因為在此同時它沒有存儲*pi
和*x
的值未更改。 但是對於第三個printf
它無法預知*pi
是否已更改,因此必須從內存中重新加載該值。
volatile
與此不同。
void tutu(double volatile* pi, double* x) {
printf("%g %g\n", *pi, *x);
printf("%g %g\n", *pi, *x);
}
在這里,對於第二個printf
,之前的編譯器可以假定*x
不變,但是對於*pi
它必須假定它可以具有並且必須從內存中重新加載。 volatile
用例在程序員的日常工作中很少見,他們主要關注的對象是
setjmp/longjmp
機制下 'const'告訴編譯器該值是永遠不會更改的,而不是由程序或其他任何人都不會更改。 當某些內容為const時,編譯器將相應地優化代碼,通常會將變量替換為代碼中的常量。 因此,即使外部更改,該程序也可能永遠不會知道。
相反,“易失性”告訴編譯器該變量可以隨時從外部更改,然后編譯器將不會執行優化操作(例如將var放入寄存器中),但始終會從內存中讀取該變量(以防萬一)改變。
演示const的示例
function1()
{
int i = 10;
function2(&i);
}
function2(int const *ptr) // usage of const
{
*ptr = 20; //will cause error; outside function can't be modify the value of i
}
揮發物的例子
function1()
{
while(1)
{
i = 20;
print(i);
}
}
function2()
{
i = 20;
while(1)
{
print(i);
}
}
考慮這兩個功能。 兩者似乎都是一樣的。 為了進行優化,編譯器將function1轉換為function2。 問題是,如果i的值被另一個線程更改,則兩個函數會變得不同,此時,循環打印i和另一個模塊的值會更改i的值。 因此我們永遠不會得到i的值20。
volatile用於通知編譯器不要優化變量。
const修飾符表示此代碼無法更改變量的值,但這並不意味着該值不能通過此代碼外部的方式進行更改。
應用const限定符有兩種不同的方法。
程序不能修改const限定對象 ,否則該程序具有未定義的行為。 const volatile
對象可以由OS /硬件/任何對象修改,但不能由程序分配。 為避免疑問,const對象是其定義使用const類型的對象。
指向const限定類型的指針可防止(在編譯時)通過該指針進行修改,但是指向同一對象的其他指針也可用於修改它。 如果對象本身不是const,則定義行為。 但是,編譯器仍然可以假設只有程序才能修改對象,考慮到OS /硬件/需要volatile
進行的任何修改。
指向非const限定類型的指針與指向const的指針完全相同,只要涉及到通過其他指針進行的修改。
但是,volatile說“此數據可能會被該程序中的代碼以外的其他東西更改”,因此,編譯器在優化時將不會對該數據做任何假設。
所以區別是:
#include <stdio.h>
void some_other_function(const int *);
int main() {
int a = 0;
int volatile b = 0;
int const c = 0;
int const *constptr = &a;
int *ptr = (int*) constptr;
printf("%d\n", a); // compiler can assume a == 0 at this point, and
// replace the code with puts("0") if it wants to
printf("%d\n", b); // compiler cannot assume a value for b, it's volatile[*]
some_other_function(constptr); // defined in another TU
printf("%d\n", a); // compiler can *no longer* assume that a == 0,
// it might have changed
*ptr = 1; // there's another example of a changing, legally
some_other_function(&c);
printf("%d\n", c); // compiler can assume c == 0 because c is const
}
[*]盡管我說它“不能假設一個值”,但可能是某些假設的C實現恰巧知道沒有OS或硬件機制通過任何需要volatile
檢測的方式來修改自動變量。 特別是在這種情況下,對b
引用都沒有轉義該函數。 如果是這樣,那么您可能會發現該實現實際上可以忽略此特定代碼中的volatile
,但是也許它“適當地”對待外部全局volatile
變量,因為它知道鏈接器提供了一種將它們映射到I / O端口地址的方法。管他呢。
在波紋管情況下,可以很容易地看出Volatile和Const之間的區別,
1)如果您將某個變量稱為Const,則可能無法通過程序進行修改。
2)如果您說的是volatile,它只是提示編譯器不要優化代碼,因為該值可能會從外部線程或其他程序中更改。
3)如果我們將變量定義為Const Volatile,則意味着該變量不能由同一程序修改,不能由編譯器優化,也可以由外部線程或程序修改。
例:
如果我寫下面的函數,
const freq = 10;
calfreq()
{
return (Const freq * 2);
}
在這種情況下,編譯器可以優化代碼以
return(20);
每時每刻。
但是在我的情況下,由於外部硬件/線程/程序的緣故,頻率值可能會發生變化。因此,如果我說的是Const Volatile,那么問題將得到解決。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.