C語言之const和volatile"究極"學習

2021-01-17 OFweek維科網

關於const的用法,現在大概前前後後應該寫了有兩篇文章,以前學習的時候,用法體會不是那麼深刻,為啥這麼說呢,因為在學習c++的時候,會發現const關鍵字有新的玩法,關於這個新的玩法,大家可以去看最近學習總結寫的c++文章專輯。

一、const的用法:

1、const只讀變量:

const修飾的變量是只讀的,本質上還是變量

const修飾的局部變量在棧上分配空間

const修飾的全局變量在全局數據區分配空間

const只在編譯期有用,在運行期沒有用

註:const修飾的變量不是真的常量,它只是告訴編譯器該變量不能出現在賦值符號的左邊

2、const全局變量的分歧:

在現代c語言編譯器中,修改const全局變量將導致程序崩潰

標準c語言編譯器不會將const修飾的全局變量存儲於只讀存儲區中,而是存儲於可修改的全局數據區,其值依然可以改變

3、代碼示例:

(1)只讀變量代碼示例:

#include <stdio.h>
int main()

  const int a =10;
  printf("a = %d",a);
  a=20;
  printf("a = %d",a);
 
 return 0;

運行結果:

test.c: In function 『main』:
test.c:8:4: error: assignment of read-only variable 『a』
   a=20;
   ^

註解:顯示這個結果很正常,變量a被const修飾了,它就成了只讀的。

(2)如果對變量a的值進行修改:

#include <stdio.h>
int main()

  const int a =10;
  int *p =(int *) &a;
  printf("a = %d",a);
  *p=20;
  printf("a = %d",a);
 return 0;

運行結果:

root@txp-virtual-machine:/home/txp# ./a.out
a = 10
a = 20

註解:通過指針的方式,就能夠把a的值進行修改,這也論證了「const修飾的變量是只讀的,本質上還是變量」這句話

(3)const修飾全局變量:

代碼版本一

#include <stdio.h>
const int b = 40;
int main()

 

  printf("b = %d",b);
  b=20;
  printf("b = %d",b);
 return 0;

輸出結果:

root@txp-virtual-machine:/home/txp# gcc test.c
test.c: In function 『main』:
test.c:10:4: error: assignment of read-only variable 『b』
   b=20;
   ^

註解:跟const修飾棧上的變量用法一樣

代碼版本二

#include <stdio.h>
const int b = 40;
int main()

  int *p =(int *) &b;
  printf("b = %d",b);
  *p=20;
  printf("b = %d",b);
 return 0;

運行結果:

root@txp-virtual-machine:/home/txp# ./a.out
b = 40
Segmentation fault (core dumped)

註解:這裡出現了段錯誤,這也驗證了我們上面所說的「修改const全局變量將導致程序崩潰」。

同時為了驗證「標準c語言編譯器不會將const修飾的全局變量存儲於只讀存儲區中,而是存儲於可修改的全局數據區,其值依然可以改變」這句話,我把這段代碼放到dev c++上進行試驗:

說明:我這個版本的編譯器支持標準c語言,所以沒導致程序崩潰,能夠正常運行

4、const的本質

c語言中的const使得變量具有隻讀屬性

現代c編譯器中的const將具有全局生命周期的變量存儲於只讀存儲區,不是放在全局數據區

註:const不能定義真正意義上的常量;同時這裡注意static關鍵字修飾的變量,它的生命周期和全局變量一樣。

代碼示例:

#include <stdio.h>
const int Array[5] = {0};
void fun(int *p,int v)

  *p=v;

int main()

   int const i =1;
   const static int j =2;
   int const array[5] = {0};
   fun((int *)&i,1);
   fun((int *)&j,2);
   fun((int *)&array[2],3);
   fun((int *)&Array[1],4);
  return 0;

輸出結果:

root@txp-virtual-machine:/home/txp# ./a.out
Segmentation fault (core dumped)

註解:這裡會有段錯誤,錯誤出現在const+static修飾的j變量對其進行修改,還有const修飾的全局數組。

5、const修飾函數參數和返回值

const修飾函數參數表示在函數體內不希望改變參數的值

const修飾函數返回值表示返回值不可改變,多用於返回指針的情形

在c語言中的字符串字面量存儲於只讀存儲區中,在程序中需要使用const char* 指針,例如:

const char * s = "TXP嵌入式";//字符串字面量

代碼示例:

#include <stdio.h>
const char*fun(const int i)

  i=8;
   return "TXP";

int main()

  const char * p=fun(0);
   printf("%s",p);
   p[1]='_';
   printf("%s",p);
   return 0;

輸出結果:

