你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

c++——const和volatile用法

[复制链接]
gaosmile 发布时间:2020-8-20 20:31
关于const的用法,现在大概前前后后应该写了有两篇文章,以前学习的时候,用法体会不是那么深刻,为啥这么说呢,因为在学习c++的时候,会发现const关键字有新的玩法,关于这个新的玩法,大家可以去看最近学习总结写的c++文章专辑。
一、const的用法:
1、const只读变量:
  • const修饰的变量是只读的,本质上还是变量
  • const修饰的局部变量在栈上分配空间
  • const修饰的全局变量在全局数据区分配空间
  • const只在编译期有用,在运行期没有用

    - s2 L. i3 h$ |+ ?' Z
注:const修饰的变量不是真的常量,它只是告诉编译器该变量不能出现在赋值符号的左边
2、const全局变量的分歧:
  • 在现代c语言编译器中,修改const全局变量将导致程序崩溃
  • 标准c语言编译器不会将const修饰的全局变量存储于只读存储区中,而是存储于可修改的全局数据区,其值依然可以改变

    # a7 Q. u  S. ]4 a
3、代码示例:
(1)只读变量代码示例:
#include <stdio.h>* E8 h# J* w% a

! ~& x+ X. O' p) _# }int main()7 s+ f5 I  M! ~% y" x1 L7 y7 u# g
{
! h7 u5 u6 Z0 ?) d5 ]% a   const int a =10;# M+ {% k6 o# U1 x
   printf("a = %d\n",a);
, t: m9 d% b: l( j9 i" ~& O' B2 }6 c0 }
   a=20;7 Y4 d; J. u2 }  V* A
   printf("a = %d\n",a);