root@txp-virtual-machine:/home/txp# gcc test.c
test.c: In function 『fun』:
test.c:5:4: error: assignment of read-only parameter 『i』
   i=8;
   ^
test.c: In function 『main』:
test.c:12:5: error: assignment of read-only location 『*(p + 1u)』
    p[1]='_';
    ^

註解:上面這樣寫,肯定有問題。

代碼進化:

#include <stdio.h>
const char*fun(const int i)

//   i=8;
   return "TXP";

int main()

  const char * p=fun(0);
   printf("%s",p);
 //  p[1]='_';
  // printf("%s",p);
   return 0;

輸出結果:

root@txp-virtual-machine:/home/txp# ./a.out
TXP

二、volatile的用法

老實說,這個關鍵字在面試題目裡面經常會出現,但是平時學習的時候,如果你沒有真正理解這其中的含義,在筆試的時候,腦袋裡面可能依稀是記得有那麼幾個結論,但是有時候吧,一緊張就把結論給忘了,也不是不可能,所以說,咋們還是老實一點,得真正把它原理搞明白才行,這樣上來戰場就不怕了,以後寫代碼也就少一點bug。

1、volatile的常用結論(volatile英文本意就是易變的意思)

這裡我先給結論,然後再給一個例子,把這個例子的講明白,所有結論就都明白了。

volatile可理解為「編譯器警告指示字」

volatile告訴編譯器必須每次去內存中取變量值

volatile主要修飾可能被多個線程訪問的變量

volatile也可以修飾可能被未知因素更改的變量

volatile可以修飾一個中斷子程序中會訪問到的非自動變量

2、分析原理

大家可能平時在博客學習,都會發現講解編譯器優化的,然後加了volatile關鍵來修飾變量,就告訴編譯器不要去優化這個變量了,那麼這裡的優化到底是什麼意思呢?

從字面上來理解優化兩個字,意思就是最優值(變量的值不會改變),這裡我用一個簡單代碼來說明一下:

#include <stdio.h>
int main()

   int a =1;//volatile int a =0;
   
   while(a)
   {
   
   }

說明:上面的代碼,如果變量a沒有加volatile修飾的話,編譯器就會優化它(也就是a的值一直不變),所以while就一直死循環;然後我如果加了volatile來修飾的話,編譯器就不會去優化變量a,不優化的意思就是說,變量a的值可能就會改變,while就不會一直死循環。

當然這裡為了好理解,我說的不是很專業,沒有從寄存器和內存的角度去說。(我也不想那麼去講解,簡單理解了就行)

總之一句話:上面的結論中,volatile修飾的都是變量,變量就可能改變,不會被編譯器優化;只是說我們上面的結論應用場景不同而已。

三、總結

const使得變量具有隻讀屬性

const不能定義真正意義上的常量

const將具有全局生命周期的變量存儲於只讀存儲區

volatile強制編譯器減少優化,必須每次從內存中取值

好了,今天的分享就到這裡,如果文章中有錯誤或者不理解的地方,可以交流互動,一起進步。我是txp,下期見!

相關焦點

  • C語言中const關鍵字的妙用總結
    請看正文學習了多年的C語言,你對在編程實踐中你對const的使用是否有困惑呢?今天就給大家一起來探討總結,期望能夠大家解惑並提供參考。C語言中const關鍵字是constant的縮寫,通常翻譯為常量、常數等,有些朋友一看到const關鍵字馬上就想到了常量。事實上在C語言中const功能很強大,它可以修飾變量、數組、指針、函數參數等。
  • C/C+編程筆記:C語言編程面試常見問題,全是經典題!
    C語言的主要特徵是什麼? C是一種過程語言。C語言的主要功能包括對內存的低級訪問,簡單的關鍵字集和簡潔的樣式。這些功能使其適用於諸如作業系統或編譯器開發之類的系統編程。 i ++和++ i有什麼區別? 1)表達式「 i ++」返回舊值,然後遞增i。
  • 快速上手系列-C語言之基礎篇(一)
    另外,相對於其他高級語言,比如C++、java、C#等來說,C語言編寫的代碼運行效率要高。在介紹C語言的數據類型和基本語法之前,先說說C程序結構和C文件結構。,是由一個、且只能有一個main()函數(又稱主函數)和若干個其他函數結合而成,或僅由一個main()函數構成;接下來看看C語言程序運行結構:基本的三種程序結構為順序結構、選擇結構和循環結構。
  • DSP編程技巧之17---非常「關鍵」的關鍵字
    C28x的編譯器支持所有的標準C89的關鍵字,包括const、volatile和register,標準的C99關鍵字,包括inline和restrict,以及支持TI自定義的擴展關鍵字__cregister、__asm,和__interrupt;對於FPU的操作,還支持restrict關鍵字。
  • C語言 volatile 關鍵字在編譯優化過程中有何作用
    volatile的作用是作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。 volatile變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。
  • Go語言的學習筆記(第二章)
    Go語言中標識符由字母數字和_(下劃線)組成,並且只能以字母和_開頭。舉幾個例子:abc, _, _123, a123。常量的聲明和變量聲明非常類似,只是把var換成了const,常量在定義的時候必須賦值。
  • ARM中ADS環境下C語言和彙編語言混合編程及示例
    彙編語言是和CPU的指令集緊密相連的,作為涉及底層的嵌入式系統開發,熟練對應彙編語言的使用也是必須的。這裡主要討論C和彙編的混合編程,包括相互之間的函數調用。下面分四種情況來進行討論,不涉及C++語言。
  • 在C語言中如何高效地複製和連接字符串?
    就目前而言,在編程領域中,C語言的運用非常之多,它兼顧了高級語言的彙編語言的優點,相較於其它程式語言具有較大優勢。void* memccpy(void* restrict dst, constvoid* restrict src, int c, size_t n);這個函數結合了memcpy、memchr的特性以及上面討論的API的最佳方面的特性。
  • C語言的一些高級議題
    指針是C語言的靈魂,我們經常聽到這樣的說法,當我們初學C語言的時候,似乎覺得也沒有什麼,但是當你越來越深入的了解它,你就會發現C語言的強大有時甚至超乎你的想像。C語言作為一種相對較為底層的語言,在某些方面有著不可替代的優勢。
  • C語言陷阱與技巧31節,都說void*指針是「萬能指針」,它萬能在哪
    ,不過不知道讀者注意到沒,有些庫函數的參數是 void * 類型的,例如:void *memcpy(void *dest, constvoid *src, size_t n);void *memmove(void *dest, constvoid *src, size_t n);前面的章節在討論C語言指針時,提到指針從某種程度上來說,
  • 用C語言實現FFT算法
    main programe********************/#includemath.h>#includestdio.h>#includestdlib.h>#include typedef.hfloat result[257];struct compx s[257];int Num=256 ;const
  • 《精靈寶可夢:究極之日/究極之月》究極傳說妖怪
    寶可夢今天在官網公開了預定2017年11月17日發賣的《精靈寶可夢:究極之日/究極之月》最新情報,可以吸收日月神獸的究極傳說妖怪「內克羅茲瑪」(音譯)正體霸氣亮相!·《精靈寶可夢:究極之日/究極之月(Pokemon Ultra Sun/ Ultra Moon)》是由任天堂製作發行的一款RPG遊戲,是第七世代的資料片,同時也是《太陽/月亮》的劇情續作。遊戲舞臺阿羅拉地區以夏威夷為原型,地圖也類似夏威夷。本作將對應9種語言日語、英語、西班牙語、法語、德國語、義大利語、韓語、繁體中文、簡體中文。
  • 標準C+的類型轉換符:static_cast等
    注意:static_cast不能轉換掉exdivssion的const、volitale、或者__unaligned屬性。 dynamic_cast主要用於類層次間的上行轉換和下行轉換,還可以用於類之間的交叉轉換。 在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的; 在進行下行轉換時,dynamic_cast具有類型檢查的功能,比static_cast更安全。
  • Java中volatile關鍵字概覽
    變量不可見性內存語義概述在介紹多線程並發修改變量不可見現象的原因之前,我們需要了解回顧一下Java內存模型(和Java並發編程有關的模 型):JMM。JMM(Java Memory Model):Java內存模型,是java虛擬機規範中所定義的一種內存模型,Java內存模型是標準化的,屏蔽掉了底層不同計算機的區別。
  • 快速上手系列-C語言之字符串處理函數(一)
    C語言中常用的字符串操作函數,有比如計算字符串長度、字符串拷貝,字符串比較等這樣的整體操作函數,有字符串查詢函數,也有字符串轉換函數等等,這裡先介紹字符串整體操作函數。1、strlen原型:int strlen ( const char *str )功能:返回字符串的實際長度,不含 '\0'。
  • 51單片機基礎剖析(基於C語言)
    2.函數的參數和局部變量的存儲模式 C51 編譯器允許採用三種存儲器模式:SMALL,COMPACT 和LARGE。一個函數的存儲器模式確定了函數的參數的局部變量在內存中的地址空間。處於SMALL模式下的函數參數和局部變量位於8051單片機內部RAM中,處於COMPACT和LARGE模式下的函數參數和局部變量則使用單片機外部RAM。