! t: P8 j& B# ?9 j% j  7 a2 ]" r( i! Z  ]' J) \
  return 0;/ f; X7 [, E0 M0 i2 z# d
}
/ [# B5 U' e; R% P! |, S. r. d. G
4 }; `% L3 ?: D  {; b
运行结果:
test.c: In function ‘main’:" g$ d2 G1 Q# w4 X$ U: n
test.c:8:4: error: assignment of read-only variable ‘a’, p9 t5 }! _& v9 z
    a=20;
7 L8 V9 }: S5 @) H! j8 r% `    ^
( t# F# t+ ?* d  }* W5 j& E2 e2 v3 V4 s0 {, D
注解:显示这个结果很正常,变量a被const修饰了,它就成了只读的。
(2)如果对变量a的值进行修改:
#include <stdio.h>: p. k9 R7 a- u. M
; G4 |! L8 N: j$ J( Y
int main()
4 h: g$ c6 f; u1 p9 h7 D- ^' [{0 O( T, h( U/ ~& F/ ^7 R& O' [
   const int a =10;
+ ?1 C* G, p3 `- w; S' X   int *p =(int *) &a;
. m0 f: v' s$ H   printf("a = %d\n",a);
1 G  i6 [+ @$ ^7 S% O5 X
0 D' j4 V( a- W/ n, o+ p1 |, t: C   *p=20;
% R; h. z( c& k) d2 M   printf("a = %d\n",a);
$ q- F8 F; W, |( l+ ~% W8 ?7 j" D+ _6 |
2 d' Z" M* ~; U; Q9 j
  return 0;$ ^  j; B4 F5 S( N5 Z, L
}
- @) j8 @; J( \3 f+ Y9 r
* F+ _* F) ?. G) y8 @  h
3 H; ]1 E: Q" R
运行结果:
root@txp-virtual-machine:/home/txp# ./a.out; G7 i9 y/ s/ t5 J
a = 10
8 W# b2 k( Q& e; O& u1 \8 g0 Xa = 20
3 m( H' j8 l: W2 T0 G# ~# \4 k6 X' ~" v9 S; k9 ]8 z$ l; }
注解:通过指针的方式,就能够把a的值进行修改,这也论证了“const修饰的变量是只读的,本质上还是变量”这句话
(3)const修饰全局变量:
代码版本一
#include <stdio.h>
+ B" M1 C6 q, n' L, O+ X+ B& yconst int b = 40;& ?" @% P9 [" s3 [7 x& A
8 p4 I8 m0 d! o2 m
int main()
1 h  R9 W4 z9 _% F5 u( z. b  t{
- e; O4 O$ @. }  Y8 k) V7 K) n   # c* j- @# B% k9 K- X8 H" w: m
. }, [3 ~+ Y+ c7 ]( c3 w* v
   printf("b = %d\n",b);! Z! l/ E/ e+ X8 q' ?" u1 |7 w
   b=20;+ c) r! c4 x7 h) d9 E: e  W2 B( c
   printf("b = %d\n",b);# u; K$ F$ K+ {2 c6 y
2 s8 G$ ~- Z9 u" ~# r+ c# \# o
# B- e5 X! j' Z4 t$ S3 E
  return 0;% E3 u6 j6 u. F# F
}9 ~8 v: Q: O; w: e* F" _

2 F3 |  i8 \$ d, I7 `: m4 l, R
输出结果:
root@txp-virtual-machine:/home/txp# gcc test.c
8 s: M. I/ s, v2 r. \test.c: In function ‘main’:! G4 s2 P& _; L4 W! J; z9 {( J
test.c:10:4: error: assignment of read-only variable ‘b’
, d( h7 O3 a) o, g0 P    b=20;
8 g4 R" u8 I# @7 g4 w    ^
5 q6 R  W4 m, a" T4 X# u: }6 O: t" m3 t) w& l$ O
注解:跟const修饰栈上的变量用法一样
代码版本二
#include <stdio.h>
! s* z' O; ~% f8 V) }( vconst int b = 40;' K+ H8 G# Z$ [7 v6 [
/ y1 N0 ?7 ]9 U) N' A/ Y* [) Z
int main()4 e* n  S8 E' h4 r0 [  K. h) e
{! a% b5 l8 `; [" L$ M
4 c8 U7 H* \+ r8 L4 {
   int *p =(int *) &b;' M8 C. h8 Q: e- p
   printf("b = %d\n",b);
. F6 d5 q2 e1 j, ?6 T( S/ e& a/ d# E6 y$ m: R0 k2 k
   *p=20;
0 c  d& U& ]" R5 f$ C4 ?& g   printf("b = %d\n",b);
5 ~+ V# ^  `, p; f( f6 Q
+ Y  o3 V/ B" m  w
% I! Z) J, g! j5 C. f9 G5 f  return 0;
8 R* ^: K& g& |; |}" H7 k. r3 N) y! l$ C6 A; Y
2 w% N7 d5 r" t# d2 b/ y- [
运行结果:
root@txp-virtual-machine:/home/txp# ./a.out. p3 a1 o2 g7 H& U; m1 Y' {- i' i
b = 40
+ N. R2 V: f- v4 k& i) n0 [Segmentation fault (core dumped)
5 Q' {( r& f  r  b4 a; _. k# p! w: r7 Z: M( i1 o+ u* U
注解:这里出现了段错误,这也验证了我们上面所说的“修改const全局变量将导致程序崩溃”。
同时为了验证“标准c语言编译器不会将const修饰的全局变量存储于只读存储区中,而是存储于可修改的全局数据区,其值依然可以改变”这句话,我把这段代码放到dev c++上进行试验:
微信图片_20200820202426.png
说明:我这个版本的编译器支持标准c语言,所以没导致程序崩溃,能够正常运行
4、const的本质
  • c语言中的const使得变量具有只读属性
  • 现代c编译器中的const将具有全局生命周期的变量存储于只读存储区,不是放在全局数据区

    $ o0 x. ?7 X* n  e
注:const不能定义真正意义上的常量;同时这里注意static关键字修饰的变量,它的生命周期和全局变量一样。
代码示例:
#include <stdio.h>. G' [1 ~' ]* O' B5 C
3 D4 T/ w8 R+ W( e/ M/ g
const int Array[5] = {0};
( Z7 n" E  ^: M9 ^, Z0 S) m0 k* c- s; ]$ O9 E
void fun(int *p,int v)
( s' e* c" m: t, j1 z/ d, C{
- {% l& K7 f3 s, c/ L5 r   *p=v;' p7 @) s+ |, F! H/ H% ?: c9 @. j
}
" P, N! u9 E' \int main()
! v  ~$ w' a; O4 f# E+ _) e{
. |: Y6 [  s  F6 o( B- _- |    int const i =1;, o- {1 U. ^9 S7 k; N1 z, }3 n
    const static int j =2;
2 b! J  r- u5 B5 h; w4 m7 N5 g    int const array[5] = {0};
  c! f  D9 @0 C0 [. S+ ~
4 S$ J( D6 W( e) o  Q    fun((int *)&i,1);
3 o1 O- b3 d. X3 [, z! F    fun((int *)&j,2);
6 L6 V# B- S- @& S9 M4 X7 B( ]    fun((int *)&array[2],3);' |/ l7 R, r+ X: w% o6 S5 a
    fun((int *)&Array[1],4);9 c4 e, y+ W; `' l! j+ S" C7 Z1 P* }

% t) ]5 ~: P* S2 u( O   return 0;
; z2 M5 n8 b' @+ _7 X1 M9 p7 Z# V9 ?0 }3 B5 U' ^7 m
}
; I6 T! |) F/ S8 }$ P; U
8 u* w" [3 I5 R! t% O
输出结果:
root@txp-virtual-machine:/home/txp# ./a.out/ m- r* _* f# \, a$ `
Segmentation fault (core dumped)
% f1 f# X$ g4 Y6 Y. }: G
0 E, e% c! Y' D" Q, E  u
注解:这里会有段错误,错误出现在const+static修饰的j变量对其进行修改,还有const修饰的全局数组。
5、const修饰函数参数和返回值
  • const修饰函数参数表示在函数体内不希望改变参数的值
  • const修饰函数返回值表示返回值不可改变,多用于返回指针的情形

    / m" G9 {6 f0 S1 l+ V
在c语言中的字符串字面量存储于只读存储区中,在程序中需要使用const char* 指针,例如:
const char * s = "TXP嵌入式";//字符串字面量. r* h' e) e# E
代码示例:
#include <stdio.h>: b6 v, S! r& H' s

3 i  g5 g4 ~! o  R. _" J3 t+ @, bconst char*fun(const int i)
) a+ ?; _# \& T  [5 A{5 O9 Y' D) E/ t5 c
   i=8;
5 H4 k3 U" \( O: M    return "TXP";
  ^3 M  _  Y; E) p: c7 h- G* h}$ F% C6 k& Z3 F8 u. ]9 y
int main()
9 G' o. m+ Q* A) g3 _9 a+ [{
1 j0 r. ?; T" y* U) |7 t8 @   const char * p=fun(0);
# F0 K0 P2 Q5 f  C0 g# W- ~    printf("%s\n",p);
5 ?. {. v& e  g, ^# a! [    p[1]='_';# {; ^( B$ x& J/ G7 z
    printf("%s\n",p);; r8 ?6 J5 t9 i
% ]8 k. K. ?: Z. k3 t
    return 0;
8 P' P8 i; l2 H; G
) m3 ]7 ^% r6 [2 w; i' A
6 g4 N5 s) r, K: Q}% M- S, O' b  Y2 M4 j; G7 P& e

1 Q: o: B2 r! [
! D& v# o- U" B
输出结果:
root@txp-virtual-machine:/home/txp# gcc test.c
4 k$ J& ], X7 O3 l" b* Y1 J4 Ltest.c: In function ‘fun’:" x* [4 A' J- G5 g4 O$ E9 n* M
test.c:5:4: error: assignment of read-only parameter ‘i’$ @1 y: n! C' I# S& t' ^1 Y+ d1 F5 p
    i=8;( \4 X$ Z. i/ X: y% Q9 X
    ^
- M+ n+ h" e7 ^( G& l) _test.c: In function ‘main’:9 n# R6 d/ ]# I( U8 o( ~6 q
test.c:12:5: error: assignment of read-only location ‘*(p + 1u)’
# d  T9 h6 W  N/ u) v! T& N     p[1]='_';
- v6 H. l% ^3 z6 X     ^4 y9 j6 j) {' L% j, N6 L7 c: H9 y

( y$ W, n5 j% V. z" H& ~6 e
注解:上面这样写,肯定有问题。
代码进化:
#include <stdio.h>" Y& d* P1 p& v4 K. O

! r! z: H- w( Z' jconst char*fun(const int i)5 y$ {$ }/ P( ^& B4 U" K" @) `
{* d4 j4 s' U- H
//   i=8;0 F6 k' o$ G8 v' n
    return "TXP";
+ ?% q0 k. g* T& \}
2 I. ~. t( E, R7 N4 Q+ X2 iint main()% v' N9 e: H* `1 K
{' J, j  D2 }' O: V3 d. R
   const char * p=fun(0);
  B/ _' `$ {% S( _    printf("%s\n",p);
- Q# D4 t( Q' G) Y2 v& X  //  p[1]='_';
& ^1 D8 c' u; z   // printf("%s\n",p);
- S! X0 U5 P, D7 [! J
% D/ E  G/ i7 |7 j, C# p* B    return 0;! ~: Q4 j. ]( m) h

8 d+ ^2 [$ M* n% w/ I0 E  l
% a/ E4 W/ {& }  Z9 M5 o$ h}
/ }( i, @& O# d7 t  i* W6 A3 U$ b9 X8 L7 J
输出结果:
root@txp-virtual-machine:/home/txp# ./a.out5 r5 g! i5 m* k3 ?; `) `% B
TXP
; D8 l9 C# _% z1 J! T" U/ t6 ~+ E3 g- N9 k& q5 `
二、volatile的用法
老实说,这个关键字在面试题目里面经常会出现,但是平时学习的时候,如果你没有真正理解这其中的含义,在笔试的时候,脑袋里面可能依稀是记得有那么几个结论,但是有时候吧,一紧张就把结论给忘了,也不是不可能,所以说,咋们还是老实一点,得真正把它原理搞明白才行,这样上来战场就不怕了,以后写代码也就少一点bug。
1、volatile的常用结论(volatile英文本意就是易变的意思)
这里我先给结论,然后再给一个例子,把这个例子的讲明白,所有结论就都明白了。
  • volatile可理解为“编译器警告指示字”
  • volatile告诉编译器必须每次去内存中取变量值
  • volatile主要修饰可能被多个线程访问的变量
  • volatile也可以修饰可能被未知因素更改的变量
  • volatile可以修饰一个中断子程序中会访问到的非自动变量

    2 ]+ }+ h- n7 c8 o- ?
2、分析原理
大家可能平时在博客学习,都会发现讲解编译器优化的,然后加了volatile关键来修饰变量,就告诉编译器不要去优化这个变量了,那么这里的优化到底是什么意思呢?
从字面上来理解优化两个字,意思就是最优值(变量的值不会改变),这里我用一个简单代码来说明一下:
#include <stdio.h>- K- C4 @' e, f7 X! Q

$ z$ m: c  J- ~) tint main()5 L( \# Y' t/ M; v( g# }/ ~
{
7 v, o& F7 V# t. Q/ T    int a =1;//volatile int a =0;. q" H; J) d3 C" M! A- x3 T
     5 R( i6 z  D7 V9 b# S
    while(a)
. D. B  U+ s$ d    {9 B5 t$ i; t; {! |
   
  @8 l1 t) E5 Y' D2 b& [* \    }
7 Q8 ?' n1 e, V' C& z}
% u0 Y3 Z! N9 p4 L9 A+ |
说明:上面的代码,如果变量a没有加volatile修饰的话,编译器就会优化它(也就是a的值一直不变),所以while就一直死循环;然后我如果加了volatile来修饰的话,编译器就不会去优化变量a,不优化的意思就是说,变量a的值可能就会改变,while就不会一直死循环。
当然这里为了好理解,我说的不是很专业,没有从寄存器和内存的角度去说。(我也不想那么去讲解,简单理解了就行)
总之一句话:上面的结论中,volatile修饰的都是变量,变量就可能改变,不会被编译器优化;只是说我们上面的结论应用场景不同而已。
三、总结
  • const使得变量具有只读属性
  • const不能定义真正意义上的常量
  • const将具有全局生命周期的变量存储于只读存储区
  • volatile强制编译器减少优化,必须每次从内存中取值
    # C" b( l- ]/ V2 C# K+ l; O2 g
微信图片_20200820202430.png
/ i, O. I( }, U6 C0 Q' _) F8 O
好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。
! ?* w$ s9 Q9 i( U" `: J* T
赞 收藏 评论0 发布时间:2020-8-20 20:31

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版